Skip to content

PLAN — Ledger id unification: decomposition of the staged P1–P9 implementation into Subtasks {102.5}–{102.8}

PLAN — Ledger id unification ({102.4} decomposition of {102.5}+)

Section titled “PLAN — Ledger id unification ({102.4} decomposition of {102.5}+)”
  • Task: ID-102 — Ledger id unification — all ids string (Tier-2), subtask number→string + deps types.
  • Subtask: {102.4} PLAN.
  • Session: S335 (authored 10/06/2026, FRESH Planner per Q-PLANNER-2 — NOT the {102.2} PRODUCT author plan-1022, NOT the {102.3} TECH author plan-1023).
  • Inputs (read in full): PRODUCT.md ({102.2}, ratified S335 — 16 invariants; D1 bare ids / D2 deps follow D1 / D3 RC-2 in scope / D4 gated-once / D5 fail-loud), TECH.md ({102.3}, Checker PASS at acc95792 — P1–P9 + P3b, flag-day atomic set, migration double-gated).
  • Scope: decompose the staged P1–P9 implementation into the minimum set of separable Subtask units, wire sibling-only dependencies, and surface the atomic-flag-day merge constraint so the Orchestrator does not merge a partial slice to a green main and break CI.

The shape is dictated by the flag-day atomic constraint (inv 10), not by file boundaries

Section titled “The shape is dictated by the flag-day atomic constraint (inv 10), not by file boundaries”

The TECH §Concurrency-gate is the load-bearing constraint on this decomposition. A naïve vertical-slice would break the ledger: the string-only schema (P1) rejects the not-yet-migrated number-typed live ledger, so P1 cannot land on main before the data migration (P6). The CLI seam edits (P2–P5, P3b) emit/accept string ids, so they too cannot be exercised green against the un-migrated ledger (the parity harness + type-strict record-set gate would trip). Therefore schema + data + mirrors + tag-bump land as one atomic flag-day commit (P1 + P6 + P8, consuming P7’s cut tag and P6’s seam edits). Only the RC-2 companion (P9) is genuinely landable ahead as a standalone PR.

This means the decomposition is shaped by what merges together, not by what files are touched. The four Subtasks are:

SubtaskP-changes ownedLands asMerge unit
{102.5}P9 (RC-2 better-errors)standalone PR, ahead of the flag-dayown merge to main
{102.6}P2, P3, P3b, P4, P5 (KH CLI seam edits)authored on the flag-day branch; staged-readymerged inside the flag-day commit
{102.7}P7 (task-view server seam + vendored-schema flip + new tag)task-view repo PR + tag cutown merge to task-view main; produces the tag {102.8} consumes
{102.8}P1, P6, P8 (schema flip + live migration + 4-site tag-bump + mirror regen)the atomic flag-day committhe flag-day merge (folds in {102.6}‘s seam edits)

Every TECH P-change is owned by exactly ONE Subtask (verified)

Section titled “Every TECH P-change is owned by exactly ONE Subtask (verified)”

P1→{102.8}, P2→{102.6}, P3→{102.6}, P3b→{102.6}, P4→{102.6}, P5→{102.6}, P6→{102.8}, P7→{102.7}, P8→{102.8}, P9→{102.5}. Ten P-changes, ten ownerships, no orphan, no overlap. The flag-day atomic set (P1 + P6 + P8, consuming P7’s tag-cut and P6’s seam edits) stays whole in {102.8} + the {102.6} fold-in — it is never split across two merges.

The five CLI seam edits are a single co-change family: P5’s record-set delta type-flip is type-strict-coupled to P2/P3/P3b (TECH §Risks — new Set(["15"]).delete(15) is a no-op; a numeric delta against a Set<string> throws a false record-set-violation). Splitting them across Subtasks would create a window where the CLI emits mixed string/number deltas and the round-trip canary fails. They share scripts/ledger-cli.ts, share the flag-day branch, and share one Executor. One Subtask, one coherent diff. (Note: P3b is the load-bearing follow-on of P3 — counter += 1 string-concatenates if nextId returns a string and the counter is not Number()-seeded; they MUST co-change.)

Why {102.5} (P9) is split out and lands first

Section titled “Why {102.5} (P9) is split out and lands first”

P9 is independent of the id contract (TECH §P9: “the helper is additive … survives the id unification unchanged”). It closes the concrete S334 datapoint (flip-subtask 90.26 in-progress returned a raw Zod issues array). Landing it ahead de-risks the flag-day by keeping the flag-day commit purely id-contract work and banks the RC-2 win regardless of when the migration gate opens. If effort pressure forces a cut, P9 is the safe defer (PRODUCT §D3) — splitting it out makes that deferral a no-op on the rest of the plan.

Section titled “Validation of the {102.3} planner-recommended shape”

The {102.4} brief carried the {102.3} planner’s recommended shape ({102.5} P9 → {102.6} seams → {102.7} task-view → {102.8} flag-day) with the instruction to validate, not rubber-stamp. Validated against TECH §Proposed-changes: the shape is sound and adopted, with two refinements made on merit:

  1. Shared-merge constraint surfaced. {102.6}‘s diff merges as part of the flag-day ({102.8}), NOT on its own green main — the recommended shape implied {102.6} as a stage-ahead Subtask but did not state the shared-merge constraint. Without it the Orchestrator could merge {102.6} alone and trip CI against the un-migrated ledger. Made explicit in {102.6} details and in the §Risk register.
  2. Dependency direction confirmed, not reversed. {102.7} (task-view) needs {102.6}‘s server-intent string contract (P5’s subtask-delete intent carries subId as a string — TECH §P7 scoped-serialise note), and consumes TECH §P1’s schema design (a spec input, fully in TECH §P1 — NOT a Subtask dep, since the P1 textual diff lives in {102.8}). So {102.7} depends on {102.6}, and {102.8} depends on both {102.6} and {102.7}. The recommended linear chain holds; the arrows are wired precisely below.

{102.5} P9 RC-2 companion ─────────────── (independent, lands first as standalone PR)
{102.6} P2-P5 + P3b CLI seam edits ──┐ (authored on flag-day branch; staged-ready)
├──► {102.7} P7 task-view server + schema + tag
│ (needs {102.6} server-intent string contract;
│ consumes TECH §P1 schema design; produces NEW TAG)
└──┐
{102.7} ─────────────────────────────────┤
{102.8} P1 schema + P6 migration + P8 tag-bump + mirror regen
(THE ATOMIC FLAG-DAY COMMIT; folds in {102.6} seam edits;
bumps TASK_VIEW_TAG to {102.7}'s tag)
TASK-LEVEL gate (NOT a subtask dep): Task 102 dependencies = ["90"] — ID-90 P3 retirement
({90.22} in_progress + {90.23} pending) merged AND cutover soak declared settled, before
{102.8}'s migration (P6) runs. Verified by the Orchestrator at {102.8} dispatch, not encoded
as a subtask dependency (sibling-only constraint forbids a cross-Task subtask dep).

Sibling-only check: {102.6} deps = []; {102.7} deps = [6]; {102.8} deps = [6, 7]; {102.5} deps = []. Every dependency is a sibling within Task 102. No cross-Task subtask dependency is expressed — the ID-90 P3 + soak gate is the Task-level dependencies: ["90"] already on Task 102, re-verified at {102.8} dispatch.


  • Wave A (parallel-safe, no inter-dep): {102.5} (P9, KH) ‖ {102.6} (P2–P5+P3b, KH). Both touch scripts/ledger-cli.ts but at disjoint line ranges (P9 at the schema-error emission sites L2280/L3541/L3619/L3963 + load-time L1301; the seam edits at L714–728, L2480–2545, L2860–2972, L3148–3204, L3660–3750). If dispatched to parallel worktrees, cherry-pick order is {102.5} then {102.6} (or rebase {102.6} onto the merged {102.5}); a trivial merge-conflict at the file level is possible but the hunks do not overlap. Recommendation: land {102.5} to main first (own PR), then author {102.6} on the flag-day branch off the post-{102.5} HEAD — cleanest, no cherry-pick gymnastics.
  • Wave B (after {102.6} seam contract is fixed): {102.7} (task-view upstream, separate repo /Users/liamj/Documents/development/task-view). Runs in the task-view repo, not a KH worktree. Produces the new tag.
  • Wave C (the flag-day — GATED, Liam-execution-gated): {102.8}. Requires Wave A ({102.6}) + Wave B ({102.7}) complete, AND the Task-level ID-90 P3 + soak gate satisfied. This is the single atomic commit.

Not parallelisable: {102.7} cannot start its server-intent mirroring until {102.6} fixes the subtask-delete subId-string contract; {102.8} cannot run the migration until both the P3 retirement has landed and the soak is declared settled (TECH §Concurrency gate preconditions 1 + 2).


RiskImpactMitigation (encoded in Subtask details)
Orchestrator merges {102.6} to a green main alone → CI parity/round-trip tripped against the un-migrated number-typed ledgerHIGH{102.6} details states the diff is flag-day-only — authored on the flag-day branch, merged inside {102.8}‘s atomic commit, never on its own main. §Decomposition-rationale “shared-merge”.
Record-set-gate false record-set-violation from a partial seam landing (string delta vs Set<number>, or vice versa)HIGHP2+P3+P3b+P5 land atomically inside {102.6}‘s single diff (never split); the add→delete round-trip test is the canary (TECH §Testing inv 8/9).
nextId string-concat regression in the bulk path ('5' + 1 === '51') if P3b’s counter is not Number()-seededHIGHP3b is co-owned with P3 in {102.6}; both canaries are mandatory in {102.6} testStrategy (TECH §Testing inv 8 bulk): ["5"]→bulk-of-3→"6","7","8" (no-concat) and the gap fixture ["2","10"]→bulk-of-1→"11" (max+1 numeric, not "3").
{102.8} migration runs before ID-90 P3 retirement + soak settles → partial re-numification by the KH-local fallback walkerHIGHTask-level gate dependencies:["90"]; {102.8} details mandates re-verifying {90.22}/{90.23} merged + soak declared settled at dispatch, and re-grepping the post-P3 ledger-cli surface (line numbers in TECH are pre-P3).
TASK_VIEW_TAG bump misses a site (4 sites: drift.yml:86,:214; ci.yml:1139,:1177) or a site moved after {90.22}‘s drift-workflow shrinkMEDIUM{102.8} details carries all 4 sites AND mandates a fresh grep -rn TASK_VIEW_TAG at flag-day HEAD (TECH §P8 {90.22} interaction).
"0" admitted by the bare /^\d+$/ regex (inv 3 violation)MEDIUMP1’s .refine((s) => Number(s) > 0) pairing (TECH §Verification finding); {102.8} testStrategy includes the "0"-reject assertion.
task-view re-vendor drift: vendored schema not byte-faithful to P1 under the drift normaliserMEDIUM{102.7} details mandates running task-view-vendor-drift.yml’s normalise() locally on both copies, expecting empty diff, before cutting the tag.
{102.8} details line numbers stale vs post-P3 surfaceMEDIUM{102.8} re-derives the seam at dispatch via gitnexus_context + grep; TECH line numbers are explicitly pre-P3 ground truth, not dispatch-time truth.

None blocking. The decomposition fits within the §3.4 25-Subtask soft ceiling (4 new Subtasks, 8 total on Task 102). No cross-Task subtask dependency surfaced — the ID-90 P3 + soak gate is correctly a Task-level dependency. No Task-boundary split/merge required.

Liam-execution gate (carried forward, not a PLAN blocker): Tier-2 migration execution ({102.8}‘s P6 run) stays Liam-gated regardless of PLAN ratification — it is a Tier-2 migration touching the live canonical ledger. PLAN ratification authorises the staging work ({102.5}, {102.6}, {102.7}) and the authoring of {102.8}; the flag-day commit remains Liam-gated behind the ID-90 P3 + soak preconditions.