akm docs

0006 — Task source version routing, from three generations to one

Context

Between P2a and P4, src/tasks/source/parse-task-source.ts was the one place every task document's version: field got routed to a parser. Through that window there were three live generations to route between: task v2 (retired at the router itself, with a dedicated unsupported-version error), task v3 (the shipped grammar every existing task file on disk still used), and task source v4 (the new grammar P2a introduced). The router had to dispatch to the right parser without a second parse, a re-serialization, or a synthetic document — an invariant P1b §4.3 established for the front end and every later phase carried forward unchanged.

Two things about that routing were the module's own, deliberately preserved warts, recorded as such at the time rather than fixed:

  1. The bounded YAML front end's pre-version failures (source not a string, source too large, YAML parse/warning/expansion) render with a fixed label — "task v3 source" — even when the document under parse turns out to declare version: 4, because root.version cannot be read until the front end has already succeeded or failed. P2a's spec explicitly deferred fixing this: "P4 owns the final label once v3 is gone."
  2. For any version: other than 2, 3, or 4, the router fell through to parseTaskV3Document's OWN wording — "version is required and must be exactly 3." — a message that names the wrong number for a document that was never v3 in the first place. This was also deliberately deferred: "P4 owns the final version-error text."

Decision

Moved verbatim from src/tasks/source/parse-task-source.ts's module header, as it read from P2a through the end of P3b (immediately before P4 §3.2 rewrote it):

The task source version router (spec docs/plans/specs/p2a-task-source-v4.md §3.4, §1.5 D2-N2).

Runs the bounded YAML front end ONCE (readBoundedTaskSourceYaml), reads root.version, and dispatches into parseTaskV3Document (unmodified, exported from ../source-v3) or parseTaskSourceV4Document with that SAME {root, lineAt} — no second parse, no re-serialization, no synthetic document (the P1b §4.3 invariant this phase carries forward).

D2-N2's exact routing table:

root version routed to observable result
4 parseTaskSourceV4Document new grammar
3 parseTaskV3Document byte-identical to today
2 parseTaskV3Document byte-identical (raises taskV2UnsupportedError itself)
absent/other parseTaskV3Document byte-identical (its own preserved wording)

Only version: 4 needs an explicit branch: parseTaskV3Document already raises taskV2UnsupportedError for version: 2 and its own "version is required.../must be exactly 3" wording for everything else (source-v3.ts:720-722) — a DELIBERATELY preserved wart, not fixed here (P4 owns the final version-error text).

The front end's own pre-version failures (source not a string, source too large, YAML parse/warning/expansion) ALWAYS render with the "task v3 source" label, even when the document later declares version: 4 — root.version cannot be read until the front end already succeeded. This is a deliberate, spec-recorded wart (§3.4): "P4 owns the final label once v3 is gone."

P4 §3.2 (docs/plans/specs/p4-deletions-closeout.md) removed task source v3 acceptance from src entirely (§3 family A2) and, in the same commit, rewrote the router to close both warts. The terminal routing table, in place since:

root version outcome
4 parseTaskSourceV4Document — the new grammar (row B-13)
any other number TASK_SCHEMA_VERSION_UNSUPPORTED, naming the migrator (rows B-14/B-15)
absent / not a number parseTaskSourceV4Document's own TASK_SOURCE_INVALID "version is required and must be 4" / "must be exactly 4" wording (row B-16)

A missing or non-numeric version: still routes into the v4 parser rather than the generic unsupported-version rejection — that part of the shape is unchanged from the original design — because a document with no readable version was never task v2 or v3 in the first place, and the v4 parser's own field error names the one grammar src still accepts.

Consequences

Provenance