Skip to content

Sales Proposal Workspaces — TECH

Status: [CURRENT-CANONICAL] — NEW-S243 (Wave 0 of Phase 1). Companion to PRODUCT.md. Per-invariant implementation references for the Phase 1 reserved-seat landing + the explicit Phase 3 ALTER pathway. Schema commitments at Phase 1 are inherited from docs/specs/reserved-workspace-seats/TECH.md T-1..T-8; per-app columns are deferred to Phase 3 build time and not constrained by this spec.

This file carries implementation references for each S-N invariant in PRODUCT.md. Each T-N entry contains:

  • Current state: code / migration file:line that today implements the invariant, or “greenfield” if no code exists.
  • Target state: what the platform must do after the relevant migration applies (Phase 1 for the reserved seat; Phase 3 for per-app columns).
  • Gate: any STILL-OPEN dependency.
  • Validation: how the invariant is verified (CI guard / parity test / manual).

Phase 1 T-Ns (T-1..T-5) implement the Phase 1 reserved-seat shape and inherit verbatim from docs/specs/reserved-workspace-seats/TECH.md. Phase 3 T-Ns (T-6..T-7) document the deferred-build pathway — they describe how Phase 3 will pick up the seat, not what Phase 3 will add. A T-N gap-flag entry (T-8) carries the construction-guide §5.3 research-substrate flag from PRODUCT.md S-8.

  • ./PRODUCT.md — numbered invariants S-1..S-8.
  • docs/specs/reserved-workspace-seats/PRODUCT.md S-1..S-8 + docs/specs/reserved-workspace-seats/TECH.md T-1..T-8 — Phase 1 seat-shape ratification this spec inherits.
  • docs/specs/id-38-rls-pattern/PRODUCT.md P-1 + P-2 + P-3 + docs/specs/id-38-rls-pattern/TECH.md T-1 + T-2 — RLS auto-trigger + grants helper this spec leverages.
  • docs/specs/id-31-canonical-pipeline-implementation-plan/PLAN.md §4.2 T2 subtask 7 (Phase 1 combined-PR migration includes 5 reserved seats) + §5 row “sales-proposal-workspaces” + §8 Wave 0 entry 2b.
  • docs/reference/project-plan.md §7 (Phase 3 closure criteria revised per RATIFIED-S243 Item 13).
  • docs/plans/phase-0-investigation/architecture/04-workspace-types.md §3.2 + §4.2 + §4.3 — sales_proposal application_type row + upfront-seats decision + extensibility procedure.
  • supabase/migrations/20260514150238_enable_rls_auto_event_trigger_and_grants_pattern.sql — RLS-PATTERN combined migration providing rls_auto_enable() event trigger + grant_standard_public_table_access(regclass) helper this spec depends on at Phase 1 apply time.
  • Pattern precedent: docs/specs/procurement-workspaces/TECH.md T-3 (satellite shape + 1:1 cardinality DDL) and docs/specs/reserved-workspace-seats/TECH.md T-1 (combined reserved-seat migration body).

Phase 1 migration authors implementing T2 subtask 7 of PLAN.md §4.2 (5 reserved seats land in the combined-PR migration); reviewers verifying compliance against PRODUCT.md + the inherited RWS spec; Phase 3 build kickoff authors who will draft the per-column ALTER migrations per T-6 + T-7.


T-1 — Satellite shape at Phase 1: reserved-seat only (implements S-1, S-2, S-3)

Section titled “T-1 — Satellite shape at Phase 1: reserved-seat only (implements S-1, S-2, S-3)”

Current state: Greenfield. sales_proposal_workspaces does not exist in either staging (turayklvaunphgbgscat) or production (rovrymhhffssilaftdwd) schemas as of S244 Wave 0. Grep over lib/, scripts/, types/, app/, components/ returns no live sales_proposal_workspaces references (verified S244 Wave 0 drafting); the only matches are scripts/audit-cross-arm-contamination.py references to docs/research/sales-proposals-*-arm-*.md audit patterns (research-doc paths — not code), which are outside the seat-shape scope and surface against PRODUCT.md S-8 as the research-substrate gap.

Target state: Phase 1 combined-PR migration (T2 subtask 7 of PLAN.md §4.2) creates sales_proposal_workspaces with the reserved-seat shape per RWS T-1, alongside the four other reserved seats and within the same BEGIN; … COMMIT; envelope.

Migration body shape (inherited from RWS T-1 migration body verbatim — included here for cross-doc traceability, NOT to commit to a separate migration file):

CREATE TABLE public.sales_proposal_workspaces (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id uuid NOT NULL UNIQUE REFERENCES workspaces(id) ON DELETE CASCADE,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.sales_proposal_workspaces ENABLE ROW LEVEL SECURITY; -- belt-and-braces per RWS T-4
SELECT grant_standard_public_table_access('public.sales_proposal_workspaces'::regclass); -- RWS T-6 / RLS-PATTERN P-2
-- per-tenant RLS policies — see T-3 below

No per-app columns. Per PRODUCT.md S-2 + S-6 the Phase 1 column set is strictly id + workspace_id + created_at + updated_at. Any column beyond this set MUST land at Phase 3 via the ALTER pathway in T-6 — the Phase 1 combined-PR migration does not extend sales_proposal_workspaces beyond the seat shape. Reviewers verifying T2 subtask 7 should treat any sales-proposal-specific column appearing in the Phase 1 migration as a violation of S-6.

Lands in: supabase/migrations/<timestamp>_create_reserved_workspace_seats.sql (NEW, drafted as part of T2 subtask 7 — the same migration file landing all 5 reserved seats per RWS T-1; not a sibling per-application migration). This spec does NOT introduce a separate migration file at Phase 1.

Gate: RLS-PATTERN combined migration (20260514150238_*.sql) MUST apply first so rls_auto_enable() event trigger + grant_standard_public_table_access(regclass) helper exist (per RWS T-1 gate). Per PLAN.md §4.3 T3 the RLS-PATTERN migration is DONE-S239 (verified S243), so this gate is satisfied at Phase 1 dispatch time.

Validation:

  • Apply migration to staging; assert SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='sales_proposal_workspaces' returns 1.
  • Column-set check: SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema='public' AND table_name='sales_proposal_workspaces' ORDER BY ordinal_position returns exactly the 4 expected columns (id, workspace_id, created_at, updated_at) — no per-app columns leaked.
  • 1:1 cardinality check: SELECT * FROM information_schema.table_constraints WHERE table_name='sales_proposal_workspaces' AND constraint_type IN ('FOREIGN KEY', 'UNIQUE') returns the expected FK + UNIQUE on workspace_id.
  • Cascade check: insert a workspace + satellite row, delete the workspace, assert the satellite row count is 0.

T-2 — RLS enabled at Phase 1 apply time (implements S-4)

Section titled “T-2 — RLS enabled at Phase 1 apply time (implements S-4)”

Current state: Greenfield seat table. The rls_auto_enable() event trigger is live in production + staging per PLAN.md §4.3 T3 (DONE-S239, verified S243 per docs/specs/phase-1-kickoff-scoping-s243.md §3).

Target state: Immediately after the CREATE TABLE public.sales_proposal_workspaces (...) statement returns at Phase 1 apply time, pg_class.relrowsecurity = true for the seat. Two mechanisms apply (both inherited from RWS T-4):

  1. The event trigger fires on the CREATE TABLE command tag and enables RLS without explicit DDL.
  2. The migration also emits an explicit ALTER TABLE public.sales_proposal_workspaces ENABLE ROW LEVEL SECURITY immediately after CREATE TABLE as a belt-and-braces safeguard (idempotent against the event trigger; covers the corner case where migration order somehow inverts).

Gate: RLS-PATTERN migration applied (satisfied per T-1 gate).

Validation:

  • Post-apply, SELECT relname, relrowsecurity FROM pg_class WHERE relname='sales_proposal_workspaces' returns (sales_proposal_workspaces, true).

T-3 — Per-tenant RLS policies at Phase 1 (implements S-4 access path)

Section titled “T-3 — Per-tenant RLS policies at Phase 1 (implements S-4 access path)”

Current state: Greenfield. Pattern precedent: existing satellite tables (notably the post-rename procurement_workspaces) install RLS policies that JOIN through to workspaces via workspace_id. RWS T-5 describes the canonical 4-policy pattern (select / insert / update / delete) inheriting tenant scope via EXISTS subquery.

Target state: Phase 1 migration installs 4 RLS policies on sales_proposal_workspaces (select / insert / update / delete) that delegate to the parent workspaces row via workspace_id. The policy predicate is shared verbatim across the 5 reserved seats; the workspaces-level access predicate (role-based via get_user_role() per CLAUDE.md) is canonical in the workspaces table’s own RLS policies and is NOT duplicated at the satellite level.

Policy shape (SELECT example; INSERT / UPDATE / DELETE mirror the same EXISTS pattern with WITH CHECK / USING as appropriate):

CREATE POLICY sales_proposal_workspaces_select
ON public.sales_proposal_workspaces
FOR SELECT
USING (
EXISTS (
SELECT 1
FROM workspaces w
WHERE w.id = sales_proposal_workspaces.workspace_id
-- tenant access check delegated to workspaces-level RLS;
-- exact predicate matches workspaces' USING clause
)
);

Lands in: Same migration as T-1 (Phase 1 combined-PR — supabase/migrations/<timestamp>_create_reserved_workspace_seats.sql). 1 seat × 4 policies = 4 CREATE POLICY statements for this satellite; 5 reserved seats × 4 policies = 20 CREATE POLICY statements total for T2 subtask 7.

Gate: Requires workspaces table RLS policies stable — confirmed in current production schema per PLAN.md §4.3 T3 (RLS-PATTERN landed).

Validation:

  • Connect as authenticated role with tenant A; insert a workspace bound to application_type='sales_proposal' + its satellite row; assert the satellite is visible to tenant A.
  • Switch to tenant B; assert the satellite row is not visible.
  • Migration test fixture covers this for the procurement seat already (post-rename per PLAN.md §4.4 T4); RWS T-5 commits the equivalent fixture for the 5 reserved seats — this spec inherits that test coverage by reference.

T-4 — Per-role grants at Phase 1 apply time (implements S-5)

Section titled “T-4 — Per-role grants at Phase 1 apply time (implements S-5)”

Current state: Greenfield seat. grant_standard_public_table_access(regclass) helper is live per PLAN.md §4.3 T3 (RLS-PATTERN T-2 landed).

Target state: Phase 1 migration calls SELECT grant_standard_public_table_access('public.sales_proposal_workspaces'::regclass); immediately after the CREATE TABLE + explicit ALTER ... ENABLE ROW LEVEL SECURITY sequence (see T-1 migration body shape). The 3-role grant set (anon SELECT, authenticated full CRUD, service_role full CRUD) is applied for the Data API surface; without this call, PostgREST returns 42501 permission denied per RLS-PATTERN P-3 (fail-loud).

Gate: RLS-PATTERN grants helper landed (satisfied).

Validation:

  • Post-apply, SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name='sales_proposal_workspaces' ORDER BY grantee, privilege_type returns the expected 3-role pattern: anon SELECT; authenticated SELECT, INSERT, UPDATE, DELETE; service_role SELECT, INSERT, UPDATE, DELETE.
  • Negative-case parity with RLS-PATTERN P-3: at staging, after migration apply, hit PostgREST /sales_proposal_workspaces as anon and assert non-permission-denied response (anon SELECT grants applied); then if any negative scenario is needed for the audit trail, temporarily revoke and re-assert 42501 permission denied (revert the revoke before sign-off).

T-5 — Naming convention frozen (implements S-1 — inherits the RWS S-8 naming-frozen ratification)

Section titled “T-5 — Naming convention frozen (implements S-1 — inherits the RWS S-8 naming-frozen ratification)”

Current state: The sales_proposal_workspaces name is fixed in docs/plans/phase-0-investigation/architecture/04-workspace-types.md §3.2 (Q-OQR1-03 vocabulary table — sales_proposal row pairs with sales_proposal_workspaces satellite name per Q-OQR1-113-A naming convention).

Target state: The Phase 1 migration filename + table name match the spec verbatim. Any rename to the satellite table or to the underlying application_types.key value requires a separate ratification cycle (precedent: bid_workspaces → procurement_workspaces per 0.9-decision-graph.md §11.3 row 4 required a full ratification cycle).

Gate: None — names are settled at S240 + S243.

Validation:

  • Inspection: Phase 1 migration file diff vs RWS PRODUCT.md S-1 name list (which includes sales_proposal_workspaces per RWS S-1).
  • CI parity: schema-parity.yml workflow flags any drift between staging and production once both apply.

T-6 — Phase 3 ALTER pathway for per-app columns (implements S-6)

Section titled “T-6 — Phase 3 ALTER pathway for per-app columns (implements S-6)”

Current state: Greenfield. No Phase 3 migration drafted; no per-app columns proposed in this spec.

Target state: At Phase 3 build kickoff, the Phase 3 build cycle drafts a sibling migration (or migrations) that extend sales_proposal_workspaces via ALTER TABLE ... ADD COLUMN ... against the existing seat. The shape of those columns is NOT constrained by this spec — column-list ratification is owned by the Phase 3 feature spec extension (per S-6 + S-7 of PRODUCT.md) and informed by the research substrate per S-8 gap flag.

ALTER pattern (illustrative — column names are placeholders pending Phase 3 ratification; this spec does NOT commit to these names):

-- Example Phase 3 ALTER (NOT ratified at S244 Wave 0 drafting; Phase 3 spec authoritative):
ALTER TABLE public.sales_proposal_workspaces
ADD COLUMN <proposal_stage_col> TEXT,
ADD COLUMN <client_name_col> TEXT,
ADD COLUMN <outcome_col> TEXT;
-- exact column names + types + nullability + CHECK constraints owned by Phase 3 feature spec

Discipline rules (inherited from RWS S-7 / T-7):

  • The Phase 3 migration MUST use ALTER TABLE against the existing seat, NOT DROP TABLE + CREATE TABLE. The seat is a stable FK target since Phase 1 apply time.
  • Column-level grants inherit from the table-level grants helper call at Phase 1 (T-4) — no per-column GRANT statements required.
  • Column-level RLS continues to inherit from the table-level policies at T-3 — no per-column policy required.
  • Migration filename convention: supabase/migrations/<timestamp>_add_sales_proposal_workspaces_columns.sql (or finer per-feature naming if the Phase 3 build phases the column set across multiple migrations).
  • 1:1 cardinality (S-3) must hold post-ALTER — Phase 3 columns are added to the satellite, NOT split into a sibling table that would tear the cardinality.

Lands in: Phase 3 build cycle — a separate migration file (or files) not yet drafted. This spec does NOT propose a migration file at Phase 1.

Gate: Phase 3 build kickoff. Per RATIFIED-S243 Item 13, Phase 3 starts from existing sales-proposal research substrate — see T-8 (the research-substrate path is the explicit gap flag). Phase 3 kickoff continuation prompt should treat the substrate-path confirmation as a Liam pre-decision item.

Validation:

  • At Phase 3 ALTER apply time, the per-app columns appear in information_schema.columns for sales_proposal_workspaces alongside the Phase 1 columns; the Phase 1 columns remain unchanged.
  • RLS policies still hold post-ALTER (re-run the T-3 tenant A/B isolation test).
  • Grants still hold post-ALTER (re-run the T-4 PostgREST grant check).
  • 1:1 cardinality still holds (re-run the T-1 cascade-delete fixture).
  • v1.1 candidate guard per RWS T-7 (CI lint that no migration re-creates an existing reserved seat) applies if landed by Phase 3.

T-7 — Per-app behaviour surfaces deferred to Phase 3 (implements S-7)

Section titled “T-7 — Per-app behaviour surfaces deferred to Phase 3 (implements S-7)”

Current state: Greenfield. No lib/sales_proposal/ directory exists; no composer surface, no workflow state machine code, no export pipeline at S244 Wave 0.

Target state: Phase 3 build cycle owns these surfaces. Per the per-application-type code convention in 04-workspace-types.md §4.3 step 4, sales-proposal state-machine code lands at lib/sales_proposal/sales_proposal_workflow.ts (analogue of lib/procurement/procurement-workflow.ts); per-application UI lands at components/sales_proposal/ (analogue of components/procurement/); per-application MCP tooling registers under lib/mcp/tools/ with sales_proposal_* prefixes (analogue of the procurement tool prefix per PLAN.md §4.13 T13 MCP tool rename pattern).

This spec reserves the directory + file naming convention by reference to the precedent pattern; it does NOT commit code at Phase 1. No lib/sales_proposal/ directory is created at Phase 1; the directory exists from Phase 3 build kickoff.

Gate: Phase 3 build kickoff + research-substrate path (T-8) + canonical pipeline T1..T13 closure (per PLAN.md §8 Wave ordering — Phase 3 build depends on Phase 1 pipeline being stable).

Validation: N/A at Phase 1 (nothing to validate — surface is deferred). Phase 3 build cycle owns validation per its own TECH.md sections drafted at Phase 3 kickoff.


T-8 — Research-substrate noted, Liam-ratification + canonical-pipeline reconciliation gap flag (carries S-8 from PRODUCT.md)

Section titled “T-8 — Research-substrate noted, Liam-ratification + canonical-pipeline reconciliation gap flag (carries S-8 from PRODUCT.md)”

Candidate substrate identified (per PRODUCT.md S-8 updated S244 Wave 0):

PathDateSizeRole
docs/research/sales-proposals-reuse-audit-arm-a.md28/04/202675 KBReuse-audit arm A.
docs/research/sales-proposals-reuse-audit-arm-b.md28/04/202671 KBReuse-audit arm B (twin arm).
docs/plans/sales-proposals-workspace-plan-arm-a.md12/05/202658 KBWorkspace-plan arm A.
docs/plans/sales-proposals-workspace-plan-arm-b.md12/05/202659 KBWorkspace-plan arm B (twin arm).

Gap flagged (construction-guide §5.3 format):

  • Specific gap: Candidate substrate identified but not Liam-reviewed yet and all four files pre-date the canonical-pipeline pivot (S241 ratifications 16-18/05/2026; PLAN.md authored 18/05/2026). Pre-pivot data-model assumptions in these files (workspace shape, application_type binding, satellite-vs-monolithic placement, q_a_pairs cross-domain reuse hook, RLS / grants patterns) require reconciliation against post-pivot canonical-pipeline scope before adoption-as-written.
  • Sources checked: docs/reference/project-plan.md §7 (refers to “existing sales-proposal research documentation prepared pre-canonical-pipeline”), filesystem listing (ls docs/research/sales-proposals-*.md docs/plans/sales-proposals-*.md returns the four files above), Liam’s S244 Wave 0 note (“Those are the correct files, and can be noted, it’s just that they were created prior to my being able to review them yet, and also they were created long before the pivot to the canonical pipeline”).
  • What was looked for at S244 Wave 0: A ratified, post-canonical-pipeline pointer to the Phase 3 substrate. The four files are correctly identified as substrate-of-record but their ratification status is “candidate, awaiting Liam-review + canonical-pipeline reconciliation”.
  • Category: Source Documentation (sub-category of Investigation needed per construction-guide §5.2) — the question is empirical-plus-product: “which arm (or merge) survives the post-canonical-pipeline review pass, and which provisions need replacement?”
  • Prerequisite work (gates Phase 3 build kickoff, NOT Phase 1):
    1. Liam-ratification pass on all four documents (arm-a vs. arm-b selection per pair, or merge synthesis).
    2. Canonical-pipeline reconciliation pass identifying and replacing any provisions superseded by Q-OQR1-* ratifications (Q-OQR1-03 application_types vocabulary, S240 upfront-seats, Q-OQR1-113-A satellite cardinality, RLS-PATTERN, etc.).
    3. Arm structure resolution — twin-arm pattern suggests deliberate option-pairing; selection or merge owned by Phase 3 kickoff.
  • What this spec does in the meantime: ships the Phase 1 reserved-seat shape (T-1..T-5) and defers all Phase 3 column / behaviour ratifications (T-6 + T-7). The Phase 3 kickoff continuation prompt should treat the Liam-review + canonical-pipeline reconciliation passes as Liam pre-decision items. Phase 3 per-app spec extension (or PHASE-3-COLUMNS.md sibling per PRODUCT.md S-6) cites the surviving arm(s) post-Liam-review as substrate.

Gate (Phase 3 only): Phase 3 build kickoff is gated on the Liam-review + canonical-pipeline reconciliation passes completing. Phase 1 is NOT gated on this gap — the reserved-seat landing (T-1..T-5) ships at Phase 1 with no dependency on the substrate scope.

Validation: N/A as a runtime invariant — this is a documentation + ratification gap, validated by inspection of the Phase 3 kickoff continuation prompt / spec extension. Once the Liam-review pass selects/merges arms and the canonical-pipeline reconciliation pass resolves provision conflicts, the surviving substrate path(s) are backfilled into PRODUCT.md S-8 cross-refs + the per-column spec extension at Phase 3 kickoff.


DocWhat it references
docs/specs/reserved-workspace-seats/PRODUCT.md + TECH.mdPhase 1 seat-shape ratification this spec inherits — all 8 RWS invariants (S-1..S-8) covered via SP S-1..S-6 + T-5 mapping (SP S-5 ⇐ RWS S-6 grants; SP S-6 ⇐ RWS S-7 ALTER discipline; SP T-5 ⇐ RWS S-8 naming-frozen).
docs/specs/id-38-rls-pattern/PRODUCT.md + TECH.mdRLS auto-trigger (P-1 / T-1) + grants helper (P-2 / T-2) + fail-loud (P-3) this spec depends on at Phase 1 apply time.
docs/specs/id-31-canonical-pipeline-implementation-plan/PLAN.md §4.2 T2 subtask 7Phase 1 combined-PR migration creates this satellite alongside the four other reserved seats.
docs/specs/id-31-canonical-pipeline-implementation-plan/PLAN.md §5 row “sales-proposal-workspaces”Spec destination row — Wave 0 drafting + Phase 3 per-app ALTER deferral.
docs/reference/project-plan.md §7Phase 3 closure criteria revised per RATIFIED-S243 Item 13 — full application functionality, not Q&A reuse alone.
docs/plans/phase-0-investigation/architecture/04-workspace-types.md §3.2 + §4.2 + §4.3sales_proposal application_type row + upfront-seats decision + extensibility procedure.
Future Phase 3 per-column spec extension or PHASE-3-COLUMNS.md siblingPer-app columns + composer / workflow / export surfaces — owned by Phase 3 build cycle.
docs/reference/SCHEMA-QUICK-REFERENCE.mdPost-apply update at Phase 1 — sales_proposal_workspaces row appears in the satellite-tables section alongside the other 4 reserved seats.

Not applicable — NEW spec. No predecessor. Heritage substrate is documented in docs/specs/reserved-workspace-seats/{PRODUCT,TECH}.md (Phase 1 seat-shape ratification) and docs/plans/phase-0-investigation/architecture/04-workspace-types.md §3.2 + §4.2 (application_types vocabulary + upfront-seats decision).

Per docs/specs/core-docs-pathway-assessment/architecture-sub-doc-construction-guide.md §4.1 — three-tier status taxonomy.

DocDateStatusUseful for
docs/specs/reserved-workspace-seats/{PRODUCT,TECH}.md15/05/2026 (S240 NEW)[CURRENT-CANONICAL] for the Phase 1 seat-shape pattern.Direct substrate for T-1..T-5; this spec inherits all 8 RWS invariants (S-1..S-8 / T-1..T-8) via the SP S-1..S-6 + T-5 mapping (per Cross-doc cross-references table above).
docs/specs/rls-pattern/{PRODUCT,TECH}.md14/05/2026 (S239 NEW; applied DONE-S239)[CURRENT-CANONICAL] for the RLS + grants pattern.Substrate for T-2 (auto-trigger) + T-4 (grants helper).
docs/plans/phase-0-investigation/architecture/04-workspace-types.md §3.2 + §4.2 + §4.314-15/05/2026 (S239 + S240 ratifications)[CURRENT-CANONICAL] for the application_types vocabulary + upfront-seats decision + extensibility procedure.Substrate for S-1 naming + S-6 ALTER discipline + T-6 Phase 3 pathway.
docs/reference/project-plan.md §720/05/2026 (S243-interim — Item 13 revision)[CURRENT-CANONICAL] for the revised Phase 3 closure criteria.Substrate for S-7 (deferred-to-Phase-3 behaviours) + the closure-criteria framing.
docs/plans/phase-0-investigation/pre-s244-project-feedback.md Item 13 + Item 1520/05/2026 (S243-interim)[CURRENT-CANONICAL] for the NEW spec ratification + revised closure criteria.Substrate for the spec existing at all (Item 15) + the revised closure framing (Item 13).
docs/specs/procurement-workspaces/{PRODUCT,TECH}.md18/05/2026 (S242 NEW)[CURRENT-CANONICAL] for the per-application feature-spec pattern.Pattern precedent — the satellite-with-typed-columns shape this spec defers to Phase 3 follows the precedent procurement set at Phase 2.

End of TECH spec. Numbered invariants in ./PRODUCT.md.