ID-147 {147.4} PLAN — Procurement UI foundation: decomposition of the remaining Lane B work into id-147 foundation + {145.19}-wave implementation subtasks
ID-147 {147.4} PLAN — Procurement UI foundation decomposition
Section titled “ID-147 {147.4} PLAN — Procurement UI foundation decomposition”Type: PLAN ({N.4}). Decomposition of the ratified
{147.2}PRODUCT (53 invariants §A1–§K3) +{147.3}TECH (§1–§7 proposed changes; DR-075 route ruling) into implementable subtasks. Fresh Planner context. Authored 16/07/2026. UK English throughout. Track:/procurement(ca-s435-procurement-baseline); ledger writes on MAIN only, nothing committed here.Substrate (read before this plan): ratified
PRODUCT.md,TECH.md,id-147-empirical-grounding.md; DR-063…068 + DR-075; the{145.19}record + journal.What this plan owns. The decomposition of the REMAINING Lane B work only — the id-147 foundation mechanics (
{147.5+}) plus the{145.19}UI half. It does not re-propose the route re-key API halves (groups A–C) that ALREADY LANDED in this branch (see §Current-state reconciliation). The parent applies FINAL Subtask numbering on MAIN; this plan RECOMMENDS the per-subtask landing (id-147 vs {145.19}-wave) and the wave structure.
Overview
Section titled “Overview”ID-147 establishes the Procurement UI foundation on Extend copy-in components + Streamdown, and
gates ID-145.19 (item-page UI). The API/route-re-key half (DR-075 groups A–C) is done; what
remains is (A) the id-147 foundation mechanics — Extend registry install, reference/evidence
attachment store, fill-slot geometry plumbing, bounding-box citation derivation, DOCX/XLSX editor +
forked e-signature, custom stepper + custom editors — and (B) the {145.19} UI half that
consumes that foundation — item-page hybrid frame, state-stepper wiring, question/coverage
surfaces, grouping lineage, drafting re-point, the content-renderer.tsx Streamdown migration, and
the dead workspace-era UI removal.
The decomposition is 21 subtasks across two landing groups (14 id-147 foundation + 7 {145.19}-wave), sequenced in 4 waves with a HARD STOP install gate at Wave 0.
Context — current-state reconciliation (LOAD-BEARING: do NOT re-propose landed work)
Section titled “Context — current-state reconciliation (LOAD-BEARING: do NOT re-propose landed work)”Verified live (16/07/2026): id-147 in_progress, id-145 in_progress, id-145.19
in_progress, id-145.18 done, id-145.17 done (DR-002 — statuses read from the ledger, not
from done-task journals).
Already landed in this branch (via the #119 merge) — OUT of this plan’s scope:
- Route groups A–C API halves (checker-gated PASS_WITH_NOTES):
c43031e8(group A+C):[id]/route.tsre-keyed toform_instancesflat reads;get_procurement_rollupRPC + child-forms list DROPPED (S470 no-stored-roll-up); PATCH/DELETE write the PK row directly;computeWorkflowTransitionextracted+consolidated (outcome delegates);outcome/integratecollapsed;forms/route.tsRETIRED 410.ec04e2db(group B):templates/route.tsRETIRED;[templateId]detail + field CRUD re-pathed to[id]/fields/*; completions download re-keyed + tender-documents/templates bucket fallback;get_template_summarydropped.f141b907(gate-groups-ac testfix): combined status+outcome PATCH fail-fast.- ⇒ The route/API re-key is DONE. This plan folds in only the UI-half collateral those retirements created (see §Collateral, below).
- W1 batch PUSHED to staging +
database.types.tsREGENERATED (be8cf6c6,1ef9757d). ⇒ TECH §7’s “sequence geometry + attachment migrations AFTER the W1 push + type-regen” precondition is MET. BUT (Lane B constraint): the geometry + attachment migrations are AUTHORED IN-BRANCH ONLY, NEVER pushed from this lane (another lane owns DB pushes). Every authored migration is flaggedmigration_authored= a parent-sequenced push.database.types.tsis Read-DENIED by the harness — subtasks cite migrations for shape, never the types file. {145.18}(item-page data/identity re-anchor) DONE;{145.17}(matches) DONE ⇒{145.19}data deps satisfied.
UI-half collateral (from the group-A/B retirements) folded into this plan (subtask W1):
components/procurement/procurement-forms-card.tsx (now fetches a 410 — gitnexus_context
confirms incoming:{}, no live callers, dead since {145.18}); components/coverage/template-upload.tsx
(POSTs the retired list route → 404; workspace-era “add another template” step with NO form-first
successor per BI-1/DR-068); page.tsx handleDownloadOriginal dead code + initial list-fetch
degradation.
Code-intelligence orientation (verbatim, 16/07/2026)
Section titled “Code-intelligence orientation (verbatim, 16/07/2026)”Outputs quoted (not paraphrased) so the Checker can verify the orientation step. Repo
disambiguated to /Users/liamj/Documents/development/procurement (two canonical repos are
registered).
gitnexus_context({name:'PdfDocument', file_path:'components/reader/pdf-document.tsx'})→Function:components/reader/pdf-document.tsx:PdfDocument (53-358);incoming.calls:[BinarySide (components/diff/binary-diff-pane.tsx)];outgoing.calls:[goToPage, Button, Input, Separator, Skeleton, createClient(lib/supabase/client.ts)];processes:[]. The §C/§D overlay host already ownsgoToPage(the “scroll-to-and-highlight” hook) and uses ourcomponents/uiprimitives.gitnexus_context({name:'computeWorkflowTransition'})→Function:app/api/procurement/[id]/route.ts:computeWorkflowTransition (77-134);incoming.calls:[outcome/route.ts:POST, [id]/route.ts:PATCH];outgoing.calls:[canTransition(lib/domains/procurement/procurement-workflow.ts), validateFormOutcome(lib/validation/schemas.ts)];processes:[]. Confirms the group-A/C consolidation LANDED — the §G3 stepper transition backend exists; the custom stepper (147-L) reads through it, it is NOT rebuilt.gitnexus_context({name:'normaliseInternalMdLinksForStreamdown'})→Function:lib/okf/prepare-streamdown-content.ts:normaliseInternalMdLinksForStreamdown (70-94);incoming.calls:[preparedContent(components/okf/file-render-pane.tsx)];outgoing.calls:[stripMdSuffix, resolveInternalMdLink(lib/okf/resolve-internal-link.ts)]. The §I3 hardening shim reuse target — confirmed present and used by the Streamdown pattern.gitnexus_query('content-renderer markdown streamdown migration heading id injection')→processes:[]; definitions:Function:components/item-detail/content-renderer.tsx:ContentRenderer (88-136),isMarkdown (90),Function:components/okf/file-render-pane.tsx:FileRenderPane (57-174).content-renderer.tsxis the only react-markdown site in scope (§I2 migrate target);FileRenderPaneis the Streamdown reuse pattern.gitnexus_query('analyse_form worker lane pdf orchestrator ExtractedField geometry PdfDetectedField')→ definitions:scripts/bid_worker.py:analyse_form_job (517-663)+_write_form_instance_fields (487-514);scripts/cocoindex_pipeline/form_extractors/orchestrator.py:_pdf_result_to_extracted_form (110-161)+extract_form_structure (164-239);scripts/cocoindex_pipeline/form_extractors/pdf.py:detect_pdf_fields (265-378)+PdfDetectedField (87-130). The geometry writer lane:pdf.pydetects,orchestrator.pyadapts,bid_worker.py:_write_form_instance_fieldspersists toform_instance_fields.gitnexus_context({name:'ProcurementFormsCard'})→Function:components/procurement/procurement-forms-card.tsx:ProcurementFormsCard (68-237);incoming:{}(no live callers);outgoing.calls:[useProcurementFormTypes, FormTypePicker, outcomeLabel, ProcurementWorkflowBadge, Button, Dialog*, formatDateUK, cn]. Confirms the dead workspace-era card is safe to remove (empty upstream blast radius).
Decomposition rationale + landing split
Section titled “Decomposition rationale + landing split”Per §K2 (this chain gates ID-145.19) and DR-075, the split follows one rule: id-147 ships foundation mechanics that ship whole; {145.19} ships the item-page wiring/consumption of them.
| Landing | Subtasks | Nature |
|---|---|---|
id-147 ({147.5+}) | A install gate · B vendor-in · C attachment migration · D attachment API+cleanup · E geometry pipeline · F geometry migration+writer · H overlay primitive+zod · I citation derivation · J editor wiring+fill · K e-sig fork · L custom stepper · M requirement editor · N question editor · O viewer/upload state contracts | Self-contained foundation mechanics (backend plumbing + reusable components) the item page consumes. |
| {145.19}-wave (ID-145 siblings) | W1 dead-code removal · W2 hybrid frame + Documents tab + attachments read-fold · W3 stepper wiring + inline states · W4 questions/coverage + zero-candidate manual-answer · W5 grouping lineage + drafting re-point · W6 §I2 content-renderer→Streamdown + snapshot · W7 fill-slot + citation overlay UI | Item-page surfaces that wire the foundation onto the {145.18} shape + DR-075 group-A detail. |
Why the group-A read edits (attachments read-fold, group-A DELETE storage remove()) land in
{145.19}, not id-147: they edit [id]/route.ts, a {145.19}-owned file (DR-075 group-A owner =
{145.19}); the id-147 attachment subtask (D) owns only the NEW [id]/attachments route + its DELETE
cleanup + the orphan-sweep backstop. This keeps file-ownership disjoint across the task boundary.
The sibling-only dependency decision (structural, parent must apply)
Section titled “The sibling-only dependency decision (structural, parent must apply)”The id-147 foundation → {145.19} consumption relationship is a task-level chain-gate (ID-145.19 depends on ID-147), already ratified (§K2, DR-075). It is NOT encoded as a subtask dependency edge — that would be a cross-task sibling violation. Instead:
- Every id-147 subtask’s
dependenciesreference only other id-147 local indices. - Every {145.19}-wave subtask’s
dependenciesreference only other {145.19}-wave local indices. - The cross-task gate (a {145.19}-wave UI subtask consuming an id-147 foundation subtask) is
expressed at the wave level (Wave 3 begins after the id-147 foundation waves complete), and
each consuming subtask’s
detailsnotes “task-gated on ID-147 foundation (not a subtask edge)”.
This is NOT a boundary escalation. The task boundary is correct and deliberate: ID-147 is the foundation, ID-145.19 the consumer. No proposed subtask needs a genuine cross-task subtask edge, so no split/merge is forced. (Considered and rejected the escalation per the sibling-only forcing function — the ratified chain-gate is the intended shape.)
Wave structure
Section titled “Wave structure”Wave 0 — STOP GATE + parallel non-Extend foundation.
- 147-A install gate (HARD STOP). Register the
@extend/*registry incomponents.json;shadcn addONE viewer; confirm@extend-ai/react-docx+@extend-ai/react-xlsxresolve at the pinned versionsshadcn addpulls; import-and-render smoke; record PRESENT/ABSENT/SIGNATURE_DRIFT with date + version. On ABSENT/DRIFT → STOP and escalate (do not proceed to 147-B). This is the deferred §Empirical check and the distinct early checkpoint everything Extend-dependent gates on. - Parallel non-Extend track (no Extend dependency — may begin immediately): 147-C (attachment migration), 147-E (geometry pipeline — Python), 147-L (custom stepper), 147-M/147-N (custom editors); {145.19}-wave 145W-1 (dead-code removal), 145W-6 (Streamdown content-renderer migration — Streamdown is already in-tree). These consume NO Extend source and are not gated on 147-A.
Wave 1 — Extend vendor-in + backend continue. 147-B (vendor-in the full v1 set + provenance manifest + vendor policy; deps A). Parallel: 147-D (attachment API + cleanup; deps C), 147-F (geometry migration + writer; deps E).
Wave 2 — Extend-consuming foundation. 147-H (overlay primitive + geometrySchema; deps B), 147-I (citation derivation B1+B2; deps B), 147-J (DOCX/XLSX editor wiring + fill; deps B), 147-K (E-Signature fork; deps B+C), 147-O (viewer/upload state contracts §B6/§B7/§E1/§E3/§E4; deps B). All parallel (disjoint files).
Wave 3 — {145.19}-wave item-page consumption (task-gated on id-147 foundation Waves 0–2). 145W-2 (hybrid frame; deps 145W-1) establishes the child-component structure; then 145W-3, 145W-4, 145W-5, 145W-7 run in parallel (deps 145W-2; each owns distinct child-component files). 145W-6 already ran in Wave 0.
Checkpoint after each wave: bun run test green on touched files; python3 -m pytest scripts/tests/ for the geometry lane; lint clean; no raw Tailwind colours; UK English; WCAG 2.1 AA
non-colour-only signalling on every state.
Parallelism + serialisation notes
Section titled “Parallelism + serialisation notes”app/procurement/[id]/page.tsxis a shared-file serialisation point across the item-page subtasks. 145W-1 (remove dead code) precedes 145W-2 (frame); 145W-2 establishes distinct child-component files so 145W-3/4/5/7 are parallel-safe thereafter.components.json+ the provenance manifest are owned solely by 147-A (registry + one viewer) then 147-B (the rest). 147-J/K touch only their own wiring/fork files, nevercomponents.json.- Python geometry lane (
pdf.py/orchestrator.pythenbid_worker.py+ migration) is a self-contained parallel track disjoint from all TS work. - Two authored-not-pushed migrations: 147-C (
<ts>_id147_form_attachments.sql) and 147-F (<ts>_id147_form_instance_fields_geometry.sql). Authored in-branch, flagged for a parent-sequenced push; NEVER pushed from Lane B.
Per-subtask briefs
Section titled “Per-subtask briefs”Each subtask’s full dispatch brief (file-ownership, TECH/invariant refs, code-intel discipline block,
three-part grounding block, testStrategy) lives in the proposed-subtasks YAML
(lane-b-proposed-subtasks.yaml). Summary:
id-147 foundation ({147.5+})
Section titled “id-147 foundation ({147.5+})”- 147-A — Extend registry install + HARD STOP import-and-render gate.
components.jsonregistriesblock;shadcn addone viewer; verify the two render engines resolve; record PRESENT/ABSENT/DRIFT; STOP on ABSENT/DRIFT. TECH §1(1-2); §K3. - 147-B — Vendor-in the v1 component set + provenance manifest + vendor policy.
shadcn addthe PDF/DOCX/XLSX/CSV viewers, Bounding Box Citations/HumanReviewPanel, File Upload + PDF Dropzone, Document Viewer Sidebar + File Thumbnail, DOCX/Excel Editor + E-Signature shells under a domain subdir (nevercomponents/root); re-point Hugeicons→lucide; re-wire shared chrome tocomponents/ui(viewers may keep Coss UI internals); install-as-is theming (white backgrounds accepted); write a checked-in provenance manifest (component name + registry version + add date). Finder NOT installed. TECH §1(3-4); §B1–B5/§A9/§E1/§J1–J3/§K3. - 147-C —
form_attachmentsmigration (authored-not-pushed). Table + scope-XOR CHECK +form_source_scopedCHECK + indexes + RLS mirroringengagement_groups(SELECT USING(true); writeget_user_role() IN admin,editor; no anon grant). TECH §2; §A5/A6/A7. - 147-D — Attachment write API + storage cleanup. NEW
POST /api/procurement/[id]/attachments(form-level;rolein body; engagement-level viaengagement_group_idbody) bindingonFilesAcceptedto the hardened BI-9 backend; storage undertender-documents/...; DELETE with best-effort storageremove(); periodic orphan-sweep backstop. NOT inproxy.tspublicRoutes;authFailureResponse(auth);sb()/tryQuery(); TanStack keys. TECH §2; §A6/A7/§E2. - 147-E — Fill-slot geometry pipeline (Python).
pdf.py: capturepage_width+ MediaBox originpage.rotation; normalise bbox into displayed post-rotation top-left page-fraction space; add ageometrydict toPdfDetectedField.orchestrator.py: carrygeometrythrough intoExtractedField(new nullable field); keep the DOCX/XLSXtable_index/row_indexreading-order path. TECH §3; §C1/C4.
- 147-F — Geometry migration + writer (authored-not-pushed migration).
<ts>_id147_form_instance_fields_geometry.sqladdsgeometry jsonbnullable;bid_worker.py:_write_form_instance_fieldspersistsgeometry. TECH §3; §C1/C3/C4. - 147-H — Spatial overlay primitive +
geometrySchemazod. Reusable component renderingHighlightArea(%-0–100 top-left,left/top/width/height × 100, no flip) boxes overPdfDocumentusing the vendored Bounding Box Citations +PdfDocument.goToPage+ a shared selection id + a text/icon label per box (never colour-only);geometrySchemazod (left/top/width/height ∈ [0,1],pageint ≥ 1,rotation ∈ {0,90,180,270}) — parse-fail degrades to §C4, never a box. TECH §3/§4; §C1–C4/§D1. - 147-I — Bounding-box citation coordinate derivation (B1+B2). B1 text-layer
getClientRectsprimary (resolvecited_text+cited_start/cited_end→ on-page rects against react-pdf TextLayer → HighlightArea, exact, deterministic); B2 Claude-vision fallback — MUST self-rasterise pages worker/client-side + pre-resize (absolute pixel coords ÷ resized dims → HighlightArea); §D3/§D5 degrade to the text-anchoreddocument-citations-panel.tsx, never a misplaced box. TECH §4; §D1–D5. - 147-J — DOCX/XLSX editor wiring + fill mechanism. Wire the vendored (experimental) DOCX/Excel
Editor as the edit affordance; §F2 “fill a missing answer” dispatches the EXISTING Claude-side
draft lane (SSE draft-stream,
form_responseswrite) — not a bespoke engine; manual edits persist via the editor save bound tostorage_path; §F4getAuthorisedClient(['admin','editor']); §F5 soft-error → read-only viewer fallback, never blank. TECH §5; §F1/F2/F4/F5. - 147-K — E-Signature fork. Fork Extend’s E-Signature block (only
file?:string): (a) drive signature-field placement from our data; (b)onSignedpersistence writing the signed PDF → aform_attachmentsrowrole='form_source'(needs 147-C); §F4 gating; §F5 fallback. TECH §5; §F3/F4/F5. - 147-L — Custom Warm Meridian workflow stepper. Custom component (lucide + semantic tokens) over
the 10-state
workflow_state; transition control offers onlycanTransition-valid next states (reads through the landedcomputeWorkflowTransitionbackend); invalid jump refused with a surfaced reason; state/deadline/outcome each carry a text label/icon (never colour-only). TECH §7; §G1–G4. - 147-M — Custom requirement-catalogue editor. Custom editor over
form_requirement_templatesdomain fields (requirement_type,requirement_text,matching_keywords,matching_guidance,is_mandatory,word_limit_guidance,section_ref,sector_applicability, domain/subtopic); Schema Builder NOT used; writes admin/editor-gated. TECH §7; §H1/H3/H4. - 147-N — Custom question/answer-slot editor. Custom editor over
form_questions/form_responses(question_text,word_limit,evaluation_weight, assignee,review_status, version); Schema Builder not used. TECH §7; §H2/H3/H4. - 147-O — Viewer + upload state contracts. Behaviour-first state layer over the vendored
viewer/upload shells (split out of 147-B to keep vendor-in pure + within budget): §B6 per-viewer
loading + soft-error-with-retry (never blank — DISTINCT from the item-page BI-19 states 145W-3
owns); §B7 unsupported/corrupt → “cannot preview” + download fallback; §E1 client
type/size/count/duplicate validation inline; §E3 uploading a form still creates the form-first item
(BI-9 regression guard); §E4 upload progress/success + the backend’s actual rejection reason. Owns
components/procurement/extend/viewer-states.tsx+upload-shell.tsx(the shells’ STATE layer, distinct from 147-B’s vendored source and 147-D’s route). TECH §1 (§B/§E→change map); §B6/B7/E1/E3/E4.
{145.19}-wave (ID-145 siblings; task-gated on id-147 foundation)
Section titled “{145.19}-wave (ID-145 siblings; task-gated on id-147 foundation)”- W1 — Dead workspace-era UI removal (collateral). Remove
procurement-forms-card.tsx(dead,incoming:{}); remove/replacetemplate-upload.tsx(POSTs the retired list route; no form-first successor per BI-1/DR-068); removepage.tsxhandleDownloadOriginaldead code + tidy the initial list-fetch degradation. NOT id-147-gated. Collateral item 4; BI-4. - W2 — Item-page hybrid frame + Documents tab + attachments read-fold. §A frame (form-as-page
header; engagement sibling rail read-only when grouped; Documents tab split FORM SOURCE /
REFERENCE-EVIDENCE; progressive disclosure; select-document-previews via vendored Sidebar +
Thumbnail). Fold the
form_attachmentsREAD into group-A GET ([id]/route.ts) split by role + group-A DELETE storageremove(). Establishes child-component structure. TECH §6 group-A; §A1–A9. - W3 — State-stepper wiring + inline empty/loading/error. Wire 147-L over the {145.18} shape via the context provider (BI-13/18/§G); inline empty/loading/error — the item-page BI-19 states, distinct from the per-viewer §B6 states owned by 147-O. {145.19}; §G1–G4 wired.
- W4 — Questions/coverage + mixed per-question honest states + zero-candidate manual-answer. Render questions/coverage/documents; mixed per-question states rendered HONESTLY (no all-or-nothing); zero-candidate question offers a manual-answer affordance → optional corpus promotion (BI-40, closes the BI-22 gap loop + BI-24 catalogue). {145.19}; §B6/BI-40.
- W5 — Grouping lineage + drafting re-point. Read-only sibling lineage where grouped (BI-28/29, no
roll-up, no engagement win-rate — S470); re-point the drafting stack (SSE draft-stream, response
editor, question navigator, citation panel) to the form id (BI-41, keys on route
[id]), stays secondary; §I4 streamed answers via Streamdown natively. {145.19}; §A3/A4/§I4. - W6 —
content-renderer.tsxreact-markdown → Streamdown migration + named snapshot. Preserve heading-id injection (Streamdowncomponentsprop asfile-render-pane.tsx), GFM parity, prose styling, plain-text fallback; reusenormaliseInternalMdLinksForStreamdown(§I3); a NAMED stored rendered-output snapshot fixture over the enumerated corpus (headings/links/lists/bold/GFM tables/blockquote/code) — fixture name fixed here:content-renderer-streamdown-parity.snapshot. NOT id-147-gated. TECH §7 §I2; §I1/I2/I3. - W7 — Fill-slot review UI + citation overlay UI. Wire 147-H + 147-I into the item-page fill-slot
review (§C: detected slots box-overlaid page-accurately; slot-list ↔ overlay linkage;
fill_statusper-slot label; PDF-only, DOCX/XLSX degrade to a list + note) and the citation panel (§D: bidirectional select; unresolvable → text-anchored entry; honest empty state). TECH §3/§4 UI; §C1–C4/§D1–D5.
Invariant coverage
Section titled “Invariant coverage”All 53 PRODUCT §-invariants are accounted for. Behaviour-specific invariants map to a single owning subtask with concrete acceptance criteria. The four viewer/upload behaviours TECH’s §B/§E→change map assigns to the install slice — §B6 (per-viewer loading + soft-error-with-retry), §B7 (unsupported → download fallback), §E3 (form-first-item BI-9 guard), §E4 (upload progress/success/honest reject) — plus §E1 (client validation) are owned by the new 147-O, split out of 147-B so vendor-in stays pure and within budget. §B6 is per-viewer (147-O) and is deliberately NOT the item-page BI-19 empty/loading/error (145W-3) — the two are distinct and separately owned.
Three invariants are cross-cutting / scope, deliberately not single-subtask-owned (matching the
checker-accepted baseline): §J4 (WCAG 2.1 AA + UK English on every surface) is enforced by the
WCAG 2.1 AA non-colour-only + UK English quality bar in every UI subtask’s testStrategy;
§K1 (Procurement-baseline-only scope) is a negative scope boundary honoured across the set (no
wider-platform surface designed); §K2 (this chain gates ID-145.19) is the task-level chain-gate
encoded at the wave level (the task_gated_on_id_147 fields), not a code change. Force-tagging these
onto one subtask would repeat the tag-not-own anti-pattern — they are enforced, not owned.
Risks and mitigations
Section titled “Risks and mitigations”| Risk | Mitigation |
|---|---|
| Extend registry ABSENT / SIGNATURE_DRIFT at install | 147-A is a HARD STOP gate at Wave 0 — no Extend wiring proceeds until PRESENT is recorded; ABSENT/DRIFT escalates. |
| Experimental DOCX/Excel Editor + E-Signature fail to initialise | §F5 degrades to the read-only viewer; the fork boundary (147-K) is budgeted explicitly, not a drop-in. |
| B2 vision coords unmappable (server-side raster) | 147-I MUST self-rasterise + pre-resize; B2 is fallback-only, spot-checkable; §D3 degrades to text, never a misplaced box. |
/Rotate 90 / non-zero MediaBox → misplaced box | 147-E normalises into displayed post-rotation top-left space + carries rotation; geometrySchema zod degrades malformed geometry to §C4 (147-H); rotated-page pytest fixture in 147-E/F. |
| Authored migrations pushed from Lane B by mistake | Both flagged migration_authored; authored in-branch only; parent sequences the push (another lane owns DB pushes). |
page.tsx edit conflicts across item-page subtasks | 145W-2 establishes distinct child-component files; 145W-1 precedes; 145W-3/4/5/7 own disjoint components. |
New [id]/attachments route silently public | 147-D asserts it is NOT in proxy.ts publicRoutes and routes failures via authFailureResponse(auth). |
Decision register
Section titled “Decision register”No new binding ruling surfaced during decomposition — DR-063…068 + DR-075 already settle the
adoption posture, geometry, editors, stepper, attachment store, and route re-key. The §I2 snapshot
fixture name (content-renderer-streamdown-parity.snapshot) is fixed in this plan per the S474
staff-review amendment; it is an implementation detail, not a DR. No DR-intent returned.
End of PLAN — ID-147 {147.4}. Output:
specs/id-147-extend-streamdown-ui-foundation/PLAN.md. Not committed here. Proposed subtask records
(TM-shape) for the parent to bulk-insert via bun scripts/ledger-cli.ts add-subtasks live in
lane-b-proposed-subtasks.yaml.