feat(prd): one transitional PRD authority — PrdService, mission linkage, labeled export, conflict-aware import (RI-4-001, #1275) #1294

Merged
jarvis merged 5 commits from feat/ri-050-prd-authority into next 2026-08-18 05:56:52 +00:00
Collaborator

Card RI-4-001 — One transitional PRD authority (RI-N3, SDLC-D-036, #1275)

Worker card for the alpha 0.0.50 release-integrity workstream, executed by a pi zai/glm-5.3:high worker under fargo, independently verified (gates re-run, sabotage control reproduced) by the dispatching seat before push.

What this establishes

@mosaicstack/prdy structured storage under docs/prdy/ is the single PRD authority for the alpha. A new PrdService owns create/get/list/update/link/import/export; both mosaic prdy and mosaic mission --plan route through it as thin adapters; the raw writers (createPrd/savePrd) are no longer exported — no second writer path.

Service surface (exported from @mosaicstack/prdy)

create (v1, draft) · get/list · update (bumps content version) · linkMission/listMissionLinks · planForMission (create+link, one write) · exportMarkdown · importDocument · acceptSuccessor. Typed errors: PrdNotFoundError, PrdUpdateError, PrdImportInvalidError (structural refusal, nothing written), PrdImportConflictError (refusal + proposed successor v+1, original bytes untouched). PRD_GENERATED_VIEW_LABEL = generated view — do not edit.

Design decisions

  • Linkage lives on the PRD document itself (missions: PrdMissionLinkage[] in the YAML authority) — survives restart by construction, no sidecar-sync failure mode. PrdMissionLinkage = { missionId, missionVersion, prdVersion, requirementIds, linkedAt }.
  • Markdown is a labeled generated view: export header carries the label + prd-id/version/generated-at + source path. No code path reads the exported Markdown back (grep-level + behavioral + negative-control: mutating the export leaves service-read authority deep-equal unchanged).
  • Import is explicit, validated, conflict-aware: zod validation before anything; valid imports persist as draft (validity ≠ approval); identical content → identical no-op; divergent same-id → typed conflict with proposed successor — never overwrites, never merges; successor persists only under explicit acceptSuccessor/--accept-successor.
  • docs/PRD.md untouched and never read in code — not a peer authority.
  • Schema v2 is backward-compatible (missions defaults empty; older docs parse).

Tests — contract per AC-RI-3

packages/prdy: 0 → 20 passed (linkage round-trip incl. fresh-process read-back, export labeling, markdown-mutation negative control, import valid/identical/conflict/invalid). packages/mosaic: 1548 → 1557 passed (6 prdy-adapter + 3 mission-linkage adapter specs; all 1548 pre-existing green).

Sabotage controls — worker's (whole persistence block removed): prdy 3 failed / mosaic 2 failed, all linkage tests, zero pre-existing failures; restored byte-identically, green. Dispatching seat's independent control (update-in-place arm only): prdy linkage test reddens (1/20), restore sha-verified green. Coverage note from the narrower control: fresh-linkage is covered at both package levels; update-in-place coverage is prdy-side.

Gates (re-run by the dispatching seat, rc-honest)

packages/prdy build/lint/vitest 20/20 rc=0 · packages/mosaic build/lint/vitest 1557/1557 rc=0 · root build 25/25 rc=0 · root typecheck 45/45 rc=0. Scope: packages/prdy/**, packages/mosaic/src/commands/{prdy,mission}*.ts + specs, plus a card scratchpad (scratchpads/ri-4-001-prd-authority.md, repo card convention, disclosed).

Known notes / non-goals

  • requirementIds is [] at the mission --plan command — no requirement-selection UI yet; the schema carries ids when a caller has them.
  • Mission version marker is mission.updatedAt (gateway MissionInfo has no numeric version — noted in code).
  • Import accepts YAML only; Markdown→YAML reconstruction is deliberately absent (it would violate the read-back invariant).
  • No version history per PRD (single current version) — future card if required.
## Card RI-4-001 — One transitional PRD authority (RI-N3, SDLC-D-036, #1275) Worker card for the alpha 0.0.50 release-integrity workstream, executed by a pi `zai/glm-5.3:high` worker under `fargo`, independently verified (gates re-run, sabotage control reproduced) by the dispatching seat before push. ## What this establishes `@mosaicstack/prdy` structured storage under `docs/prdy/` is the single PRD authority for the alpha. A new `PrdService` owns create/get/list/update/link/import/export; **both** `mosaic prdy` and `mosaic mission --plan` route through it as thin adapters; the raw writers (`createPrd`/`savePrd`) are no longer exported — no second writer path. ## Service surface (exported from `@mosaicstack/prdy`) `create` (v1, draft) · `get`/`list` · `update` (bumps content `version`) · `linkMission`/`listMissionLinks` · `planForMission` (create+link, one write) · `exportMarkdown` · `importDocument` · `acceptSuccessor`. Typed errors: `PrdNotFoundError`, `PrdUpdateError`, `PrdImportInvalidError` (structural refusal, nothing written), `PrdImportConflictError` (refusal + proposed successor v+1, original bytes untouched). `PRD_GENERATED_VIEW_LABEL` = `generated view — do not edit`. ## Design decisions - **Linkage lives on the PRD document itself** (`missions: PrdMissionLinkage[]` in the YAML authority) — survives restart by construction, no sidecar-sync failure mode. `PrdMissionLinkage = { missionId, missionVersion, prdVersion, requirementIds, linkedAt }`. - **Markdown is a labeled generated view**: export header carries the label + prd-id/version/generated-at + source path. No code path reads the exported Markdown back (grep-level + behavioral + negative-control: mutating the export leaves service-read authority deep-equal unchanged). - **Import is explicit, validated, conflict-aware**: zod validation before anything; valid imports persist as `draft` (validity ≠ approval); identical content → `identical` no-op; divergent same-id → typed conflict with proposed successor — never overwrites, never merges; successor persists only under explicit `acceptSuccessor`/`--accept-successor`. - `docs/PRD.md` untouched and never read in code — not a peer authority. - Schema v2 is backward-compatible (`missions` defaults empty; older docs parse). ## Tests — contract per AC-RI-3 `packages/prdy`: 0 → **20 passed** (linkage round-trip incl. fresh-process read-back, export labeling, markdown-mutation negative control, import valid/identical/conflict/invalid). `packages/mosaic`: 1548 → **1557 passed** (6 prdy-adapter + 3 mission-linkage adapter specs; all 1548 pre-existing green). **Sabotage controls** — worker's (whole persistence block removed): prdy 3 failed / mosaic 2 failed, all linkage tests, zero pre-existing failures; restored byte-identically, green. Dispatching seat's independent control (update-in-place arm only): prdy linkage test reddens (1/20), restore sha-verified green. Coverage note from the narrower control: fresh-linkage is covered at both package levels; update-in-place coverage is prdy-side. ## Gates (re-run by the dispatching seat, rc-honest) `packages/prdy` build/lint/vitest 20/20 rc=0 · `packages/mosaic` build/lint/vitest 1557/1557 rc=0 · root build **25/25** rc=0 · root typecheck **45/45** rc=0. Scope: `packages/prdy/**`, `packages/mosaic/src/commands/{prdy,mission}*.ts` + specs, plus a card scratchpad (`scratchpads/ri-4-001-prd-authority.md`, repo card convention, disclosed). ## Known notes / non-goals - `requirementIds` is `[]` at the `mission --plan` command — no requirement-selection UI yet; the schema carries ids when a caller has them. - Mission version marker is `mission.updatedAt` (gateway `MissionInfo` has no numeric version — noted in code). - Import accepts YAML only; Markdown→YAML reconstruction is deliberately absent (it would violate the read-back invariant). - No version history per PRD (single current version) — future card if required.
fargo added 5 commits 2026-08-17 21:52:16 +00:00
- PrdService owns create/read/update/link/import/export; wizard and package
  CLI become prompt layers over the service (no second writer path)
- PRD documents gain a content version and a missions linkage array persisted
  in the YAML authority store (survives restart); linkage writes do not bump
  the content version
- exportMarkdown renders a labeled generated view (id + version + do-not-edit
  header) that no code path reads back; the store loads .yaml/.yml only
- importDocument validates structure with zod before anything else, persists
  valid imports as draft (validity is not approval), and refuses conflicts
  with a typed PrdImportConflictError carrying a proposed successor; the
  original document stays byte-identical until acceptSuccessor
- raw store writers (createPrd/savePrd) are no longer exported from the
  package entry point
- mosaic prdy becomes a thin adapter: --init/--update (wizard for TTY,
  service otherwise), --import with --accept-successor, --export
- mosaic mission --plan persists the mission↔PRD linkage (mission id +
  version marker, PRD id + version) through the service instead of invoking
  the wizard as a second writer; non-TTY runs create non-interactively
- import paths: created/draft, typed refusal creates nothing, conflict
  refusal leaves bytes untouched, --accept-successor persists v+1
- export writes the labeled generated view; authority unchanged
- mission --plan persists linkage readable by a fresh service instance;
  mission path and prdy path share the docs/prdy store with stable
  ids/versions
jarvis approved these changes 2026-08-18 01:18:08 +00:00
jarvis left a comment
Contributor

RI-4-001 independent review (jarvis, cross-seat per handoff protocol; producer was a pi worker under fargo).

Verified against head 8d258e1d: PrdService is the single authority surface — raw writers createPrd/savePrd deliberately unexported from @mosaicstack/prdy (no second writer path); both mosaic prdy and mission --plan route through it as thin adapters; planForMission performs create + mission<->PRD linkage in one authority write, linkage persisted in the PRD document (survives restart), mission.updatedAt as version marker documented; Markdown export carries the PRD_GENERATED_VIEW_LABEL header; import is conflict-aware (PrdImportConflictError carrying existing vs proposal). Executed locally: prdy 20/20, mosaic prdy.spec + mission-prd.spec 9/9 (after building prdy dist; the 19 unrelated failing suites in a bare minimal install are the known workspace build-order artifact, not this change — same class fargo documented).

RI-4-001 independent review (jarvis, cross-seat per handoff protocol; producer was a pi worker under fargo). Verified against head 8d258e1d: PrdService is the single authority surface — raw writers createPrd/savePrd deliberately unexported from @mosaicstack/prdy (no second writer path); both mosaic prdy and mission --plan route through it as thin adapters; planForMission performs create + mission<->PRD linkage in one authority write, linkage persisted in the PRD document (survives restart), mission.updatedAt as version marker documented; Markdown export carries the PRD_GENERATED_VIEW_LABEL header; import is conflict-aware (PrdImportConflictError carrying existing vs proposal). Executed locally: prdy 20/20, mosaic prdy.spec + mission-prd.spec 9/9 (after building prdy dist; the 19 unrelated failing suites in a bare minimal install are the known workspace build-order artifact, not this change — same class fargo documented).
jarvis merged commit d92de53399 into next 2026-08-18 05:56:52 +00:00
Sign in to join this conversation.