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.
§1 Scope
Section titled “§1 Scope”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.
| Field | Shape | Budget (advisory) | Load-bearing for | Limit class |
|---|---|---|---|---|
Task.title | Short noun phrase | ~30–60 chars | Human scan + cross-ref label | doc-only |
Task.description | One 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 chars | Cross-doc cross-reference target | soft-warn |
Task.status_note | Short rationale for the current status (blocked: waiting on X; S267: prioritised for …). Acute carryover only. Bump on status flip. | ≤ 300 chars | Status-line context | soft-warn |
Task.priority_note | One-line rationale for the MoSCoW/Ranked priority. | ~≤ 300 chars | Priority justification | doc-only |
Subtask.title | Short noun phrase | ~40–80 chars | Dispatch label | doc-only |
Subtask.description | One sentence summarising the Subtask. Not a copy of details. | ≤ 250 chars | Subtask scan label | soft-warn |
Subtask.details | Multi-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 limit | Per-Subtask traceability + journal | none |
Subtask.testStrategy | One-line acceptance criterion the Checker verifies against. Not a multi-criterion test plan. | ≤ 300 chars, single assertion | Checker contract | soft-warn |
cross_doc_links[] | {path, anchor, raw} per DocLinkSchema. Repo-relative path; pointer to canonical reference. | — | Doc-graph traversal | already Zod-enforced |
Task.session_refs[] / commit_refs[] | Bare session ids / short commit SHAs. | — | Provenance | doc-only |
document_purpose (root) | One-paragraph purpose of the ledger. | — | Doc header | doc-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.
§3 Behavior invariants
Section titled “§3 Behavior invariants”Discipline definition
Section titled “Discipline definition”-
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. -
Task.descriptioncarries compact what+why, not canonical rationale. Design rationale, migration option trade-offs, and REJECTED-alternatives prose belong indocs/specs/<feature>/{PRODUCT,TECH}.md(ordocs/research/), referenced fromdescriptionvia across_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). -
Task.status_notecarries acute current-status context only. Session-by-session narrative does not accumulate here; it goes to the relevant Subtaskdetailsjournal.status_noteis bumped on status flip and otherwise short. Acceptance: discipline doc states the ≤300-char acute-context rule. -
Subtask.detailsis 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 indetails. When in doubt about which field carries narrative content, the answer isdetails. Acceptance: discipline doc designatesdetailsas the catch-all; matches the existing workflow-orchestration §Ledger field-discipline table. -
Subtask.testStrategyis 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. -
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}perDocLinkSchema. Acceptance: discipline doc documents the pointer-not-copy rule; no schema change (the shape is already enforced). -
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)”-
Field-length drift surfaces as a non-fatal warning, never a schema rejection.
parseTaskListWithWarnings()(KHlib/validation/task-list-schema.ts) emits aTaskListWarningfor 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-budgetdescriptionparses 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. -
The four spec/planning skills cite the discipline doc.
workflow-orchestration,write-product-spec,write-tech-spec, andplanning-and-task-breakdownreference 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. -
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 diffon the schema shows added warning logic only, no.max(on a text field. -
The soft-warning addition introduces no
*Schemafield-shape change. The warning logic lives inparseTaskListWithWarnings(a function body) + a module-levelFIELD_BUDGETSconst, not in the exportedSubtaskSchema/TaskSchema/TaskListSchemafield definitions — so the validated shape stays logically identical to task-view’s. (Post-Checker correction: the drift workflow’snormalise()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).
Retroactive sweep policy
Section titled “Retroactive sweep policy”-
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 → Subtaskdetails, withcross_doc_linkspointers added). Execution is the lowest-priority {34.5+} Subtask and MUST be preceded by anOQ-pending.mdto the parent so it is sequenced against the other terminals’ sharedtask-list.jsonedits. Acceptance: PRODUCT §4 enumerates the sweep target set + relocation rules; the {34.5+} decomposition marks the sweep lowest-priority with the OQ-first gate. -
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 across_doc_linkspointer back; narrative moves into the appropriate Subtaskdetailsjournal 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 newcross_doc_linkstarget.
§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 type | Action |
|---|---|
Design rationale / migration trade-offs / REJECTED alternatives in description | Move 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_note | Move to the relevant Subtask details <info added on …> journal block. |
Acute status context > 300 in status_note | Trim to the current-status assertion; move the rest to details. |
Multi-criterion testStrategy | Reduce 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.
§5 Validation summary
Section titled “§5 Validation summary”| Inv | Testable assertion | Verified by |
|---|---|---|
| 1 | discipline doc exists with full table | file existence + content check |
| 2,3,7 | boundary rules + worked examples present | doc content check |
| 4 | details designated journal home | doc content check |
| 5 | testStrategy one-line rule | doc content check |
| 6 | pointer-not-copy rule | doc content check |
| 8 | over-budget fixture warns, does not throw; 25-ceiling intact | unit test on parseTaskListWithWarnings |
| 9 | 4 skills cite discipline doc | grep each SKILL.md |
| 10 | no .max() on text fields | schema diff |
| 11 | vendor-drift not tripped | schema field-export diff |
| 12,13 | sweep policy enumerated + OQ-first gate + relocate-not-delete | PLAN 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).