Roadmap-Backlog Consolidation + Roadmap Rethink (Linear-style) — PRODUCT.md
Roadmap-Backlog Consolidation + Roadmap Rethink (Linear-style) — PRODUCT.md
Section titled “Roadmap-Backlog Consolidation + Roadmap Rethink (Linear-style) — PRODUCT.md”Spec slug: roadmap-backlog-consolidation
Version: v1.0 (draft for ratification at S63)
Authored by: task-planner under write-product-spec for Subtask ID-30.2.
Status: PENDING-RATIFICATION (Liam to ratify; Checker promotes 30.2 → done).
Inputs: docs/research/roadmap-backlog-consolidation-and-roadmap-rethink.md (1648 lines), docs/reference/task-list.json Task ID-30 (description + 30.1 journal with all 8 S62 W3 ratifications).
Summary
Section titled “Summary”Three coupled surfaces are reshaped so Knowledge Hub’s product-management ledgers match user-intent Linear-style semantics rather than the inverted shape latent since S40. The work is scoped across four concerns, each of which yields its own invariant cluster below:
- Consolidated Backlog — the canonical prioritised work list. Today’s 54 items grow to 108 by absorbing 53 KEEP + PARTIAL Roadmap items per RESEARCH §3 migration (54 net-new entries minus 0 already-promoted), plus a new
rankfield that gives within-priority deterministic ordering. (T-OQ-1 reconciled at S67 W1a per Subtask 30.9 — original “55 → 109” reflected a stale count; live baseline measured 54.) - Shape A Roadmap — a thin strategic surface of ~10-12 capability themes with
now / next / laterhorizons, forward-linking to Tasks and Backlog. Replaces the 16-section / 61-item shape that 55/61 items mis-fit as tactical work. - Task-view (ID-20.14) extension — the per-task-mirror Backlog edit surface gains
rankediting and a bulk-reorder UX so the new field is operable. Promote affordance stays deferred to v1.1 per OQ-5. - Curator skill rewrites —
triage-findingBranch B/C semantics andupdate-roadmap-backlogCRUD modes both rewrite under Shape A so future intake routes to the right surface. Per OQ-8 ALT, this is a load-bearing, standalone Subtask in PLAN.md authored via dedicated sub-agent invoking/update-skill.
Net effect: one canonical prioritised work list (Backlog), one slowly-changing strategic surface (Roadmap), one canonical active-sequencing surface (task-list.json), all consistent with Linear / Aha! conventions. Schema, content, and skill changes ship in 3 sequential PRs per OQ-7.
OQ → invariant mapping
Section titled “OQ → invariant mapping”The 8 OQs ratified at S62 W3 (verbatim from task-list.json Subtask ID-30.1 <info added on 2026-05-21T21:15:00.000Z> journal) map one-to-one onto the invariants below. The Checker uses this table to verify spec compliance per-ratification.
| Ratification (S62 W3 verbatim) | Decision | Invariant(s) carrying it |
|---|---|---|
| OQ-1 Roadmap shape | RATIFIED-DEFAULT — Shape A (flat capability themes) | inv 6, 7, 8 |
| OQ-2 Time horizon enum | RATIFIED-DEFAULT — now / next / later (Aha! convention) | inv 7 |
| OQ-3 Theme cardinality | RATIFIED-DEFAULT — soft cap 12 (guidance, not schema-enforced) | inv 8 |
| OQ-4 Rank field location | RATIFIED-DEFAULT — Backlog only under Shape A | inv 3, 4 |
| OQ-5 Promote affordance in task-view | RATIFIED-DEFAULT — defer to v1.1 | inv 11 |
OQ-6 capability_theme back-link on TaskSchema | RATIFIED-DEFAULT — optional, not mandatory | inv 9 |
| OQ-7 Migration sequencing | RATIFIED-DEFAULT — 3 sequential PRs (PR-A schema/skills → PR-B bulk → PR-C reshape) | inv 12 |
| OQ-8 Curator skill amendments | RATIFIED-ALT — own dedicated Subtask via /update-skill sub-agent | inv 13 (load-bearing) |
Scope (in / out)
Section titled “Scope (in / out)”In scope (covered by invariants 1-13):
docs/reference/product-backlog.jsoncontent + schema changes (Backlog growth from 55 → 109 items + newrankfield).docs/reference/product-roadmap.jsonreshape to Shape A capability themes + schema extension (time_horizon,linked_tasks,linked_backlog).docs/reference/task-list.jsonschema extension (capability_themeoptional back-link).lib/validation/backlog-schema.ts+lib/validation/roadmap-schema.ts+ Task schema additions.- Per-task-mirror (ID-20) extension Subtask 20.14 —
rankinteger-input edit + bulk-reorder UX on Backlog index page. .claude/skills/triage-finding/SKILL.mdBranch B/C rewrite..claude/skills/update-roadmap-backlog/SKILL.mdCreate / Update / Delete / Promote mode rewrites under Shape A.- 3-PR migration sequencing per OQ-7.
Out of scope (explicitly):
- Promote affordance in task-view (deferred to v1.1 per OQ-5; inv 11 records the deferral).
- Roadmap MD-render pipeline rewrite — the
bun run roadmap:renderstep adapts but doesn’t restructure. - Cross-branch task-list ID coordination — that’s Task ID-31’s territory (canonical-pipeline → task-list migration); inv 12 only sequences the 3 PRs for this Task.
- Astro docs site rendering of Roadmap themes — covered by Task ID-9; this Task only owns the JSON surface.
- Re-ranking the existing 55 pre-migration Backlog items — rank defaults
null(inv 4); curator skill (per inv 13 rewrite) may rank items over time, but the migration does not force a back-fill.
Behavior
Section titled “Behavior”Cluster A — Consolidated Backlog (invariants 1-5)
Section titled “Cluster A — Consolidated Backlog (invariants 1-5)”-
Backlog as canonical prioritised work list.
docs/reference/product-backlog.jsonis the single canonical surface for active, scoped-but-not-yet-sequenced work items. Items appear on the Backlog when they are scoped enough to act on but not yet picked up ontotask-list.json. The Backlog’s audience is internal (Liam + agents); its purpose is to answer “what should we pick up next?” deterministically. After migration its item count grows from 55 to 109 (54 net-new entries IDs 87-140 per RESEARCH §3.2 minus 0 already-promoted; the migration adds 54 and removes 0 from the existing Backlog). -
Migration absorbs 53 KEEP + PARTIAL Roadmap items as net-new Backlog entries with full provenance. Every net-new Backlog entry (IDs 87-140) carries the canonical Phase-B schema shape:
id(bare-digit string),description,type(BacklogItemType enum: feature / research / tech_debt / infrastructure / testing / ux / documentation / bug),status(BacklogStatus enum: spec_needed / needs_research / parked / ready / blocked),effort_estimate(nullable string),priority(Priority enum),track(free-string domain label),dependencies(string array of other backlog IDs),session_refs(string array — at minimum["kh-prod-readiness-S62"]for migration provenance),commit_refs(string array),cross_doc_links(DocLinkSchema array),notes(nullable string; carries the"Migrated from roadmap §X.Y. ..."marker so the migration source is queryable). New optional fieldsdetails+testStrategymay be populated for promotion-ready items but are not required at migration time. No EXACT_DUPs are created — the RESEARCH §1 audit found 0 EXACT_DUPs; the 6 PARTIAL_OVERLAP clusters (RESEARCH §5.4) reconcile via thematic adjacency notes in thenotesfield rather than merges. -
rankis a new optional integer-or-null field onBacklogItemSchema. The field encodes within-priority ordering. Lower integer = higher rank within its priority tier. Default value at insert isnull. The field is nullable AND optional on the Zod schema — pre-existing items omit the field; new items may set it explicitly. There is no schema-level constraint that ranks within a priority tier are unique or contiguous; the curator skill (per inv 13 rewrite) maintains rank discipline but the schema does not enforce it. -
Sort order on the Backlog is priority-then-rank-then-id, with null rank sorting last within its tier. When two items share
priority, the one with a non-null lowerrankappears first; items withrank: nullappear after all ranked items in their tier, sorted byidas a stable tiebreaker. This deterministic sort applies wherever the Backlog is listed — the per-task-mirror Backlog index page (per inv 10), MD renderers, curator skill listings. The sort is implementable purely from the schema fields; no derived state lives outside the JSON. -
No item on the consolidated Backlog duplicates an in-flight or scheduled Task on
task-list.json. Per RESEARCH §5.2 the existing Backlog has 0 conflicts with the 16 active Tasks (ID-6 through ID-27); the migration must preserve this property. Before commit, the migration verifies that no net-new Backlog entry’sdescription,track, or scope semantically duplicates an existing Task entry. PARTIAL_OVERLAP cases per RESEARCH §5.4 (ingest-UI family ID-61 + ID-62 + ID-79 + ID-100; doc-triage ID-64 + ID-82; prompt-suggestion ID-49 + ID-91; portal-automation ID-53 + ID-101) are documented in each item’snotesfield as thematic adjacency rather than merged.
Cluster B — Shape A Roadmap (invariants 6-9)
Section titled “Cluster B — Shape A Roadmap (invariants 6-9)”-
The Roadmap is a thin strategic surface naming capability themes, not tactical work.
docs/reference/product-roadmap.jsonreshapes from 16 sections / 61 items to a flat list of ~10-12 capability theme entries. Sections disappear; the document’ssections[]array becomes a flatthemes[]array (or the section structure is preserved only as a single root with all themes inside — see TECH for the exact serialised shape). The Roadmap’s audience is Liam-as-product-owner; its purpose is to answer “what capabilities are we building this quarter / next quarter / eventually?” without enumerating individual Tasks or Backlog items. Per RESEARCH §4.4, no surveyed PM source — Linear, GitHub, Aha!, ProductPlan, Pichler, ProdPad — treats a roadmap as a place for hours-to-days tactical items, and the rethink resolves that mis-classification (55/61 items pre-rethink were tactical). -
Each capability theme carries
time_horizon ∈ {now, next, later}and forward-links to Tasks and Backlog items. Per OQ-2 ratification the time horizon enum is exactly the three valuesnow/next/later, matching Aha! best practice. No hard dates are stored — the surface is deliberately fuzzy on dates per Aha!‘s “loose time horizons over hard release dates and deadlines.” The required theme fields are:id(bare-digit string),title(short UK-English capability name),description(Markdown — why this capability matters; outcome shape),time_horizon(one ofnow/next/later),status(theme-level only; granular status lives on Tasks),linked_tasks(string array oftask-list.jsonTask IDs that deliver this capability),linked_backlog(string array of Backlog item IDs that contribute),session_refs,commit_refs,cross_doc_links(DocLinkSchema array),notes(free-text). Linked-id rendering: when a theme renders (e.g. in the per-task-mirror viewer or in a future Astro docs site page),linked_tasks[]andlinked_backlog[]resolve to inline links to the per-record pages; broken links (referenced ID not present) render with the same missing-target marker the per-task-mirror viewer uses for brokendependencies[](perdocs/specs/id-20-per-task-mirror/PRODUCT.mdinv 12). -
Theme cardinality has a soft cap of 12. Per OQ-3 ratification, the schema does NOT enforce a hard cap — themes are added freely — but the curator skill (per inv 13 rewrite) treats 12+ themes as a signal that two themes should merge (multi-month capabilities should still be coherent at the headline level). The cap is guidance, surfaced in the rewritten skill’s failure-modes-to-avoid section, not in
RoadmapSchema.superRefine. Re-evaluation cadence is quarterly per RESEARCH §4.5. -
capability_themeis an optional back-link field onTaskSchema. Per OQ-6 ratification the field iscapability_theme: string | null | undefined(optional and nullable on the Zod schema). When set, it names theidof the Roadmap theme this Task contributes to. When absent or null, the Task is unaffiliated — typical for cross-cutting hardening Tasks like ID-19 worktree isolation that don’t fit a product-capability narrative. Mandatory back-linking is rejected because it would force every Task to claim a theme even when the relationship is fuzzy. The forward-link from theme to Task (linked_tasks[]per inv 7) is the authoritative direction;capability_themeis a convenience back-link that the curator skill maintains in sync but is not the source of truth.
Cluster C — Task-view (ID-20.14) extension (invariants 10-11)
Section titled “Cluster C — Task-view (ID-20.14) extension (invariants 10-11)”-
The per-task-mirror Backlog index page exposes
rankas an editable integer field per item. Extendingdocs/specs/id-20-per-task-mirror/PRODUCT.mdinv 20-25 and inv 30: the Backlog per-item page surfacesrankin the frontmatter table next topriority. The field is editable in place via the same affordance pattern as other structured fields (inv 30) — click a small affordance, integer input replaces the rendered value, Save commits via the structured-patch path (inv 33),nullis selectable via an explicit “(unset)” option, schema validation rejects non-integer input server-side. The new field appears in the Backlog index page’s sort order per inv 4 above; the existing sort (track, thenstatus, thenidper per-task-mirror inv 20) becomespriority, thenrank(nulls last), thenidto match inv 4 on this surface specifically. Bulk-reorder UX: the Backlog index page exposes a drag-and-drop affordance that re-ranks all items in a given priority tier in a single multi-field patch transaction (the server applies the new ranks atomically; mid-flight failures roll back to the prior order). The drag handle is keyboard-operable (arrow keys move the focused row up/down within its tier; Enter commits) so the affordance meets WCAG 2.1 AA. -
Promote-to-task-list affordance remains explicitly out-of-scope for v1. Per OQ-5 ratification, the per-task-mirror Backlog index page does NOT expose a Promote button. The existing “Promotion-ready” badge (per-task-mirror inv 24) remains purely informational. Promote stays a curator-skill / orchestrator responsibility (
update-roadmap-backlogPromote mode) for v1. Re-evaluation is explicitly scheduled for v1.1 once the editor is in real use and the friction is measurable — the schedule is recorded here so the deferral is auditable, not silent. If v1.1 lands the affordance, this invariant updates; this PRODUCT.md edits in the same PR perwrite-product-spec“keep the spec current” guidance.
Cluster D — Curator skill rewrites (invariant 12-13)
Section titled “Cluster D — Curator skill rewrites (invariant 12-13)”-
The 3-PR migration sequencing is the contract for landing this work. Per OQ-7 ratification the three PRs execute strictly in order, with each PR fully merged and CI-green before the next opens:
- PR-A — Schema + skill foundation. Extends
lib/validation/roadmap-schema.tswithtime_horizon/linked_tasks/linked_backlog(or whatever serialised shape TECH ratifies). Addsranktolib/validation/backlog-schema.ts. Adds optionalcapability_themetoTaskSchema. Lands the rewritten.claude/skills/triage-finding/SKILL.mdand.claude/skills/update-roadmap-backlog/SKILL.md(per inv 13). Lands the per-task-mirror 20.14 extension (rank edit + drag-reorder per inv 10). PR-A is purely additive — no JSON content reshape — so existing roadmap.json and backlog.json continue validating against the extended schemas with all new fields optional / nullable. Acceptance gate: schema tests pass on the current pre-migration JSON content; curator skills load under their new shapes. - PR-B — Bulk content migration. Removes the 8 REMOVE_REDUNDANT roadmap items (3.2, 3.4, 3.6, 7.2, 7.6, 8.1, 11.17, and the rename-gated 11.2 partition). Adds the 54 net-new Backlog entries IDs 87-140 per RESEARCH §3.2 with full Phase-B provenance per inv 2. Roadmap structure unchanged in PR-B — still 16-section shape minus the 8 removals; the reshape happens in PR-C. Acceptance gate: Backlog item count goes from 54 to 108 (T-OQ-1 reconciled at S67 W1a — original “55 → 109” reflected a stale count; live baseline measured 54); Roadmap item count drops from 61 to 53; no schema errors; no item duplicates per inv 5; all migration
notesfields carry the canonical"Migrated from roadmap §X.Y. ..."marker. - PR-C — Roadmap reshape to Shape A. Reshapes
docs/reference/product-roadmap.jsonfrom the 16-section / 53-item shape into ~10-12 capability themes per inv 6, 7, 8. Section structure collapses; remaining roadmap items either (a) are subsumed into a parent theme asnotescontext, (b) migrate to the Backlog if they were in fact tactical (handled via additional KEEP items), or (c) drop because they’re already covered by an existing theme. Setscapability_themeback-links on Tasks per inv 9 where the relationship is clear. Acceptance gate: theme count ≤ 12; every theme has at least onelinked_tasks[]orlinked_backlog[]entry (no orphan themes); every theme has atime_horizonset. - PR boundaries are reviewable independently because each PR’s acceptance gate is verifiable in isolation. Cross-PR dependencies are encoded as PR-B blocked-on PR-A merge, PR-C blocked-on PR-B merge.
- PR-A — Schema + skill foundation. Extends
-
Curator skill rewrites are a load-bearing, standalone Subtask of PLAN.md, authored via
/update-skillsub-agent. Per OQ-8 RATIFIED-ALT, the rewrites of.claude/skills/triage-finding/SKILL.mdand.claude/skills/update-roadmap-backlog/SKILL.mdare NOT folded into a broader implementation Subtask. They get their own Subtask in PLAN.md, dispatched to a sub-agent that invokes the/update-skillskill directly. The rewrite covers, at minimum: (a)triage-findingBranch B rewrites to mean “capability theme promotion” — a finding that surfaces a new capability theme not already on the Roadmap. (b)triage-findingBranch C rewrites to mean “active work item promotion” — add to prioritised Backlog withpriority+rank. Branch A (in-Subtask scope) and Branch D (no-action) semantics are unchanged. (c)update-roadmap-backlogCreate mode for Roadmap targets the new Shape A field set (time_horizon,linked_tasks,linked_backlog); Create for Backlog supports the optionalrankfield. Update mode supportsrankedits andtime_horizonedits. Promote mode (backlog → task-list) gains an optionalcapability_themecopy-through from the source Backlog item’s parent theme (resolved via Roadmaplinked_backlog[]lookup) onto the destination Task. Delete mode unchanged. (d) Both skills’ failure-modes-to-avoid sections gain the new ratifications: triage-finding warns against routing a tactical item to Branch B (it belongs on Backlog); update-roadmap-backlog warns against creating a 13th theme without first checking whether two existing themes should merge per inv 8 soft cap. (e) Ordering: skill rewrites land in PR-A per inv 12 (foundation PR), BEFORE PR-B bulk migration uses the rewritten Create-mode contract to write the 54 net-new Backlog entries. The Subtask is sized for 1 sub-agent dispatch (~2-3h estimated) and is independently testable: the rewritten skills load, their decision-tree examples cover the new branch semantics, and a smoke test invokes Create against both ledgers under the new field sets without schema errors.
Cluster E — Cross-cutting hygiene (invariants 14-15)
Section titled “Cluster E — Cross-cutting hygiene (invariants 14-15)”-
UK English + Warm Meridian discipline applies throughout. All authored prose in PRODUCT.md, TECH.md, PLAN.md, and the rewritten skills uses UK English (
colour,organisation,behaviour, DD/MM/YYYY dates). All UI changes in the per-task-mirror 20.14 extension use semantic tokens perdocs/design/warm-meridian-implementation-spec.md; no raw Tailwind colour classes; the drag-handle affordance + the integer-input affordance meet WCAG 2.1 AA (keyboard-operable, never colour alone for meaning). The new “(unset)” rank option uses the same visual treatment as the “(unset)” Roadmap status / priority options in per-task-mirror inv 30 — consistency carries across surfaces. -
All migration writes pass through validated schemas; no silent failures. Per the project-wide silent-failure-prevention discipline (
docs/specs/silent-failure-prevention-spec.md): the migration script and the rewritten curator skills usesb()/tryQuery()from@/lib/supabase/safefor any Supabase interaction (though most migration writes are filesystem-only JSON edits). Composite responses usewarningsEnvelope(). After every migration write, the JSON is re-read and re-validated through the canonical Zod schema; a validation failure rolls back the write rather than committing partial state. The migration is idempotent: running PR-B’s migration script twice produces the same final JSON state as running it once (the script detects already-migrated items byidand skips them rather than duplicating). Idempotency is verified by a TECH-side test.
Open questions (P-OQ-1 through P-OQ-4) — for Liam ratification at S63
Section titled “Open questions (P-OQ-1 through P-OQ-4) — for Liam ratification at S63”These are NEW open questions raised during PRODUCT authoring (post-S62 W3 ratifications). Each carries a default proposal and an alternative. None block 30.3 TECH dispatch — defaults apply unless Liam ratifies otherwise.
P-OQ-1. Theme status enum values
Section titled “P-OQ-1. Theme status enum values”Question. Inv 7 specifies that each capability theme carries status (“theme-level only; granular status lives on Tasks”) but does not fix the enum. Which values does it accept?
Default proposal. Three values: pending (theme scoped, no linked work has started), in_progress (at least one linked Task is in_progress), done (all linked Tasks done; no remaining linked Backlog items). Derivable from linked_tasks[] + linked_backlog[] states, but stored explicitly so the curator skill can override (e.g. mark a theme done when the residual Backlog items get re-scoped to a different theme).
Alternative. Match task-list.json’s richer TaskListStatus enum (8 values including blocked, deferred, cancelled) so themes can carry the same operational state as Tasks. Rejected by default because theme-level state is intentionally coarse (per RESEARCH §4.5 Shape A “slowly-changing strategic surface”); finer-grained operational state belongs on the linked Tasks.
P-OQ-2. Serialised shape of the Roadmap document under Shape A
Section titled “P-OQ-2. Serialised shape of the Roadmap document under Shape A”Question. Today RoadmapSchema has sections: RoadmapSectionSchema[] with items nested inside. Under Shape A the section structure collapses. Does the document become (a) a flat themes: RoadmapThemeSchema[] array with sections dropped, OR (b) a single root section containing all themes as items (preserving the sections/items nesting for backwards-compat with the existing renderer)?
Default proposal. Option (a) — flat themes[] array; sections field dropped from RoadmapSchema. The MD renderer adapts to emit one heading-per-theme without section grouping. Cleaner schema; cleaner mental model; matches the RESEARCH §4.5 Shape A description (“flat list of themes”).
Alternative. Option (b) — single root section as a transitional shape that keeps sections intact, with sections.length === 1 and all themes as items inside. Lower MD-renderer change cost. Rejected by default because the residual sections field becomes confusing dead structure once the cardinality is fixed at 1.
P-OQ-3. Backlog rank collision policy
Section titled “P-OQ-3. Backlog rank collision policy”Question. Inv 3 says the schema does NOT enforce that ranks within a priority tier are unique. The curator skill (per inv 13 rewrite) maintains discipline. What does the curator do when adding an item with a rank value that collides with an existing item in the same priority tier?
Default proposal. The curator skill auto-shifts the existing items: inserting at rank: N in tier T pushes every item in tier T with rank ≥ N to rank: rank + 1. This is the simplest “insert into a sorted list” semantic. Recorded in the rewritten update-roadmap-backlog skill’s Update-mode section (per inv 13 c).
Alternative. Reject the write with a “rank collision” error and require the curator to pass an explicit shift policy. Higher ceremony; more predictable for batch operations.
P-OQ-4. capability_theme back-link consistency on Promote
Section titled “P-OQ-4. capability_theme back-link consistency on Promote”Question. Inv 13 (d) says Promote-mode gains an optional capability_theme copy-through from the source Backlog item’s parent theme onto the destination Task. The parent theme is resolved via Roadmap linked_backlog[] lookup — but a Backlog item may be linked from multiple themes. What does the curator do?
Default proposal. If exactly one Roadmap theme lists the source Backlog item in its linked_backlog[], set capability_theme on the destination Task to that theme’s id. If zero themes link the item, leave capability_theme unset (null / absent). If two or more themes link the item, leave capability_theme unset AND emit a warning so the curator (or Liam) sets it explicitly. Inv 13 (d) phrasing “optional copy-through” already permits this; default makes the ambiguity-handling concrete.
Alternative. Always set capability_theme to the first matching theme alphabetically. Rejected because it hides multi-theme membership signal.
Validation pointers (for TECH.md to plan against)
Section titled “Validation pointers (for TECH.md to plan against)”The Behavior invariants above are the source of truth. TECH.md (Subtask 30.3) authors a one-to-one mapping of proposed changes against each invariant. Validation hooks the TECH spec should plan against:
- Schema-roundtrip tests against
RoadmapSchemapost-Shape-A extension (covers inv 6, 7, 9 forward link shape). - Schema-roundtrip test against
BacklogSchemapost-rank-addition (covers inv 3, 4). - Backlog sort-order test that asserts inv 4 ordering against a fixture set.
- Migration-idempotency test per inv 15 — run the PR-B migration script twice, assert equal final state.
- Per-task-mirror E2E test for the rank-edit affordance (inv 10) — render the Backlog index page, click the integer-input affordance, save, re-render, assert the sort order updated.
- Skill smoke test per inv 13 (e) — invoke Create against both ledgers under the new field sets, assert no schema error and that Phase-B provenance fields round-trip cleanly.
- PR-by-PR acceptance-gate tests per inv 12 — each PR’s acceptance gate is independently verifiable.
Test philosophy: behaviour-verifying, not implementation-coupled per docs/reference/test-philosophy.md. The integration tests above assert observable schema, file, and UI behaviour; they do not lock in implementation choices.
End of PRODUCT.md. Awaits Liam ratification of P-OQ-1 through P-OQ-4. On ratification, Subtask 30.2 promotes to done (Checker action) and 30.3 TECH dispatch unblocks.