Skip to content

Per-Task-File Mirror Design

Status: Research / design-only — not yet promoted to a Task. Session: kh-prod-readiness-S55. Author: Claude (with Liam ratification: Q3 = “Design only this session”). Context: Liam asked whether the KH task-list.json should be supplemented with per-Task Markdown mirror files (analogous to Taskmaster’s task_NNN_*.md pattern at /Users/liamj/Documents/development/propel-pathways-39761/.taskmaster/tasks/). Goal: enable browser-renderable per-Task view + Liam reading individual Tasks without loading the whole JSON into context.

This doc lays out the design space + open questions. No implementation here.


docs/reference/task-list.json is the canonical traceability + observability surface for KH work. Active and recently-closed Tasks live here (per kh-sdlc-workflow.md §6.3). It is the only ingress path for workflow-orchestration (parseTaskListWithWarnings enforces schema + surfaces the 25-Subtask soft-ceiling).

Currently, the only way to read a single Task’s details + journal is to load and grep the whole JSON. That is fine for agents (they read targeted Subtasks via the dispatch brief) but suboptimal for Liam, who is the human-in-the-loop and frequently wants to view one Task’s full content in a renderable form (browser, editor, markdown viewer).

Taskmaster solves this with auto-generated per-Task .md mirrors at .taskmaster/tasks/task_NNN_*.md. Regen via task-master generate. The 319-file example dir at propel-pathways-39761/.taskmaster/tasks/ shows the pattern at scale.


docs/reference/
├── task-list.json # canonical source (unchanged)
└── tasks/ # NEW — auto-generated per-Task .md mirrors
├── ID-6.md
├── ID-7.md
├── ID-8.md
├── ...
└── ID-N.md

File naming: ID-{id}.md where {id} matches the integer id field in task-list.json. Canonical “ID-N” prefix everywhere except the JSON id field itself.

KH currently has 9 active Tasks (S55 baseline). At Taskmaster scale (~300 tasks) the dir would be larger but still manageable.

Render the full Task object including all Subtasks + journal blocks:

# ID-{id}: {title}
| Field | Value |
| --- | --- |
| Status | {status} |
| Priority | {priority} |
| Effort estimate | {effort_estimate} |
| Owner | {owner} |
| Created | {createdAt or earliest session_refs entry} |
| Updated | {updatedAt} |
| Session refs | {session_refs[]} |
| Commit refs | {commit_refs[]} |
| Dependencies | {dependencies[]} |
| Cross-doc links | {cross_doc_links[]} |
## Description
{description}
{priority_note ? "**Priority note:** " + priority_note}
{status_note ? "**Status note:** " + status_note}
## Subtasks
### ID-{id}.{subtask.id}: {subtask.title}
| Field | Value |
| --- | --- |
| Status | {subtask.status} |
| Dependencies | {subtask.dependencies[]} |
**Description:** {subtask.description}
**Test strategy:** {subtask.testStrategy}
**Details:**
{subtask.details}
---
(repeat per Subtask)

Path: scripts/generate-task-md.ts.

Signature: bun run gen:tasks (alias in package.json).

Behaviour:

  1. Reads docs/reference/task-list.json via parseTaskListWithWarnings from lib/validation/task-list-schema.ts (canonical ingress).
  2. For each Task: writes docs/reference/tasks/ID-{id}.md using the template above.
  3. Idempotent — same input produces byte-identical output across runs.
  4. Deletes orphan tasks/ID-N.md files for Tasks no longer in task-list.json (post-cancel / post-reclassification cleanup).
  5. Optional --check mode (CI-friendly) — exits non-zero if regen would change any file, no writes.

Effort: ~1h authoring (mirror-file shape + script harness + tests).

Three layered options (recommend deploying B + C; A is the always-available fallback):

OptionTriggerProCon
A. ManualLiam runs bun run gen:tasksZero automation surfaceEasy to forget; drift accumulates silently
B. Local Stop hookFires after any Edit/Write to docs/reference/task-list.jsonLocal mirror always fresh; no PR frictionAdds a hook entry; need to scope correctly
C. CI guardgen:tasks --check step in ci.ymlCatches drift on PR (mirror-file commits enforced)Adds ~5s to CI

Recommendation: B + C. Manual A is implicitly available regardless.

The mirror is for humans, not agents. Existing workflow stays unchanged:

FileTouched?Why
workflow-orchestration SKILL.md “Loading task-list.json” sectionNoCanonical ingress remains parseTaskListWithWarnings(JSON)
task-executor agent — dispatch-brief readingNoExecutor reads the details field passed in its brief; doesn’t ingest mirrors
task-planner agent — Subtask populationNoPlanner writes to JSON; mirror regenerates after
task-checker agent — verificationNoChecker reads commit set + spec slice + JSON Subtask; not the mirror
workflow-curator agent — triageNoCurator’s update-roadmap-backlog writes to other JSON ledgers, not task-list.json
start-session skill §2c “Task-list state inspection”OptionalCould add a footnote: “Per-Task .md mirrors at docs/reference/tasks/ give a faster read for individual Tasks; JSON remains the parse target.”
handoff skill — continuation prompt assemblyOptionalCould point to mirror paths in the “Read first” section for human-friendly opens
claude-md-management:claude-md-improver, kpf:refresh-reference-docs etc.NoMirror is downstream; never the source

Net: agent paths untouched. Only Liam-facing skills (start-session + handoff) optionally point to mirrors.


These are the design judgement calls. Defaults proposed where possible.

QQuestionDefault proposal
Q1Subtask details field journal blocks (<info added on …>) — render full verbose journal in per-Task .md or summarise?Full verbose. Loses fidelity if summarised; mirror is the human-readable surface, fidelity matters there
Q2Counter padding: ID-1.md vs ID-01.md vs ID-001.md?No padding (matches existing convention — ID-6.md, ID-19.md). Filesystem sort fine for current scale
Q3CI guard policy — block PR on drift, or warn only?Block. Mirror drift = stale view; treat as test failure to keep them honest
Q4Include backlinks from per-Task .md to source spec docs (PRODUCT.md, TECH.md, PLAN.md) where they exist?Yes. Each Subtask {N.2}/{N.3}/{N.4} should link to its written artefact for one-click navigation
Q5Render dependencies[] as plain text or as links to other ID-N.md mirrors?Links. [ID-7] → ./ID-7.md (illustrative output format) — one-click navigation between Tasks
Q6Mirror for cancelled Tasks — keep, delete, or move to archive?Delete. Per existing task-list.json rules (cancelled Tasks are removed), mirror follows
Q7Mirror for done Tasks — same as in_progress, or compact form?Same as in_progress. Done Tasks are recently-closed records per §6.3; full content stays useful
Q8Should the Stop hook (option B) fire on every edit, or debounce (e.g. only on commit)?Every edit. Cheap (~9 small files at current scale); deterministic

Treating this as a Task with PRODUCT + TECH + implementation Subtasks:

SubtaskScopeEffort
{N.1} RESEARCH.mdSkip — this doc is the research0h
{N.2} PRODUCT.mdBehaviour invariants for mirror file shape + regen triggers + Q1-Q8 answers~30min
{N.3} TECH.mdGenerator script + hook config + CI step + per-Task .md template~30min
{N.4} PLAN.mdProbably skip — N.5+ Subtasks are linear0h
{N.5} Generator script + testsscripts/generate-task-md.ts + Vitest~1.5h
{N.6} Initial mirror commit + add to .gitignore/CIOne-time generation + git add + gen:tasks package.json alias~30min
{N.7} Stop hook configuration.claude/settings.json PostToolUse hook on task-list.json edits~30min
{N.8} CI guardci.yml step running bun run gen:tasks --check~30min
{N.9} Optional start-session + handoff skill footnotesTwo small Edit ops~15min

Total: ~4h. Comfortably one-Task scope.


  • Browser rendering surface. Liam mentioned “viewing a markdown version and rendering it to the UI or a browser” — that’s a separate concern (Astro-Starlight from ID-9, or a lightweight docs.kh.phew.org.uk route). Per-task .md files are render-ready by any markdown viewer; they don’t dictate which one
  • Bidirectional sync. Mirror is read-only; canonical writes still go to task-list.json
  • Cross-Task aggregation views. A “next task to work on” view would need a separate index file (docs/reference/tasks/INDEX.md) — outside this design’s scope; can add as a follow-up
  • Migration of any Taskmaster CLI behaviour. Per workflow-orchestration §“What this skill is NOT” → “Not Taskmaster-coupled” — we adopt the per-file shape, not the task-master generate invocation or MCP tool

Promoted to ID-20 at S55 close (per Liam ratification). Subtasks:

  • 20.1 RESEARCH — this design doc (status: done at S55 close; awaits Liam OQ responses to mark complete)
  • 20.2 RESEARCH — rendering-surface options (see §8 below)
  • 20.3+ — spec chain (PRODUCT.md / TECH.md / PLAN.md if needed) authored after both research subtasks complete

7. Cross-project tooling constraint (important — affects file locations)

Section titled “7. Cross-project tooling constraint (important — affects file locations)”

Per Liam at S55 close:

The task list tooling and setup, as well as our dev workflow eventually aren’t Knowledge Hub products. They’re the tooling that you and I use across multiple projects. So anything that we do create or build, we need to keep this in mind in terms of where we are storing tools/files etc.

Implication: the generator script + render surface + any supporting tooling MUST NOT live inside knowledge-hub/scripts/ or knowledge-hub/lib/ even though they will operate on knowledge-hub/docs/reference/task-list.json first. Reasonable target locations to evaluate during 20.2 / TECH.md:

  • ~/.claude/plugins/task-list-mirror/ — Claude plugin shape (skill + scripts + settings). Works cross-project; auto-discovered by Claude Code.
  • ~/.claude/tools/task-list-mirror/ — bare scripts dir under global Claude config. Simpler; no plugin discovery semantics needed.
  • Separate dev-tooling repo — own git repo, installable via npm/bun link or symlink into each project. Highest ceremony; clearest project boundary.
  • ~/.config/dev-workflow/ — XDG-style; standalone CLI invoked by name.

What stays in KH: the canonical docs/reference/task-list.json (this is KH’s data, not the tool); the mirror output docs/reference/tasks/*.md (KH-rendered artefact); the Stop hook + CI guard wiring in .claude/settings.json + .github/workflows/ (per-project glue).

What moves out of KH: the generator script logic; the renderer (Plannotator fork or alternative); any shared schemas / TypeScript types describing the task-list shape.

The Q1-Q8 answers stay portable across this constraint — they describe the mirror file shape, not where the generator lives.


8. Rendering surface research (Subtask 20.2 scope)

Section titled “8. Rendering surface research (Subtask 20.2 scope)”

Liam at S55 close: the per-Task .md mirror solves the storage problem (one file per Task, browser-renderable, editor-friendly). It does not solve the interaction problem — Liam wants to edit Task / Subtask state (text, status) from a UI, not just view.

Inputs to evaluate in 20.2:

Input A — Plannotator (Liam’s locally-forked candidate)

Section titled “Input A — Plannotator (Liam’s locally-forked candidate)”
  • Current capability: annotation UI on markdown files; takes filename as input
  • Gap (per Liam): ability to update content (e.g. Subtask text or status), not just annotate
  • Research questions:
    • Does Plannotator already support edit mode that wasn’t surfaced in the README?
    • If not, what’s the smallest fork delta to add: (a) edit-in-place for arbitrary text spans, (b) structured field edit for status (enum) + priority (enum)?
    • Does the fork need a write-back path to task-list.json (the canonical), or only to the .md mirror (with the generator running in reverse)?

Input B — knowledge-hub-knowledge-platform repo legacy work

Section titled “Input B — knowledge-hub-knowledge-platform repo legacy work”
  • Path: /Users/liamj/Documents/development/knowledge-hub-knowledge-platform/ (worktree on kh-knowledge-platform branch)
  • What was built: migrations + code that rendered state files (backlog, roadmap) to the platform’s UI
  • Research questions:
    • Catalogue the relevant files (likely app/, components/, lib/, migrations) — what does it render, how, with what UI shape?
    • Which pieces are reusable as cross-project tooling vs. KH-coupled (assume most are KH-coupled given they’re in the KH repo)?
    • Would the rendering approach work for per-Task .md mirrors with edit-back, or is it too coupled to the platform’s auth + Supabase setup?

Recommendation framing for the 20.2 deliverable

Section titled “Recommendation framing for the 20.2 deliverable”

A short doc (docs/research/per-task-render-surface.md) covering:

  1. Plannotator current edit capabilities (with link to fork)
  2. kh-knowledge-platform legacy work inventory (with file paths + what each does)
  3. Recommendation: build on Plannotator fork (with required deltas), build on kh-platform legacy (with caveats), or new minimal UI
  4. Cross-project tooling location implications (per §7) for whichever path wins

This research blocks 20.3 PRODUCT.md authoring. Once both 20.1 and 20.2 are ratified, the spec chain captures the unified design.