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 atacc95792— 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
mainand break CI.
Decomposition rationale
Section titled “Decomposition rationale”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:
| Subtask | P-changes owned | Lands as | Merge unit |
|---|---|---|---|
| {102.5} | P9 (RC-2 better-errors) | standalone PR, ahead of the flag-day | own merge to main |
| {102.6} | P2, P3, P3b, P4, P5 (KH CLI seam edits) | authored on the flag-day branch; staged-ready | merged inside the flag-day commit |
| {102.7} | P7 (task-view server seam + vendored-schema flip + new tag) | task-view repo PR + tag cut | own 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 commit | the 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.
Why P2–P5+P3b is ONE Subtask, not five
Section titled “Why P2–P5+P3b is ONE Subtask, not five”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.
Validation of the {102.3} planner-recommended shape
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:
- 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}detailsand in the §Risk register. - Dependency direction confirmed, not reversed. {102.7} (task-view) needs {102.6}‘s
server-intent string contract (P5’s
subtask-deleteintent carriessubIdas 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.
Dependency graph
Section titled “Dependency graph”{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 / parallelisation guidance
Section titled “Wave / parallelisation guidance”- Wave A (parallel-safe, no inter-dep): {102.5} (P9, KH) ‖ {102.6} (P2–P5+P3b, KH).
Both touch
scripts/ledger-cli.tsbut 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} tomainfirst (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).
Risk register
Section titled “Risk register”| Risk | Impact | Mitigation (encoded in Subtask details) |
|---|---|---|
Orchestrator merges {102.6} to a green main alone → CI parity/round-trip tripped against the un-migrated number-typed ledger | HIGH | {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) | HIGH | P2+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()-seeded | HIGH | P3b 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 walker | HIGH | Task-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 shrink | MEDIUM | {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) | MEDIUM | P1’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 normaliser | MEDIUM | {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 surface | MEDIUM | {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. |
Open questions / escalations
Section titled “Open questions / escalations”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.