Skip to content

PRODUCT — Task-list field discipline (ID-34.2)

PRODUCT — Task-list field discipline (ID-34.2)

Section titled “PRODUCT — Task-list field discipline (ID-34.2)”

Task: ID-34 — Task-list description discipline + content audit. This Subtask: {34.2} PRODUCT — per-field discipline invariants + retroactive-sweep scope. Predecessor: RESEARCH.md (ratified data + Zod-encodable-vs-doc-only split).

This spec defines the observable behaviour of the field discipline: what shape of content each ledger field carries, which limits are machine-checked (soft warnings) vs human-convention, and the policy for the one retroactive sweep. Each invariant is numbered and testable so {34.3} TECH maps a Proposed change to each, and the Checker gates against them.


In scope: the per-field write discipline for the three workflow ledgers’ human-writable text fields, with task-list.json as the primary surface; a soft-warning mechanism for the machine-checkable subset; the canonical discipline doc; amendments to the four spec/planning skills; and the policy (not the execution) for one retroactive sweep.

Out of scope: hard schema rejection of over-length fields (RESEARCH §4.1 — would reject the live ledger); the ID-35 CLI itself (consumes this discipline, specified separately); the ID-22 ledger-path relocation (independent per crossover audit §4); executing the sweep across all 40 Tasks (decomposed as the lowest-priority {34.5+} Subtask, parent-sequenced).


§2 Per-field discipline (the canonical table)

Section titled “§2 Per-field discipline (the canonical table)”

The discipline assigns each field a shape, a budget (advisory char limit where meaningful), and a load-bearing-for purpose. This table is the artefact {34.3} TECH publishes as the canonical doc.

FieldShapeBudget (advisory)Load-bearing forLimit class
Task.titleShort noun phrase~30–60 charsHuman scan + cross-ref labeldoc-only
Task.descriptionOne paragraph: compact what + why. No design rationale, no migration narrative, no REJECTED-alternatives prose — those go to docs/specs/… + a cross_doc_links pointer. Updated only on scope amendment.≤ 1500 charsCross-doc cross-reference targetsoft-warn
Task.status_noteShort rationale for the current status (blocked: waiting on X; S267: prioritised for …). Acute carryover only. Bump on status flip.≤ 300 charsStatus-line contextsoft-warn
Task.priority_noteOne-line rationale for the MoSCoW/Ranked priority.~≤ 300 charsPriority justificationdoc-only
Subtask.titleShort noun phrase~40–80 charsDispatch labeldoc-only
Subtask.descriptionOne sentence summarising the Subtask. Not a copy of details.≤ 250 charsSubtask scan labelsoft-warn
Subtask.detailsMulti-line markdown: the dispatch-brief slice and the append-only <info added on …> journal (commits, test counts, OQ ratifications, Checker verdicts, Curator decisions). THE canonical home for session-by-session narrative.no limitPer-Subtask traceability + journalnone
Subtask.testStrategyOne-line acceptance criterion the Checker verifies against. Not a multi-criterion test plan.≤ 300 chars, single assertionChecker contractsoft-warn
cross_doc_links[]{path, anchor, raw} per DocLinkSchema. Repo-relative path; pointer to canonical reference.Doc-graph traversalalready Zod-enforced
Task.session_refs[] / commit_refs[]Bare session ids / short commit SHAs.Provenancedoc-only
document_purpose (root)One-paragraph purpose of the ledger.Doc headerdoc-only

Note on details: length is intentionally uncapped. The discipline for details is structural (invariant 7), not length-based — capping the journal would defeat its purpose as the catch-all traceability home.


  1. The canonical discipline doc exists and is authoritative. A single doc (location per {34.3} TECH; default docs/reference/task-list-discipline.md) publishes the §2 table. Any author/Planner/Checker resolving “which field carries this content” consults it. Acceptance: the doc exists, contains the full per-field table, and is linked from the documentation inventory.

  2. Task.description carries compact what+why, not canonical rationale. Design rationale, migration option trade-offs, and REJECTED-alternatives prose belong in docs/specs/<feature>/{PRODUCT,TECH}.md (or docs/research/), referenced from description via a cross_doc_links[] entry — never inlined. Acceptance: the discipline doc states this rule with the “would-it-survive-the-task” test (RESEARCH §3); the four spec/planning skills cite it (inv 9).

  3. Task.status_note carries acute current-status context only. Session-by-session narrative does not accumulate here; it goes to the relevant Subtask details journal. status_note is bumped on status flip and otherwise short. Acceptance: discipline doc states the ≤300-char acute-context rule.

  4. Subtask.details is the canonical session-by-session journal home. Commits, test counts, OQ ratifications, Checker verdicts, and Curator decisions are recorded in append-only <info added on …> blocks in details. When in doubt about which field carries narrative content, the answer is details. Acceptance: discipline doc designates details as the catch-all; matches the existing workflow-orchestration §Ledger field-discipline table.

  5. Subtask.testStrategy is a one-line acceptance criterion. It states a single assertion the Checker can gate against — not a multi-paragraph test plan. Acceptance: discipline doc states the one-line rule; the soft-warning (inv 8) fires on multi-criterion blobs over budget.

  6. cross_doc_links[] is the pointer mechanism. Canonical references are pointed at, not copied into task-list fields. Each entry is {path (repo-relative), anchor, raw} per DocLinkSchema. Acceptance: discipline doc documents the pointer-not-copy rule; no schema change (the shape is already enforced).

  7. The discipline doc states the canonical-ref-vs-traceability boundary. It reproduces the “would-it-survive-the-task” decision rule (RESEARCH §3): post-close-durable content → docs/; task-execution/tracking content → task-list. Acceptance: the rule and ≥3 worked examples appear in the doc.

Soft-warning mechanism (the Zod-encodable side)

Section titled “Soft-warning mechanism (the Zod-encodable side)”
  1. Field-length drift surfaces as a non-fatal warning, never a schema rejection. parseTaskListWithWarnings() (KH lib/validation/task-list-schema.ts) emits a TaskListWarning for each field that exceeds its §2 budget, alongside the existing 25-Subtask-ceiling warning. TaskListSchema.parse() is unchanged — the live (over-budget) ledger still parses successfully. Acceptance: a fixture with an over-budget description parses without throwing and yields a warning naming the Task id, the field, and the measured-vs-budget chars; the 25-Subtask warning still fires unchanged.

  2. The four spec/planning skills cite the discipline doc. workflow-orchestration, write-product-spec, write-tech-spec, and planning-and-task-breakdown reference the canonical discipline doc at the point where each writes/advises on ledger field content, so future Planners honour it without rediscovery. Acceptance: each of the four SKILL.md bodies contains a reference to the discipline doc path.

  3. No hard length cap is introduced. The schema’s text fields remain z.string()/z.string().nullable() with no .max(). (RESEARCH §4.1: a hard cap would reject the live ledger and diverge the vendored schema from task-view.) Acceptance: git diff on the schema shows added warning logic only, no .max( on a text field.

  4. The soft-warning addition introduces no *Schema field-shape change. The warning logic lives in parseTaskListWithWarnings (a function body) + a module-level FIELD_BUDGETS const, not in the exported SubtaskSchema/TaskSchema/TaskListSchema field definitions — so the validated shape stays logically identical to task-view’s. (Post-Checker correction: the drift workflow’s normalise() does not strip the new function body / object const, so it WILL emit a non-blocking ::warning:: re-vendor reminder until task-view re-vendors — that is intended + acceptable per OQ-T2, not a failure.) Acceptance: no field added/removed/retyped on any *Schema; the drift workflow stays non-blocking (warning, never CI-fail).

  1. The sweep policy is specified; sweep execution is deferred and parent-sequenced. PRODUCT defines which Tasks the sweep targets (those over budget per §2: the 19 descriptions >1500, the 10 status_notes >300, etc.) and how content is relocated (rationale → docs/, journal → Subtask details, with cross_doc_links pointers added). Execution is the lowest-priority {34.5+} Subtask and MUST be preceded by an OQ-pending.md to the parent so it is sequenced against the other terminals’ shared task-list.json edits. Acceptance: PRODUCT §4 enumerates the sweep target set + relocation rules; the {34.5+} decomposition marks the sweep lowest-priority with the OQ-first gate.

  2. The sweep preserves provenance and is non-destructive. No content is deleted — it is relocated: rationale moves to a docs/ artefact (created if absent) with a cross_doc_links pointer back; narrative moves into the appropriate Subtask details journal block. After the sweep, every previously-inlined fact is still reachable. Acceptance: the sweep policy states the relocate-not-delete rule; spot-check shows each swept Task’s removed prose is recoverable via its new cross_doc_links target.


§4 Retroactive sweep policy (target set + relocation rules)

Section titled “§4 Retroactive sweep policy (target set + relocation rules)”

Target set (measured, RESEARCH §2):

  • Task.description > 1500 chars — 19 Tasks (priority: the 10 over 2500, then the rest).
  • Task.status_note > 300 chars — 10 Tasks.
  • Subtask.description > 250 chars — 119 Subtasks (low priority; cosmetic).
  • Subtask.testStrategy > 300 chars — 78 Subtasks (low priority; cosmetic).

Relocation rules:

Over-budget content typeAction
Design rationale / migration trade-offs / REJECTED alternatives in descriptionMove to docs/specs/<feature>/TECH.md (REJECTED-alternatives §) or docs/research/; replace with 1-paragraph what+why + cross_doc_links pointer.
Session narrative in description or status_noteMove to the relevant Subtask details <info added on …> journal block.
Acute status context > 300 in status_noteTrim to the current-status assertion; move the rest to details.
Multi-criterion testStrategyReduce to the single load-bearing acceptance assertion; move detail to details.

Sequencing constraint (load-bearing): the sweep is a broad edit to task-list.json, which 4 parallel cmux terminals share. It MUST NOT run as an uncoordinated wide rewrite. The {34.5+} sweep Subtask’s first action is to write OQ-pending.md to the parent requesting a sequencing slot; the parent serialises it against the other terminals’ status flips.


InvTestable assertionVerified by
1discipline doc exists with full tablefile existence + content check
2,3,7boundary rules + worked examples presentdoc content check
4details designated journal homedoc content check
5testStrategy one-line ruledoc content check
6pointer-not-copy ruledoc content check
8over-budget fixture warns, does not throw; 25-ceiling intactunit test on parseTaskListWithWarnings
94 skills cite discipline docgrep each SKILL.md
10no .max() on text fieldsschema diff
11vendor-drift not trippedschema field-export diff
12,13sweep policy enumerated + OQ-first gate + relocate-not-deletePLAN content + sweep Subtask brief

≥12 numbered Behavior invariants, each with an acceptance criterion the Checker verifies. Honours all RESEARCH ratifications (soft-warning over hard-cap, vendor-drift avoidance, doc as the canonical home, sweep deferral).