Skip to content

Sales Proposals Workspace — Implementation Plan (Arm b)

Sales Proposals Workspace — Implementation Plan (Arm b)

Section titled “Sales Proposals Workspace — Implementation Plan (Arm b)”

This plan decomposes the §9.2 Sales Proposals workspace spec at docs/specs/sales-proposals-workspace-spec-arm-b.md into ordered, individually verifiable tasks. Every spec acceptance criterion (AC-1 to AC-17) is mapped to one or more tasks; every task carries acceptance criteria, a verification gate, dependencies, file-touch boundaries, and a size estimate.

The plan is structured as twelve sequential phases with checkpoints between them. Where tasks within a phase have disjoint file ownership, they are flagged for parallel implementation. The dependency graph follows the bottom-up order: foundation (schema, types, workspace registration) before vertical feature slices (intake → components → assembly → review → export → MCP surface → polish).

The workspace adds a new sales_proposal value to the existing workspace type set, backed by a new proposals table joined by workspace_id with supporting tables for sections and pricing line items. A typed ProposalContext captures deal context at intake. Seven §9.2 components (ROI calculator, case-study selector, quote picker, pricing configurator, templates, assembly logic, export) compose into a draft proposal that passes a pre-send review and exports to DOCX, PDF, and HTML preview. A new MCP App (Account Brief) and three MCP prompts (account_brief, proposal_shortcut, case_study_assembly) extend the workspace into the Claude Desktop / Claude.ai client. A cover-email template and a post-send follow-up workflow complete the lifecycle.

The build draws on substantial existing scaffolding documented in the Stage 1 audit: the proposal-writer skill (rules library), the ai-smb-playbook ROI/feasibility components, the playbook DOCX exporter, brand-review and uk-english enforcers, the markdown-converter for RFP ingest, and the existing review-skill pattern for the pre-send gate. Disposition per asset is recorded in spec §6 and governed by spec §9 (the embedded OPS-26 plugin reuse policy).

The following decisions are taken at plan time and frame every downstream task. Each is anchored to a spec section.

  • Schema-first persistence (spec §1, §3). A dedicated proposals table joined to workspaces holds proposal records. Sections live in proposal_sections; pricing in proposal_pricing_line_items. The content_items table is not extended with a proposal content_type. This is settled here against OQ-3 to unblock Phase 1; if reversed, Phase 1 re-scopes.
  • Vertical slicing through workspace lifecycle, intake, components, assembly, export. Each phase delivers a working slice of the user journey; checkpoints validate the slice end-to-end before the next phase begins. The spec’s seventeen ACs map onto this structure naturally because each AC describes a behaviour visible at the slice boundary.
  • Component orthogonality. The four mid-stack components (ROI, case-study selector, quote picker, pricing configurator) are each owned by their own subdirectory (components/proposals/<component>/, lib/proposals/<component>/) so they can be built in parallel after the foundation phase. The assembly orchestrator depends on all four, so it cannot start until they have settled their typed interfaces.
  • Templates are file-driven (spec §FR-7, §AC-7). Adding a template is a file-system change, not a code change. The orchestrator iterates the template directory at boot. This makes the templates phase parallelisable with the components phase: their interfaces meet only inside the orchestrator, which is built later.
  • Brand voice enforcement runs at section save and at export gate (spec §FR-9). It is built as a substrate (Phase 4) before the components that depend on it. Severity model (Critical blocks export; non-Critical warns inline) is set in Phase 4 and consumed everywhere downstream.
  • Pre-send review is a checks-driven mirror of the existing review skill (spec §6.11). Check files in .claude/checks/proposal-readiness/ are read by a thin wrapper around the existing /review machinery. No new review framework is built.
  • PDF route is deferred to a Phase 9 spike (OQ-2). A short investigation chooses between DOCX-to-PDF server-side conversion and HTML-to-PDF print-rendering before AC-11 acceptance; the choice changes the export pipeline shape but does not affect upstream phases.
  • Three MCP prompts ship in two waves (spec §FR-14). account_brief may ship first with static Phase 1 grounding (per gap-tracker §D.1 v1.2 disposition); proposal_shortcut and case_study_assembly ship with the workspace in Phase 10. This split is preserved in the task list.

Settle the spec’s open questions that block downstream tasks. No code in this phase.

Task 0.1: Resolve OQ-1 (bid-writing reconciliation)

Section titled “Task 0.1: Resolve OQ-1 (bid-writing reconciliation)”

Description: Decide the canonical home for RFP/bid response strategy. Either (a) keep bid-writing as canonical for UK public procurement and scope proposal-writer/rfp-response-strategy.md to commercial proposals only, (b) merge into a single skill, or (c) keep both with a clearly bounded delegation. The decision is captured as a one-paragraph note appended to spec §11.

Acceptance criteria:

  • A written decision is recorded against OQ-1 in the spec.
  • The decision identifies which skill is canonical for which path.
  • If (b) is chosen, a follow-on task is opened to merge.

Verification: Manual — Liam reads and signs off.

Dependencies: None.

Files likely touched: docs/specs/sales-proposals-workspace-spec-arm-b.md (§11 OQ-1 update).

Estimated scope: XS.

Task 0.2: Resolve OQ-2 (PDF rendering route)

Section titled “Task 0.2: Resolve OQ-2 (PDF rendering route)”

Description: Spike a comparison of DOCX-to-PDF server-side conversion versus HTML-to-PDF print-rendering. Produce a one-page comparison covering styling fidelity, server cost, library footprint, and dependency risk. Pick one. The choice settles AC-11 acceptance and §6.7 effort.

Acceptance criteria:

  • Comparison written.
  • Decision recorded against OQ-2 in the spec.
  • Acceptance criteria for AC-11 are updated to reflect the chosen route.

Verification: Manual — Liam reads and signs off.

Dependencies: None.

Files likely touched: docs/specs/sales-proposals-workspace-spec-arm-b.md (§11 OQ-2 + §AC-11).

Estimated scope: S.

Description: Settle the remaining six open questions in spec §11: dedicated table vs content_type (OQ-3, framed by Architecture Decision above as table-first; confirm or reverse), cover-email home (OQ-4), Account Brief data freshness (OQ-5), multi-proposal-per-workspace cardinality (OQ-6), citation provenance rendering (OQ-7), follow-up reminder mechanism (OQ-8). Each gets a short paragraph in the spec.

Acceptance criteria:

  • All six OQs are resolved with rationale.
  • Where an OQ resolution changes downstream task scope, the affected tasks are flagged in this plan.

Verification: Manual — Liam reads and signs off.

Dependencies: Task 0.2 (PDF route influences OQ-5 if Account Brief ships PDF artefacts).

Files likely touched: docs/specs/sales-proposals-workspace-spec-arm-b.md (§11 OQ-3 to OQ-8).

Estimated scope: S.

  • All eight OQs resolved.
  • Plan tasks updated for any scope changes implied by OQ resolutions.
  • Liam signoff captured.

Phase 1 — Foundation (schema, types, workspace registration)

Section titled “Phase 1 — Foundation (schema, types, workspace registration)”

Description: Author a single migration creating proposals, proposal_sections, proposal_pricing_line_items. Include foreign keys to workspaces.id, RLS policies that defer to existing get_user_role() plus workspace-membership tables, audit columns (created_by, updated_by, created_at, updated_at, last_exported_at, last_sent_at), and per-table indexes appropriate to the access pattern. Authored via supabase migration new; applied via supabase db push. PL/pgSQL helpers include SET search_path = public, extensions. The migration also extends any DB-level enum or constraint on workspaces.type to include sales_proposal (or, if type is plain text, no DDL change but the in-app type list is extended).

Acceptance criteria:

  • Migration applies cleanly to a fresh local DB.
  • Three new tables exist with the documented columns and FKs.
  • RLS policies enforce workspace membership for read, write, archive, delete.
  • No execute_sql DDL anywhere; Liam runs db push after author commits the file.

Verification:

  • supabase migration up succeeds locally.
  • A short Vitest integration test inserts a sales_proposal workspace + one proposal and reads it back as the workspace owner.
  • An admin advisors run produces no security or performance warnings on the new tables.

Dependencies: Phase 0 complete.

Files likely touched:

  • supabase/migrations/<timestamp>_proposals_schema.sql
  • __tests__/integration/proposals/schema.integration.test.ts

Estimated scope: M.

Description: Add types/proposal.ts exporting ProposalContext, AssembledProposal, ProposalSection, PricingPlan, RoiInputs, RoiOutput, CoverEmailContext, and any union types referenced by the components. No barrel re-exports; named exports only. Strict TypeScript, no any.

Acceptance criteria:

  • Type file compiles under tsc --noEmit with no diagnostics.
  • Each type’s shape matches the spec (FR-2 for ProposalContext, FR-6 for PricingPlan, FR-3 for the ROI types).
  • No type imports from index files.

Verification:

  • bun run lint passes.
  • bunx tsc --noEmit passes.
  • A short type-only Vitest spec asserts the discriminated unions in ProposalContext.dealType are exhaustive.

Dependencies: Task 1.1.

Files likely touched: types/proposal.ts, __tests__/lib/proposals/types.test.ts.

Estimated scope: S.

Description: Add a Vitest guard that enumerates every workspaces.type value the codebase recognises and fails if a runtime type appears in the DB that the code does not handle. Mitigates spec §R-5 (hard-coded type-string filter drift). The guard inspects lib/mcp/tools/workspaces.ts, app/workspaces/**, and any other file pattern-matching on the type string.

Acceptance criteria:

  • The guard test exists and currently passes against the four known types (bid, intelligence, content, sales_proposal).
  • Adding an unrecognised string to the test fixture causes the test to fail with a clear message.

Verification: bun run test __tests__/lib/proposals/workspace-type-guard.test.ts passes.

Dependencies: Task 1.1.

Files likely touched: __tests__/lib/proposals/workspace-type-guard.test.ts, possibly a single helper export in lib/workspaces/types.ts.

Estimated scope: S.

Task 1.4: Validation schemas extension (touch-with-care)

Section titled “Task 1.4: Validation schemas extension (touch-with-care)”

Description: If Phase 0 OQ-3 is reversed (proposals as content_items rows), extend lib/validation/schemas.ts to add a proposal content_type. If OQ-3 stays at table-first, this task is no-op and the file is not touched. The plan flags this conditionally because the spec §8.2 lists lib/validation/schemas.ts as touch-with-care.

Acceptance criteria:

  • Either no change to lib/validation/schemas.ts, or one new entry in VALID_CONTENT_TYPES matched by a single test.
  • If extended, the validation-sweep guard still passes.

Verification: bun run test __tests__/validation/ passes; bun run lint passes.

Dependencies: Task 0.3 (OQ-3 resolution).

Files likely touched: lib/validation/schemas.ts (only if OQ-3 reversed).

Estimated scope: XS.

  • Schema applied; types compile; workspace-type guard green.
  • No raw Tailwind colours introduced.
  • No unchecked Supabase calls (ESLint local/no-unchecked-supabase-error and local/no-silent-promise-catch clean).
  • bun run knip reports no orphans.

Description: Implement app/api/proposals/route.ts (POST create, GET list with workspace filter), app/api/proposals/[id]/route.ts (GET, PATCH, DELETE), and app/api/proposals/[id]/archive/route.ts (POST archive/unarchive). Every route uses getAuthorisedClient() + authFailureResponse(auth) for auth routing. Body parsing via parseBody from @/lib/validation; query parsing via parseSearchParams. All Supabase calls use sb() or tryQuery().

Acceptance criteria:

  • All four routes return correct HTTP for unauthenticated, forbidden, and ok cases.
  • Editor-role users can create and update; admin can hard-delete archived; viewer is read-only.
  • No raw fetch callers; no unchecked Supabase responses.

Verification:

  • bun run test __tests__/api/proposals/ passes.
  • An integration test exercises the full CRUD lifecycle as editor + admin.

Dependencies: Task 1.1, Task 1.2.

Files likely touched:

  • app/api/proposals/route.ts
  • app/api/proposals/[id]/route.ts
  • app/api/proposals/[id]/archive/route.ts
  • __tests__/api/proposals/*.test.ts
  • __tests__/integration/proposals/crud.integration.test.ts
  • lib/query/query-keys.ts, lib/query/fetchers.ts (proposal keys + fetchers)

Estimated scope: M.

Task 2.2: Workspace page — list of proposals

Section titled “Task 2.2: Workspace page — list of proposals”

Description: Implement app/workspaces/[id]/page.tsx with branching on workspaces.type. When type is sales_proposal, render the proposals list view; when type is bid or intelligence, defer to the existing routes. The list view shows each proposal with status, last edited, and quick-action buttons. Uses TanStack Query exclusively.

Acceptance criteria:

  • The page loads in under 800 ms server-timing on a warm session of 50 proposals (AC-17).
  • Type-branching works without breaking existing bid or intelligence workspace renders.
  • WCAG 2.1 AA passes on the list view (status colour is paired with iconography or text).
  • No raw Tailwind colour utilities.

Verification:

  • bun run test __tests__/components/proposals/ passes.
  • A Lighthouse accessibility check on the list page scores ≥95.
  • Manual: existing bid workspace page still renders correctly.

Dependencies: Task 2.1.

Files likely touched:

  • app/workspaces/[id]/page.tsx (new dynamic route)
  • app/workspaces/[id]/proposals-list.tsx
  • components/proposals/proposal-card.tsx
  • components/proposals/proposal-list.tsx
  • hooks/proposals/use-proposals-list.ts

Estimated scope: M.

Task 2.3: Create-proposal dialog + create / archive / delete UI

Section titled “Task 2.3: Create-proposal dialog + create / archive / delete UI”

Description: Add a CreateProposalDialog invoked from the workspace page header. Captures the minimal seed (title, prospect handle, deal type) and routes the user into the intake page on submit. Adds archive and delete affordances on each card consistent with the existing workspace-archive pattern. Hard-delete is admin-only and gated behind a confirmation modal.

Acceptance criteria (AC-1):

  • An editor can create a proposal from the dialog.
  • The new proposal appears in the list with status draft.
  • Archive and unarchive work; archived proposals are excluded from the default list.
  • An admin can hard-delete an archived proposal; an editor cannot.

Verification:

  • bun run test __tests__/components/proposals/create-proposal-dialog.test.tsx passes.
  • An E2E spec at e2e/tests/proposals/lifecycle.spec.ts exercises create-archive-unarchive-delete across editor and admin roles.

Dependencies: Task 2.2.

Files likely touched:

  • components/proposals/create-proposal-dialog.tsx
  • components/proposals/proposal-card.tsx (archive/delete affordances)
  • app/workspaces/[id]/proposals-list.tsx (wire dialog)
  • e2e/tests/proposals/lifecycle.spec.ts

Estimated scope: M.

  • AC-1 acceptance criteria all green.
  • Type-branching does not regress bid or intelligence workspaces.
  • Lighthouse accessibility ≥95 on workspace and authoring pages.
  • bun run knip clean.

Phase 3 — Intake (ProposalContext, manual + RFP + MCP-prompt entry points)

Section titled “Phase 3 — Intake (ProposalContext, manual + RFP + MCP-prompt entry points)”

Task 3.1: ProposalContext intake form (manual entry)

Section titled “Task 3.1: ProposalContext intake form (manual entry)”

Description: Build components/proposals/intake-form.tsx capturing the typed ProposalContext. Fields: prospect identity, sector, deal type, target outcomes, decision drivers, optional budget signal, optional decision date. Required fields fail closed; the user cannot leave intake until they are complete. Persists via the API route at app/api/proposals/[id]/context/route.ts.

Acceptance criteria (AC-2 partial):

  • Required-field validation blocks submission.
  • Submitted context is persisted as a typed ProposalContext against the proposal record.
  • DD/MM/YYYY is enforced on the decision-date field; GBP on the budget-signal field.

Verification:

  • bun run test __tests__/components/proposals/intake-form.test.tsx passes.
  • Integration test asserts the persisted shape matches ProposalContext.

Dependencies: Tasks 2.1, 2.3.

Files likely touched:

  • components/proposals/intake-form.tsx
  • app/api/proposals/[id]/context/route.ts
  • app/workspaces/[id]/proposals/[proposalId]/intake/page.tsx
  • hooks/proposals/use-proposal-context.ts
  • __tests__/components/proposals/intake-form.test.tsx

Estimated scope: M.

Task 3.2: RFP attachment storage + ingest pipeline

Section titled “Task 3.2: RFP attachment storage + ingest pipeline”

Description: Add a Supabase Storage bucket proposal-attachments with signed-URL access only. Implement an ingest route at app/api/proposals/[id]/rfp/route.ts that accepts a PDF or DOCX upload, converts to Markdown via the markdown-converter, and stores the converted Markdown against the proposal record’s rfp_context field. Conversion uses the documented two-step HTML → Turndown path (with the GFM plugin) so tables survive (per CLAUDE.md gotcha; spec §R-8). Pipeline runs are recorded via recordPipelineRun().

Acceptance criteria (AC-2 partial, AC-12):

  • PDF and DOCX uploads convert to Markdown with table preservation.
  • The converted Markdown is stored against the proposal record and accessible to the assembly logic.
  • Raw URLs are never exposed to the UI; signed URLs only.
  • A pipeline-run row is written for every ingest.

Verification:

  • bun run test:integration __tests__/integration/proposals/rfp-ingest.integration.test.ts ingests a fixture DOCX containing a table and asserts the table survives.
  • bun run test __tests__/api/proposals/rfp.test.ts covers auth and validation paths.

Dependencies: Task 1.1 (storage bucket), Task 2.1.

Files likely touched:

  • app/api/proposals/[id]/rfp/route.ts
  • lib/proposals/rfp-ingest.ts
  • __tests__/integration/proposals/rfp-ingest.integration.test.ts
  • __tests__/api/proposals/rfp.test.ts
  • A DOCX fixture under __tests__/fixtures/rfp/
  • supabase/migrations/<timestamp>_proposal_attachments_bucket.sql

Estimated scope: M.

Task 3.3: Account-brief MCP prompt — Phase 1 static grounding (parallelisable with 3.1, 3.2)

Section titled “Task 3.3: Account-brief MCP prompt — Phase 1 static grounding (parallelisable with 3.1, 3.2)”

Description: Register the first of the three MCP prompts: account_brief. Per gap-tracker §D.1 v1.2 and spec §FR-14, this prompt may ship first with static Phase 1 Company Profile grounding while proposal_shortcut and case_study_assembly wait for Phase 10. The prompt produces a structured AccountSnapshot consumable by an intake-form pre-fill button.

Acceptance criteria (AC-14 partial, AC-2 partial):

  • The prompt registers in the MCP prompt list.
  • Invoking the prompt with a prospect handle returns a grounded snapshot.
  • The intake form has a “pre-fill from account brief” affordance that consumes the snapshot.

Verification:

  • MCP eval Layer 1 passes for the new prompt.
  • Functional-correctness eval (Layer 4) asserts citations resolve.

Dependencies: Task 1.2 (types), Task 2.1.

Files likely touched:

  • lib/mcp/prompts/account-brief.ts
  • lib/mcp/index.ts (registration entry)
  • components/proposals/intake-form.tsx (pre-fill button)
  • __tests__/mcp/prompts/account-brief.test.ts

Estimated scope: M.

Checkpoint D — Phase 3 (validates AC-2 and AC-12)

Section titled “Checkpoint D — Phase 3 (validates AC-2 and AC-12)”
  • All three intake entry points populate the same ProposalContext schema.
  • RFP ingest preserves tables.
  • Account-brief prompt grounded against the static Company Profile.
  • bun run test:mcp-eval Layers 1 and 4 pass for account_brief.

Phase 4 — Brand voice + UK English enforcement substrate (validates AC-9)

Section titled “Phase 4 — Brand voice + UK English enforcement substrate (validates AC-9)”

Task 4.1: Configure brand-review enforcer with KH brand voice

Section titled “Task 4.1: Configure brand-review enforcer with KH brand voice”

Description: Connect the brand-review skill to the KH brand voice document as the single source. Author or reuse the brand voice document. Add a lib/proposals/qa/brand-review.ts adapter that exposes runBrandReview(content): Promise<Findings> for callers.

Acceptance criteria:

  • The KH brand voice document exists and is referenced as the only source for brand-review.
  • The adapter returns structured findings with severity (critical | warning | info).

Verification:

  • bun run test __tests__/lib/proposals/qa/brand-review.test.ts passes.

Dependencies: Phase 0.

Files likely touched:

  • lib/proposals/qa/brand-review.ts
  • docs/reference/kh-brand-voice.md (if not present)
  • __tests__/lib/proposals/qa/brand-review.test.ts

Estimated scope: S.

Description: Add lib/proposals/qa/uk-english.ts wrapping the existing uk-english skill so it returns the same structured-findings shape as 4.1.

Acceptance criteria:

  • The adapter returns structured findings with severity.
  • Test fixtures cover at least three UK/US deviations (color, optimize, MM/DD/YYYY).

Verification: bun run test __tests__/lib/proposals/qa/uk-english.test.ts passes.

Dependencies: Phase 0.

Files likely touched:

  • lib/proposals/qa/uk-english.ts
  • __tests__/lib/proposals/qa/uk-english.test.ts

Estimated scope: S.

Task 4.3: Section-save + export-gate hooks

Section titled “Task 4.3: Section-save + export-gate hooks”

Description: Wire 4.1 and 4.2 into lib/proposals/sections.ts so every section save runs both passes. Critical findings block export (set a flag on the proposal record); warnings surface as inline UI annotations. The export endpoint reads the flag and refuses to export when Critical findings remain unresolved.

Acceptance criteria (AC-9):

  • Section save triggers both passes.
  • Critical findings block export; non-critical findings produce inline warnings.
  • The KH brand voice document is the only configured source for brand-review.

Verification:

  • bun run test:integration __tests__/integration/proposals/qa-gate.integration.test.ts exercises the section-save → export-block path with seeded Critical findings.
  • An E2E spec asserts the warnings render in the authoring UI.

Dependencies: Tasks 4.1, 4.2; Task 1.1 (proposal record).

Files likely touched:

  • lib/proposals/sections.ts
  • lib/proposals/qa/run-gates.ts
  • app/api/proposals/[id]/sections/[sectionId]/route.ts
  • __tests__/integration/proposals/qa-gate.integration.test.ts

Estimated scope: M.

  • AC-9 acceptance criteria green.
  • No silent error swallow in the QA gates.

The four components below have disjoint file ownership and may run as parallel agents. Their typed interfaces are declared in types/proposal.ts (Task 1.2) and consumed by the assembly orchestrator in Phase 7.

Description: Implement components/proposals/roi-calculator.tsx and lib/proposals/roi/calculator.ts. Inputs: current state, target state, value drivers, risk-adjusted-return parameters. Output: a templated section conforming to the proposal-writer pricing-roi-business-case.md framework. Live preview re-renders within 200 ms of an input change. Three sector-scenario tests draw on existing KB case-study data.

Acceptance criteria (AC-3):

  • Inputs accepted; output structure matches the framework.
  • GBP and UK English throughout.
  • Editing an input updates the section preview within 200 ms (measured).
  • Three sector-scenario unit tests pass.

Verification:

  • bun run test __tests__/components/proposals/roi-calculator.test.tsx passes.
  • bun run test __tests__/lib/proposals/roi/calculator.test.ts passes.

Dependencies: Tasks 1.2, 4.3.

Files likely touched:

  • components/proposals/roi-calculator.tsx
  • lib/proposals/roi/calculator.ts
  • lib/proposals/roi/render.ts
  • hooks/proposals/use-roi-preview.ts

Estimated scope: M.

Description: Implement components/proposals/case-study-selector.tsx and lib/proposals/case-study/select.ts. The selector queries KB content_type case_study with sector + use-case + decision-driver match scoring and returns up to N candidates (default 3). The salesperson picks which to include. Selected case studies render as templated sections with KB-backed citations resolving to source records.

Acceptance criteria (AC-4):

  • A query with sector + use-case returns up to N ranked candidates.
  • Selected case studies render with citations that resolve.
  • No selection still allows the assembly to proceed (empty section permitted).

Verification:

  • bun run test __tests__/components/proposals/case-study-selector.test.tsx passes.
  • An integration test asserts citation resolution against a seeded KB.

Dependencies: Tasks 1.2, 4.3.

Files likely touched:

  • components/proposals/case-study-selector.tsx
  • lib/proposals/case-study/select.ts
  • lib/proposals/case-study/rank.ts
  • lib/proposals/case-study/render.ts
  • hooks/proposals/use-case-study-candidates.ts

Estimated scope: M.

Description: Implement components/proposals/quote-picker.tsx and lib/proposals/quote/select.ts. The picker filters quotes from q_a_pair, policy, methodology, and capability content by sector / use-case / topic and renders selected quotes inline with attribution. Citation orphans must report zero on the rendered draft.

Acceptance criteria (AC-5):

  • Filters surface matches from the four supported content_types.
  • Selected quotes render with attribution; citations resolve.
  • No orphaned citations.

Verification:

  • bun run test __tests__/components/proposals/quote-picker.test.tsx passes.
  • The citation-orphans hook reports zero on a rendered draft fixture.

Dependencies: Tasks 1.2, 4.3.

Files likely touched:

  • components/proposals/quote-picker.tsx
  • lib/proposals/quote/select.ts
  • lib/proposals/quote/render.ts
  • hooks/proposals/use-quote-candidates.ts

Estimated scope: S.

Description: Implement components/proposals/pricing-configurator.tsx and lib/proposals/pricing/configure.ts. Line-item table (description, unit, unit price, quantity, discount); totals re-compute live. Optional good/better/best packaging. The renderer encodes the proposal-writer pricing-presentation.md rule: pricing section position is locked to after value-establishing sections (the orchestrator enforces order; the renderer refuses to emit pricing if the consumer requests it before value sections). Currency is GBP.

Acceptance criteria (AC-6):

  • Line items add / edit / reorder / remove; totals re-compute live.
  • Good/better/best packaging supported where configured.
  • Pricing-section position is locked to after value sections.
  • GBP throughout.

Verification:

  • bun run test __tests__/components/proposals/pricing-configurator.test.tsx passes.
  • An integration test asserts the position-lock rule fires when the orchestrator is asked to render pricing first.

Dependencies: Tasks 1.2, 4.3.

Files likely touched:

  • components/proposals/pricing-configurator.tsx
  • lib/proposals/pricing/configure.ts
  • lib/proposals/pricing/render.ts
  • hooks/proposals/use-pricing-plan.ts

Estimated scope: M.

Checkpoint F — Phase 5 (validates AC-3, AC-4, AC-5, AC-6)

Section titled “Checkpoint F — Phase 5 (validates AC-3, AC-4, AC-5, AC-6)”
  • All four components have green Vitest coverage and are wired through the QA gates.
  • Citation-orphans report zero on a rendered draft.
  • No raw Tailwind colour utilities.

Phase 6 — Templates registry (validates AC-7) — parallel with Phase 5

Section titled “Phase 6 — Templates registry (validates AC-7) — parallel with Phase 5”

Description: Define the frontmatter shape every proposal-section template must declare (id, rule_reference, section_kind, page_budget_target, required_context_fields, optional_context_fields). Persist as lib/proposals/templates/schema.ts.

Acceptance criteria:

  • Schema parses every existing proposal-writer rule into a synthetic frontmatter for testing.
  • An invalid template fails parse with a typed error.

Verification: bun run test __tests__/lib/proposals/templates/schema.test.ts passes.

Dependencies: Task 1.2.

Files likely touched: lib/proposals/templates/schema.ts, __tests__/lib/proposals/templates/schema.test.ts.

Estimated scope: S.

Task 6.2: Template registry + per-rule templates

Section titled “Task 6.2: Template registry + per-rule templates”

Description: Implement lib/proposals/templates/registry.ts reading every .md file under lib/proposals/templates/ at boot. Author one template per proposal-writer rule listed in spec §FR-7 (architecture, executive-summary, pricing-presentation, pricing-roi-business-case, sow, terms-conditions, design-formatting, executive-stakeholder-alignment, strategy-competitive-differentiation, strategy-followup, plus rfp-response per OQ-1 outcome).

Acceptance criteria (AC-7 partial):

  • At least one template exists per rule listed in §FR-7.
  • Each template’s frontmatter parses.
  • Adding a new template file is picked up without code change.

Verification:

  • bun run test __tests__/lib/proposals/templates/registry.test.ts passes.
  • A fixture-sync guard test enumerates all template files and asserts each has a frontmatter entry the schema accepts.

Dependencies: Task 6.1.

Files likely touched:

  • lib/proposals/templates/registry.ts
  • lib/proposals/templates/<rule-id>.md (one per rule)
  • __tests__/lib/proposals/templates/registry.test.ts
  • __tests__/lib/proposals/templates/fixture-sync.test.ts

Estimated scope: M.

Task 6.3: Adapt feasibility-study + delivery-email templates

Section titled “Task 6.3: Adapt feasibility-study + delivery-email templates”

Description: Adapt the two highest-fit reusable templates from the Stage 1 audit: the feasibility-study template (annex slot for proposals) and the two delivery-email variants (cover-email pattern). Each lands as a registry-conformant template file with adapted placeholders pointing at ProposalContext fields.

Acceptance criteria (AC-7 partial):

  • Both adapted templates parse under the registry.
  • Cover-email variants exist for advisory / project / retainer engagement types.

Verification: Same fixture-sync guard from Task 6.2 covers these.

Dependencies: Task 6.2.

Files likely touched:

  • lib/proposals/templates/feasibility-study.md
  • lib/proposals/templates/cover-email-advisory.md
  • lib/proposals/templates/cover-email-project.md
  • lib/proposals/templates/cover-email-retainer.md

Estimated scope: S.

  • AC-7 acceptance criteria green.
  • Fixture-sync guard passes.

Phase 7 — Assembly orchestrator (validates AC-8)

Section titled “Phase 7 — Assembly orchestrator (validates AC-8)”

Description: Implement lib/proposals/assembly.ts. Inputs: ProposalContext + selected components’ state (ROI inputs, selected case studies, selected quotes, pricing plan). Output: a typed AssembledProposal with sections in the order defined by the proposal-writer structure-proposal-architecture.md rule (Executive Summary → Solution → Investment → Proof) and within the rule’s page-budget targets. The orchestrator selects templates from the registry and renders each section.

Acceptance criteria (AC-8 partial):

  • Assembling a draft from a populated context produces sections matching the rule’s architecture and page-budget targets.
  • Pricing-section position is locked after value sections (spec §R-7 mitigation).
  • LLM calls within the orchestrator are scoped to section-level rendering with retrieval-grounded context (spec §R-2 mitigation).

Verification:

  • bun run test __tests__/lib/proposals/assembly.test.ts passes.
  • An integration test asserts the section ordering and page-budget caps under a seeded context.

Dependencies: Tasks 5.1–5.4, 6.2.

Files likely touched:

  • lib/proposals/assembly.ts
  • lib/proposals/assemble-section.ts
  • __tests__/lib/proposals/assembly.test.ts
  • __tests__/integration/proposals/assembly.integration.test.ts

Estimated scope: L.

Task 7.2: Section regeneration with manual-edit preservation

Section titled “Task 7.2: Section regeneration with manual-edit preservation”

Description: Implement single-section regeneration. The orchestrator accepts a section id and regenerates only that section, preserving manual edits in other sections. A user opting to overwrite the section’s manual edits is supported as an explicit flag.

Acceptance criteria (AC-8 partial):

  • Regenerating one section preserves manual edits in the other sections.
  • Manual edits to the regenerated section survive unless the user opts to overwrite.

Verification: bun run test __tests__/lib/proposals/regenerate-section.test.ts passes.

Dependencies: Task 7.1.

Files likely touched:

  • lib/proposals/regenerate-section.ts
  • __tests__/lib/proposals/regenerate-section.test.ts

Estimated scope: M.

Task 7.3: Authoring page UI — assembled draft preview

Section titled “Task 7.3: Authoring page UI — assembled draft preview”

Description: Implement app/workspaces/[id]/proposals/[proposalId]/page.tsx showing the assembled draft. Editable section-by-section; each section carries a regenerate button and a manual-edit indicator. Initial render under 1.5 s on a 50-proposal workspace (spec §AC-17).

Acceptance criteria:

  • The authoring page renders the assembled draft.
  • Section regeneration triggers via the section’s regenerate button.
  • Manual edits are visually flagged.

Verification:

  • bun run test __tests__/components/proposals/section-editor.test.tsx passes.
  • An E2E spec exercises edit-regenerate-edit on three sections.

Dependencies: Tasks 7.1, 7.2.

Files likely touched:

  • app/workspaces/[id]/proposals/[proposalId]/page.tsx
  • components/proposals/section-editor.tsx
  • components/proposals/section-list.tsx
  • hooks/proposals/use-section-state.ts

Estimated scope: M.

  • AC-8 acceptance criteria green.
  • Section regeneration under 4 s, streaming where LLM-bound (spec §AC-17).
  • Pricing position-lock rule firing in integration test.

Phase 8 — Pre-send review (validates AC-10)

Section titled “Phase 8 — Pre-send review (validates AC-10)”

Task 8.1: Author proposal-readiness checks

Section titled “Task 8.1: Author proposal-readiness checks”

Description: Author check files under .claude/checks/proposal-readiness/: legal-clauses.md, pricing-set.md, citations-resolved.md, brand-review-pass.md, uk-english-pass.md, executive-summary-present.md, signoffs-captured.md. Each defines a single binary check evaluable by the existing review-skill machinery.

Acceptance criteria:

  • All seven check files exist and parse.
  • Each check has a specific, machine-evaluable assertion.

Verification: bun run test __tests__/lib/proposals/proposal-readiness-checks.test.ts parses all check files.

Dependencies: Phase 4.

Files likely touched: .claude/checks/proposal-readiness/*.md, __tests__/lib/proposals/proposal-readiness-checks.test.ts.

Estimated scope: S.

Task 8.2: Pre-send review wrapper + report

Section titled “Task 8.2: Pre-send review wrapper + report”

Description: Implement lib/proposals/review/run-readiness.ts invoking the existing review machinery against the check directory, producing a structured report. Wire to an API route at app/api/proposals/[id]/readiness/route.ts. The route returns pass/fail plus per-check pointers. Failing checks block export.

Acceptance criteria (AC-10):

  • All seven checks are exercised by an integration test.
  • A failing check blocks export; a passing report sets a green-state flag on the proposal.
  • The schema-drift guard test (spec §R-9) fails when a check references a field no longer in the schema.

Verification:

  • bun run test:integration __tests__/integration/proposals/readiness.integration.test.ts passes.
  • A schema-drift fixture asserts the guard fires when a renamed column is referenced.

Dependencies: Tasks 8.1, 1.1.

Files likely touched:

  • lib/proposals/review/run-readiness.ts
  • app/api/proposals/[id]/readiness/route.ts
  • __tests__/integration/proposals/readiness.integration.test.ts
  • __tests__/lib/proposals/proposal-readiness-schema-drift.test.ts

Estimated scope: M.

Checkpoint I — Phase 8 (validates AC-10)

Section titled “Checkpoint I — Phase 8 (validates AC-10)”
  • AC-10 acceptance criteria green.
  • Schema-drift guard armed.

Description: Adapt app/src/lib/docx-export.ts from the playbook into lib/proposals/export/docx.ts. Extend with cover page, headers/footers, page breaks, table support, image embed, and branded heading styles. The exporter consumes AssembledProposal. Branded heading styles encode the proposal-writer design-formatting.md rule.

Acceptance criteria (AC-11 partial):

  • DOCX export produces a file that opens cleanly in Microsoft Word and Google Docs.
  • DOCX export of a 30-page proposal completes in under 6 s server-side.

Verification:

  • bun run test __tests__/lib/proposals/export/docx.test.ts covers cover page, headers/footers, page breaks, tables, images, branded headings.
  • A 30-page fixture proposal exports under 6 s in CI.

Dependencies: Task 7.1.

Files likely touched:

  • lib/proposals/export/docx.ts
  • lib/proposals/export/docx-styles.ts
  • __tests__/lib/proposals/export/docx.test.ts

Estimated scope: L.

Task 9.2: PDF + HTML preview pipeline (route per OQ-2)

Section titled “Task 9.2: PDF + HTML preview pipeline (route per OQ-2)”

Description: Implement the PDF route chosen in Task 0.2. Either DOCX-to-PDF server-side conversion (one library; one CLI step downstream of 9.1) or HTML-to-PDF print-rendering (duplicate styling layer; print stylesheet). Implement HTML preview as the in-app live view consumed by the authoring page.

Acceptance criteria (AC-11 partial):

  • PDF export produces a file that renders correctly in the two most common readers.
  • HTML preview matches the DOCX layout within the documented styling-fidelity tolerance.
  • Export endpoint refuses to emit when Critical brand-review or UK-English findings remain (spec §FR-9, §FR-10 gate consumed here).

Verification:

  • bun run test __tests__/lib/proposals/export/pdf.test.ts covers the chosen pipeline.
  • An E2E spec at e2e/tests/proposals/export.spec.ts exercises DOCX + PDF + HTML preview from the assembled draft.

Dependencies: Tasks 0.2, 9.1.

Files likely touched:

  • lib/proposals/export/pdf.ts
  • lib/proposals/export/html-preview.ts
  • app/api/proposals/[id]/export/route.ts
  • e2e/tests/proposals/export.spec.ts

Estimated scope: M.

Description: Wire DOCX, PDF, and HTML preview behind a single app/api/proposals/[id]/export/route.ts that selects format from a query param. The endpoint records last_exported_at on the proposal, writes a pipeline-run row, and surfaces export errors via the Sentry path. No silent catches.

Acceptance criteria:

  • All three formats produced from the same canonical source.
  • last_exported_at updates on every successful export.
  • Pipeline-run row written.

Verification: bun run test __tests__/api/proposals/export.test.ts passes.

Dependencies: Tasks 9.1, 9.2.

Files likely touched: app/api/proposals/[id]/export/route.ts, __tests__/api/proposals/export.test.ts.

Estimated scope: S.

Checkpoint J — Phase 9 (validates AC-11)

Section titled “Checkpoint J — Phase 9 (validates AC-11)”
  • AC-11 acceptance criteria green.
  • DOCX export under 6 s for 30-page proposal.
  • No silent error swallow in the export pipeline.

Phase 10 — MCP App + remaining prompts (validates AC-13, AC-14)

Section titled “Phase 10 — MCP App + remaining prompts (validates AC-13, AC-14)”

Description: Scaffold mcp-apps/account-brief/ as a sibling of the four existing MCP Apps (bid-dashboard, coverage-matrix, intelligence-feed, reorient-me) using the same Vite single-file build. The App shows company snapshot, sector signals, recent KB hits, and a suggested intake form pre-fill. Registered as an MCP UI resource.

Acceptance criteria (AC-13):

  • The App appears alongside the existing four with the same Vite build.
  • Invoking /account-brief from a Claude.ai client launches the App with prospect intelligence.
  • The App’s “open in workspace” action seeds an intake-form pre-fill.

Verification:

  • bun run build:mcp-apps produces the new bundle.
  • Manual smoke test from a Claude.ai client.

Dependencies: Task 3.3.

Files likely touched:

  • mcp-apps/account-brief/** (Vite config, source, build)
  • lib/mcp/resources/account-brief.ts (UI resource registration)

Estimated scope: M.

Description: Register the proposal_shortcut prompt. Given a ProposalContext summary, returns a draft skeleton with section recommendations and ROI / pricing inputs pre-filled where derivable from KB content.

Acceptance criteria (AC-14 partial):

  • Prompt registers and is discoverable.
  • Output is grounded with resolvable citations.

Verification: MCP eval Layers 1, 3, 4 pass for the prompt.

Dependencies: Tasks 3.3, 7.1.

Files likely touched:

  • lib/mcp/prompts/proposal-shortcut.ts
  • lib/mcp/index.ts
  • __tests__/mcp/prompts/proposal-shortcut.test.ts

Estimated scope: M.

Description: Register the case_study_assembly prompt. Given a ProposalContext, returns a ranked candidate set for the case-study selector with brief justifications.

Acceptance criteria (AC-14 partial):

  • Prompt registers and is discoverable.
  • Output is grounded with resolvable citations.

Verification: MCP eval Layers 1, 3, 4 pass for the prompt.

Dependencies: Tasks 3.3, 5.2.

Files likely touched:

  • lib/mcp/prompts/case-study-assembly.ts
  • lib/mcp/index.ts
  • __tests__/mcp/prompts/case-study-assembly.test.ts

Estimated scope: M.

Checkpoint K — Phase 10 (validates AC-13, AC-14)

Section titled “Checkpoint K — Phase 10 (validates AC-13, AC-14)”
  • AC-13 and AC-14 acceptance criteria green.
  • All three prompts ground against KB content; citations resolve.
  • MCP inventory regenerated via bun run generate:mcp-inventory.

Phase 11 — Cover email + follow-up (validates AC-15, AC-16) — parallel inside this phase

Section titled “Phase 11 — Cover email + follow-up (validates AC-15, AC-16) — parallel inside this phase”

Task 11.1: Cover-email templating at send time

Section titled “Task 11.1: Cover-email templating at send time”

Description: Implement lib/proposals/cover-email/render.ts selecting from the three engagement-type variants in lib/proposals/templates/cover-email-*.md. The rendered email references the proposal record by ID and embeds the export filename. Surfaced from the authoring page’s “send” menu.

Acceptance criteria (AC-16):

  • At send time, a cover email is templated for the proposal type.
  • The email references the proposal record by ID and embeds the export filename.

Verification: bun run test __tests__/lib/proposals/cover-email.test.ts passes.

Dependencies: Task 6.3.

Files likely touched:

  • lib/proposals/cover-email/render.ts
  • components/proposals/send-menu.tsx
  • __tests__/lib/proposals/cover-email.test.ts

Estimated scope: S.

Task 11.2: Follow-up reminder surface + draft-outreach integration

Section titled “Task 11.2: Follow-up reminder surface + draft-outreach integration”

Description: Implement the follow-up surface in the authoring page sidebar. After a proposal is sent, reminders surface at the cadence the proposal-writer strategy-followup.md rule defines. The user can invoke the Anthropic Sales plugin’s draft-outreach skill with the proposal context flowing in. Reminder mechanism per OQ-8 resolution (in-app only, or in-app + notifications hook).

Acceptance criteria (AC-15):

  • After a proposal is sent, follow-up reminders surface at the documented cadence.
  • Invoking draft-outreach from inside the workspace flows the proposal context into the outreach skill and returns a personalised draft.

Verification:

  • bun run test __tests__/components/proposals/follow-up-panel.test.tsx passes.
  • An E2E spec at e2e/tests/proposals/follow-up.spec.ts exercises the post-send reminder flow.

Dependencies: Tasks 0.3 (OQ-8), 9.3.

Files likely touched:

  • components/proposals/follow-up-panel.tsx
  • lib/proposals/follow-up/schedule.ts
  • lib/proposals/follow-up/draft-outreach-bridge.ts
  • e2e/tests/proposals/follow-up.spec.ts

Estimated scope: M.

Checkpoint L — Phase 11 (validates AC-15, AC-16)

Section titled “Checkpoint L — Phase 11 (validates AC-15, AC-16)”
  • AC-15 and AC-16 acceptance criteria green.

Phase 12 — Non-functional gates + golden path (validates AC-17)

Section titled “Phase 12 — Non-functional gates + golden path (validates AC-17)”

Description: Add timing assertions to the workspace-list, authoring-page-render, DOCX-export, and section-regeneration paths matching the budgets in spec §5.3 and §AC-17. CI fails on regression.

Acceptance criteria (AC-17 partial):

  • Workspace list under 800 ms warm.
  • Authoring page initial render under 1.5 s on a 50-proposal workspace.
  • DOCX export under 6 s for 30-page proposal.
  • Section regeneration under 4 s, streaming where LLM-bound.

Verification: Custom CI check at __tests__/integration/proposals/performance.integration.test.ts.

Dependencies: Phases 2, 7, 9.

Files likely touched: __tests__/integration/proposals/performance.integration.test.ts.

Estimated scope: S.

Description: Run an accessibility audit against the workspace-list and authoring pages. WCAG 2.1 AA target. Fix any colour-only signals (status colour pairs with iconography or text).

Acceptance criteria (AC-17 partial):

  • Lighthouse accessibility score ≥95 on the two pages.
  • No raw Tailwind colour utilities introduced (lint guard fires).
  • All status states present an iconographic or textual signal alongside colour.

Verification: bun run test __tests__/lib/proposals/accessibility.test.ts; manual Lighthouse audit.

Dependencies: Phases 2, 7.

Files likely touched: Components under components/proposals/ may receive iconography/text additions.

Estimated scope: S.

Description: Confirm all new code passes the existing guards: local/no-unchecked-supabase-error, local/no-silent-promise-catch, the validation-sweep guard (any route reading request.nextUrl.searchParams or request.json() imports parseBody / parseSearchParams), the no-raw-Tailwind-colour guard, the integration-test-location guard.

Acceptance criteria (AC-17 partial):

  • bun run lint clean.
  • All ESLint custom rules pass.
  • No new *.integration.test.{ts,tsx} outside __tests__/integration/.

Verification: CI runs bun run lint; integration-location guard test passes.

Dependencies: All prior phases.

Files likely touched: Any with cleanup needed.

Estimated scope: XS.

Task 12.4: Golden-path E2E + knip + stats regen

Section titled “Task 12.4: Golden-path E2E + knip + stats regen”

Description: Author the golden-path E2E spec at e2e/tests/proposals/golden-path.spec.ts exercising create → intake → assemble → export end-to-end. Multi-role variants (admin / editor / viewer) cover the permission boundaries. Run bun run knip to catch orphans. Regenerate docs/generated/codebase-stats.md and docs/generated/mcp-inventory.md end-of-session.

Acceptance criteria:

  • Golden-path E2E spec passes for editor; viewer is read-only; admin can hard-delete.
  • bun run knip reports no new orphans.
  • Generated stats and MCP inventory regenerated.

Verification:

  • bun run test:e2e e2e/tests/proposals/ passes.
  • bun run knip clean.

Dependencies: Phases 2, 3, 7, 8, 9.

Files likely touched: e2e/tests/proposals/golden-path.spec.ts, docs/generated/codebase-stats.md, docs/generated/mcp-inventory.md.

Estimated scope: M.

Checkpoint M — Phase 12 (validates AC-17)

Section titled “Checkpoint M — Phase 12 (validates AC-17)”
  • AC-17 acceptance criteria green.
  • Golden-path E2E spec green for all three roles.
  • bun run knip, bun run lint, bun run test, bun run test:e2e, bun run test:mcp-eval all green.
  • Generated docs refreshed.

Task 13.1: Update roadmap + state-of-the-product

Section titled “Task 13.1: Update roadmap + state-of-the-product”

Description: Add the §9.2 implementation entry to docs/reference/product-roadmap.md referencing this spec. After merge, add a sales-proposals capability section to docs/reference/state-of-the-product.md.

Acceptance criteria:

  • Roadmap entry exists with cross-reference to the spec and the plan.
  • State-of-the-product is updated post-merge (not pre-merge per the canonical-discipline rule).

Verification: Manual review.

Dependencies: Phase 12 complete and merged.

Files likely touched: docs/reference/product-roadmap.md, docs/reference/state-of-the-product.md.

Estimated scope: XS.

Every spec AC is addressed by at least one task. Tasks marked with multiple ACs deliver the criteria jointly.

ACTitleTasks
AC-1Workspace lifecycle2.1, 2.2, 2.3
AC-2ProposalContext intake3.1, 3.2, 3.3
AC-3ROI calculator5.1
AC-4Case-study selector5.2
AC-5Quote picker5.3
AC-6Pricing configurator5.4
AC-7Templates6.1, 6.2, 6.3
AC-8Assembly logic7.1, 7.2, 7.3
AC-9Brand voice + UK English4.1, 4.2, 4.3
AC-10Pre-send review8.1, 8.2
AC-11Export9.1, 9.2, 9.3
AC-12RFP ingest3.2
AC-13Account Brief MCP App10.1
AC-14Three MCP prompts3.3, 10.2, 10.3
AC-15Follow-up workflow11.2
AC-16Cover email11.1
AC-17Non-functional gates12.1, 12.2, 12.3, 12.4

Tasks below have disjoint file ownership and may run as parallel agents under the existing isolation: "worktree" pattern. Each parallel group has a coordination point (a checkpoint) where the parent thread merges branches sequentially.

  • Group P1 — Phase 5 components. Tasks 5.1, 5.2, 5.3, 5.4 own separate subdirectories under components/proposals/<x>/ and lib/proposals/<x>/; their interfaces are pre-declared in types/proposal.ts (Task 1.2). Run as four parallel agents. Coordination: Checkpoint F.
  • Group P2 — Phase 6 templates. Task 6.2’s per-rule template authoring may be split across two agents (architecture + executive + pricing rules to one; SOW + RFP + design + strategy rules to another). Coordination: Checkpoint G.
  • Group P3 — Phase 5 + Phase 6 cross-phase. Phase 5 components and Phase 6 templates have disjoint file ownership and interface only inside the assembly orchestrator (Phase 7). Both phases may run in parallel after Phase 4 completes. Coordination: Checkpoints F and G before Phase 7 begins.
  • Group P4 — Phase 10 prompts. Tasks 10.2 and 10.3 are independent prompt registrations. Run as two parallel agents after Task 10.1 completes. Coordination: Checkpoint K.
  • Group P5 — Phase 11. Tasks 11.1 and 11.2 touch disjoint files and may run in parallel. Coordination: Checkpoint L.

Tasks below must run sequentially because they share state or sit on the dependency chain:

  • Sequential — Phase 1. Task 1.1 (migration) before Task 1.2 (types referencing the schema) before Task 1.3 (guard).
  • Sequential — Phase 4. Tasks 4.1 + 4.2 before Task 4.3 (Task 4.3 wires both into the section-save hook).
  • Sequential — Phase 7. Task 7.1 before 7.2 before 7.3 (each builds on the prior).
  • Sequential — Phase 9. Task 9.1 before 9.2 (PDF route may depend on DOCX output) before 9.3 (endpoint wires both).
  • Sequential — Phase 12. Performance tests after the relevant phases land; golden-path E2E last because it exercises everything.

The risks and mitigations are inherited from spec §10 and reflected in the plan as follows.

RiskMitigation in plan
R-1 — bid-writing duplicationTask 0.1 forces resolution before any template authoring.
R-2 — orchestrator hallucinationTask 7.1 acceptance constrains LLM calls to section-level rendering with retrieval-grounded context.
R-3 — DOCX styling fidelityTask 0.2 spike + Task 9.1 + Task 9.2 split ownership between DOCX and PDF/HTML.
R-4 — brand-review noiseTask 4.3 codifies the severity model (Critical blocks, non-Critical warns).
R-5 — workspace-type filter driftTask 1.3 adds a guard.
R-6 — refactor pressure for shared workspace abstractionPlan does not introduce a workspace abstraction; defers per spec §R-6.
R-7 — pricing-section ordering vs orchestrator orderTask 7.1 acceptance encodes the rule’s order; Task 5.4’s renderer enforces.
R-8 — RFP table lossTask 3.2 mandates the documented two-step HTML → Turndown path with a fixture test.
R-9 — pre-send check schema driftTask 8.2 adds a schema-drift guard.
R-10 — MCP prompt grounding regressionTask 3.3 pins to a versioned grounding source; Phase-2 grounding is out of scope.

The plan inherits spec §11’s open questions and surfaces them as Phase 0 tasks. No additional plan-level questions are introduced.

The plan resolves to the following on-disk artefacts.

  • Input spec — docs/specs/sales-proposals-workspace-spec-arm-b.md (§1 to §13).
  • Input research — docs/research/sales-proposals-reuse-audit-arm-b.md (Bucket assessments inform Phase 5 component dispositions and Phase 6 template adaptations).
  • Knowledge Hub conventions — CLAUDE.md (commands, gotchas, design system, sandbox, parallel tracks). Used throughout the file-touch boundaries (spec §8) and the verification gates.
  • The decomposition was almost entirely a function of the spec quality, not the planning effort. Spec §6 (component dispositions) and §7 (acceptance criteria, one per FR) gave a near-finished task graph; the plan’s job was to order them, identify the parallel groups, and bolt verification gates onto each. The longest single deliberation in the planning pass was deciding whether to split Phase 5 and Phase 6 or run them in parallel — the disjoint file ownership made that decision easy once the directory layout was sketched.
  • Phase 0 (resolve open questions) was added explicitly because three of the eight OQs change downstream task scope: OQ-1 changes whether RFP-response templates exist (Task 6.2), OQ-2 changes the entire shape of Task 9.2, and OQ-3 turns Task 1.4 from a no-op into a schema extension. Treating these as code-blocking is the cheapest risk reduction available.
  • The component phase (Phase 5) is the natural parallelisation prize: four tasks, four agents, four directories. Naming the per-component subdirectories (components/proposals/roi-calculator/, lib/proposals/roi/ and so on) up front in this plan removes the ambiguity that usually causes parallel-agent worktree collisions later.
  • Testing discipline tracks the AC structure verbatim: every AC bullet maps to a Vitest or Playwright assertion, and every task’s verification line names a runnable command. This is heavier than the planning skill’s minimal template asks for, but the spec’s CLAUDE.md gotcha density (vector serialisation, generated columns, sandboxed Bash, gitignored env files, validation-sweep guard, etc.) makes implicit verification dangerous — naming the command at task time is cheaper than rediscovering the right invocation under time pressure during impl.
  • The plan deliberately defers a workspace abstraction. Three workspace types is the smallest sample size from which to refactor, and spec §R-6 says wait for the fourth. Phase 13 Documentation flags the deferral so the next planner knows what was on the table and what was not.