Skip to content

RESEARCH — Task-list field discipline + content audit (ID-34.1)

RESEARCH — Task-list field discipline + content audit (ID-34.1)

Section titled “RESEARCH — Task-list field discipline + content audit (ID-34.1)”

Task: ID-34 — Task-list description discipline + content audit. This Subtask: {34.1} RESEARCH — per-field length audit + canonical-ref-vs-traceability boundary + Zod-encodable-vs-doc-only split. Downstream consumer: ID-35 ledger-cli (the CLI must honour whatever discipline this chain ratifies; per the S62E crossover audit §5 the contract is “ID-34 produces inputs the CLI consumes”, not merely “ID-34 must precede the CLI”).


S62 W3 Liam concern: task-list Task.description fields are trending too long (ID-30 + ID-31 descriptions ~3000 chars each at the time). The original ID-34 framing audited 22 Tasks. The ledger has since grown to 40 Tasks / 292 Subtasks and the drift is now materially worse — Task.description max is 9590 chars (ID-28).

The root cause is not a single field but an absence of per-field write discipline: the ledger has nine human-writable text fields (description, status_note, priority_note, details, testStrategy, cross_doc_links[].raw, plus the document-level document_purpose) and no codified rule for what shape of content belongs in each. Authors default to the path of least resistance — dumping session narrative, design rationale, and canonical-reference prose into whichever field is in front of them, most often description.

The guiding principle (ID-34 description): documentation is the canonical reference (docs/specs/, docs/research/, docs/reference/); the task-list carries implementation/test steps + traceability context only. This RESEARCH establishes the measured baseline, the per-field boundary, and — critically per the crossover audit — the binary split between discipline points that can be Zod-encoded (becoming free runtime guards for the ID-35 CLI) versus those that remain human conventions enforced by Planner/Checker review.


§2 Per-field length audit (measured baseline)

Section titled “§2 Per-field length audit (measured baseline)”

Measured over docs/reference/task-list.json at HEAD 73ffb2b4 (40 Tasks, 292 Subtasks). Char counts; percentiles are nearest-rank.

Fieldnminp50p90maxmeanOver-budget count
description40258143433489590184819/40 >1500; 10/40 >2500
status_note40021047483622010/40 >300
priority_note40074217302801/40 >300

Worst offenders (description):

Taskcharstitle (truncated)
ID-289590Cocoindex flow scaffolding + Cloud Run sidecar dep
ID-298203TanStack workspace-types migration (Option C hybrid)
ID-333777Orchestrator-of-orchestrators polish
ID-313348Canonical-pipeline → task-list migration approach
ID-423211Deploy pullmd Cloud Run Service
ID-272919session-driver-cmux maintenance
ID-232796Code-intelligence integration into SDLC workflow
ID-302769Roadmap-backlog consolidation
Fieldnminp50p90maxmeanOver-budget count
details292020637996269713473journal home — long is legitimate
description292362204641957282119/292 >250
testStrategy292021142195123678/292 >300
  • Task.description is the primary drift surface. Half of all descriptions exceed ~1.4k chars; the top decile exceeds 3.3k. The two extreme cases (ID-28, ID-29) carry full design rationale + migration narrative that belongs in docs/specs/.
  • status_note drift is real but milder. A quarter exceed the 300-char convention documented in the workflow-orchestration §Ledger field-discipline table. The over-budget notes carry session-by-session carryover that belongs in the Subtask details journal.
  • Subtask.description drift. 41% exceed 250 chars; the schema comment says “one-sentence summary (~80–200 chars)”. Authors are duplicating details content up into description.
  • Subtask.testStrategy drift. 27% exceed 300 chars; many encode a multi-criterion test plan where a one-line acceptance criterion is the contract (the Checker verifies against testStrategy; a paragraph is harder to gate against than a sentence).
  • Subtask.details is correctly the largest field (p50 2k, max 27k). This is the load-bearing dispatch brief + append-only journal (<info added on …> blocks). Length here is expected and should NOT be capped — capping the journal would defeat its purpose. The discipline for details is structural (journal-block format), not length.

§3 Canonical-reference vs traceability boundary

Section titled “§3 Canonical-reference vs traceability boundary”

The boundary question: for any piece of text an author wants to record, does it belong in documentation (canonical reference) or in the task-list (traceability)?

Decision rule (the “would-it-survive-the-task” test):

Content that remains true and useful after the Task closes — design rationale, architecture decisions, the “why” behind an approach, API contracts, schema definitions — is canonical reference. It belongs in docs/specs/<feature>/{PRODUCT,TECH}.md, docs/research/, or docs/reference/. The task-list field carries a pointer (cross_doc_links[]) to it, not a copy.

Content that is specific to executing or tracking this Task — what to build next, which files a Subtask owns, the one-line acceptance the Checker gates against, the session-by-session record of what happened — is traceability. It belongs in the task-list, in the field whose shape matches.

Worked examples from the current ledger:

Current contentWhere it lives nowWhere it belongs
ID-28 9590-char flow-scaffolding design narrativeTask.descriptiondocs/specs/cocoindex-*/description keeps a 1-paragraph what+why + cross_doc_links pointer
ID-29 8203-char migration-option rationale (Option A/B/C trade-offs)Task.descriptiondocs/specs/.../TECH.md REJECTED-alternatives §; description keeps the chosen-approach summary
ID-35 “REJECTED alternatives: (A)… (B)…”Task.description (1100+ chars of it)docs/specs/id-35-ledger-cli/TECH.md; description keeps the one-line verdict
”S266: PRIORITISED for next-session parallel-cmux phase…”ID-35 status_notecorrect — acute status context, ≤300
Session-by-session “I did X, learned Y, Checker said Z”sometimes status_note, sometimes descriptionSubtask details <info added on …> journal block

The pattern: Task.description should be a compact what+why (one paragraph), with the heavy material extracted to docs/ and pointed at via cross_doc_links. This is exactly the relationship ID-35’s own record already models well (docs/research/id-35-crossover-audit.md is referenced, not inlined) and ID-28/ID-29 model poorly (rationale inlined).


§4 Zod-encodable vs doc-only split (the load-bearing analysis)

Section titled “§4 Zod-encodable vs doc-only split (the load-bearing analysis)”

Per crossover audit §5 (RECOMMEND-CHANGE on ID-34 TECH scope): the discipline points must be split into those that can become a Zod schema delta (free runtime guard for the ID-35 CLI, since the CLI re-parses via the vendored schema on every write) versus those that remain human conventions (Planner/Checker review + skill-body amendments).

4.1 The hard-cap trap (why naive .max() is wrong)

Section titled “4.1 The hard-cap trap (why naive .max() is wrong)”

The KH task-list Zod schema (lib/validation/task-list-schema.ts) currently has no length caps on any text field — description: z.string().min(1), status_note: z.string().nullable(), etc. Adding a hard z.string().max(N) is dangerous:

  1. It would reject the current ledger. 19 Tasks have descriptions >1500 chars; if we set description: z.string().max(1500), TaskListSchema.parse() throws on the live task-list.json immediately — breaking parseTaskListWithWarnings, the task-view viewer, every consumer, and the ID-35 CLI itself — until the retroactive sweep completes. The sweep is explicitly the LOWEST-priority, integration-sensitive piece (4 terminals share the file). A hard cap inverts the dependency: it would force the sweep to land first and atomically, which the parallel-cmux phase cannot accommodate.

  2. It creates vendoring drift. lib/validation/task-list-schema.ts is a vendored copy of task-view’s packages/schemas/src/task-list-schema.ts, guarded by the non-blocking task-view-vendor-drift.yml workflow. A KH-side .max() that is not also in task-view’s source diverges the two; a task-view re-vendor would silently revert it. Any Zod delta must be applied to both repos to stay coherent — a real maintenance cost.

4.2 The established KH precedent: soft warnings, not hard rejection

Section titled “4.2 The established KH precedent: soft warnings, not hard rejection”

KH already solved “advisory limit that must not break the ledger” — the 25-Subtask soft ceiling (PRODUCT inv 20). It is implemented in parseTaskListWithWarnings() as a warning, not a schema .max():

// lib/validation/task-list-schema.ts:211-230 (installed, verified)
export function parseTaskListWithWarnings(input: unknown): {
value: TaskList; warnings: TaskListWarning[];
} {
const value = TaskListSchema.parse(input); // hard schema still passes
const warnings: TaskListWarning[] = [];
for (const task of value.tasks) {
if (task.subtasks.length > 25) {
warnings.push({ taskId: task.id, message: `… consider splitting …` });
}
}
return { value, warnings };
}

This is the correct home for field-length discipline: soft warnings that surface drift without rejecting the document. The ID-35 CLI already plans to parse via this helper, so length warnings flow to the CLI operator “for free” — exactly the audit §5 framing (“ID-34 produces inputs the CLI consumes via Zod”). It avoids the hard-cap trap and is naturally implemented once in parseTaskListWithWarnings (KH-side; task-view’s viewer can adopt the same helper independently without a schema-shape divergence, since adding a function — not a field constraint — does not trip the vendor-drift field diff).

Discipline pointMechanismRationale
Task.description ≤ ~1500 chars (warn)Soft warning in parseTaskListWithWarningsHard cap rejects current ledger; warning surfaces drift to author + CLI
Task.status_note ≤ 300 charsSoft warning (could later harden once swept)Already a documented convention; warn to enforce
Subtask.description ≤ 250 chars (warn)Soft warningSchema comment already says ~80–200; warn on drift
Subtask.testStrategy one-line (≤ ~300, no newline-heavy)Soft warningChecker contract is a sentence; warn on multi-criterion blobs
Subtask.details lengthNo limitJournal + dispatch brief; length is legitimate
description carries what+why, not session narrative”Doc-only (skill amendment)Not mechanically detectable
”canonical rationale lives in docs/, pointed at via cross_doc_linksDoc-only (skill amendment)Semantic judgement, not Zod-expressible
”journal goes in details <info added on …> blocks”Doc-only (already in workflow-orchestration table)Structural convention, Planner/Checker enforced
cross_doc_links[] shape (path + anchor + raw)Already Zod-enforced (DocLinkSchema)No change needed

Net: four soft-warning additions to parseTaskListWithWarnings (Zod-encodable side) + a documented per-field discipline table + four skill-body amendments (doc-only side). No hard .max() caps. No vendor-schema field divergence.


§5 Current state — what already exists (do not reinvent)

Section titled “§5 Current state — what already exists (do not reinvent)”
  • workflow-orchestration/SKILL.md already carries a §Ledger field-discipline table (verified in the installed skill body) mapping last_updated / Subtask details / Task.description / Task.status_note / testStrategy / cross_doc_links / commit messages / continuation prompts / Mempalace diary to shape + load-bearing-for. ID-34 does not invent this from scratch — it promotes the table to a canonical reference doc, adds the measured budgets, and back-references the doc from the four spec/planning skills.
  • docs/reference/taskmaster-schema-reference.md is the empirical TM schema reference (field-by-field shapes, conventions). It documents what the fields are, not what discipline to write into them — the discipline doc is a distinct concern (the doc-location decision is {34.3} TECH’s call: new task-list-discipline.md vs an appended §to the schema-reference).
  • parseTaskListWithWarnings is the soft-warning seam (verified installed).
  • DocLinkSchema (lib/validation/roadmap-schema.ts) already constrains cross_doc_links[] entries.

§6 Open Questions (provisional defaults applied; parent may override)

Section titled “§6 Open Questions (provisional defaults applied; parent may override)”
  1. OQ-34-1 — Discipline doc location. New docs/reference/task-list-discipline.md vs an appended §to docs/reference/taskmaster-schema-reference.md? Provisional default (→ {34.3} TECH to ratify): new dedicated doc docs/reference/task-list-discipline.md. The schema-reference is a stable empirical what-the-fields-are artefact (snapshot-dated, “re-run if upstream changes”); discipline is an evolving how-to-write policy with a different update cadence and audience (authors/Planners, not parser implementers). Mixing them couples two doc lifecycles.

  2. OQ-34-2 — Soft-warning caps: warn-only now, or warn-now-harden-later? Defaulting to warn-only, indefinitely — hardening to .max() requires the sweep to have driven the ledger fully under budget AND a coordinated task-view re-vendor, neither of which ID-34 should block on. Revisit as a future backlog item if drift recurs post-sweep.

  3. OQ-34-3 — Retroactive sweep scope. The sweep is a broad task-list.json edit; 4 parallel terminals share the file (integration-sensitive). Defaulting to: spec the sweep policy + thresholds in PRODUCT, decompose it as the LOWEST-priority {34.5+} Subtask, and write an OQ-pending.md to the parent BEFORE attempting any broad description rewrite so the parent sequences it against the other terminals’ status flips. Per the cmux brief this is explicitly deferred to last.

  4. OQ-34-4 — Where does the soft-warning code land vs task-view? Defaulting to: extend parseTaskListWithWarnings in KH lib/validation/task-list-schema.ts only (a new function-internal warning loop, not a schema field change → does not trip vendor-drift). task-view can adopt the same warnings independently. Avoids forcing a coupled two-repo schema edit inside the parallel-cmux phase.


  • Per-field length distribution measured over all 40 Tasks / 292 Subtasks (§2).
  • Canonical-ref vs traceability boundary articulated with worked examples from the live ledger (§3).
  • Zod-encodable-vs-doc-only split produced (§4) — the load-bearing crossover-audit §5 deliverable; grounded in the installed parseTaskListWithWarnings soft-warning precedent and the vendor-drift coupling.
  • Existing artefacts catalogued so {34.2}+ do not reinvent (§5).
  • ≥4 OQs surfaced with provisional defaults (§6).