Per-Task Render Surface Research
Per-Task Render Surface Research
Section titled “Per-Task Render Surface Research”Status: Research — ID-20.2 deliverable (Subtask of ID-20 per-Task .md mirror feature).
Session: kh-prod-readiness-S56.
Author: Claude (sub-agent, worktree dispatch).
Context: Companion to docs/research/per-task-file-mirror-design.md §8.
§1 — Plannotator current capability
Section titled “§1 — Plannotator current capability”1.1 Repository overview
Section titled “1.1 Repository overview”Plannotator (v0.19.18, github.com/backnotprop/plannotator) is a Bun-runtime monorepo
comprising two apps that matter for this research:
apps/hook/— the Claude Code plugin. Entry point:server/index.ts. Spawned as a local HTTP server (random port) by hooks or slash commands; opens the UI in the browser.packages/ui/— all shared React components, includingViewer.tsx,AnnotationPanel.tsx, the markdown parser, and annotation types.
Additional packages: packages/server/ (Bun server primitives), packages/shared/
(runtime-agnostic types + storage helpers), packages/editor/ (plan review app entry),
packages/review-editor/ (code review app entry).
1.2 Modes of operation
Section titled “1.2 Modes of operation”Plannotator runs in nine modes (per apps/hook/server/index.ts). The three relevant to
this research:
| Mode | Trigger | Input | Output |
|---|---|---|---|
| Annotate | /plannotator-annotate <file.md> | Single markdown file (path on disk) | Structured annotation feedback → stdout (captured by calling agent) |
| Plan Review | ExitPlanMode PermissionRequest hook | Plan markdown from hook JSON payload | approve / deny decision → stdout |
| Code Review | /plannotator-review | git diff output | Annotation feedback → stdout |
1.3 Edit capability (current)
Section titled “1.3 Edit capability (current)”Verdict: annotation-only, no edit-in-place of source content.
What Plannotator does today:
- Reads a markdown file and serves its content via
/api/plan(GET endpoint inpackages/server/annotate.ts). - Renders the markdown as parsed blocks (
packages/ui/utils/parser.ts) inside theViewercomponent (packages/ui/components/Viewer.tsx). - Annotates rendered text: user selects spans → toolbar → DELETION / COMMENT /
GLOBAL_COMMENT annotation types. Annotation cards in the
AnnotationPanelcomponent support inline text editing of annotation comment text (theAnnotationCardisEditingstate inpackages/ui/components/AnnotationPanel.tsx), but this edits the annotation’s comment field, NOT the source document text. - Submits via
/api/feedback(POST) — sends{ feedback: string; annotations: unknown[] }to the server, which resolves thedecisionPromiseand writes the assembled feedback string tostdout. The calling agent receives this as a signal to revise. No write-back to the source file occurs at any point.
What Plannotator does NOT do today:
- Does not write back to the source file (
filePathis display-only in the server). - Does not support field-level structured edits (e.g., changing a
statusenum value). - Does not provide a form UI for structured metadata — frontmatter is rendered read-only
via
FrontmatterCardinViewer.tsx. - Does not expose a JSON data layer; the server is stateless between invocations.
1.4 Input shape
Section titled “1.4 Input shape”- Single file path (
plannotator annotate <file.md>) → reads to string → served via/api/plan. - HTML / URL / folder (via
--render-html, Jina/Turndown, or folder browser) — these convert to markdown before serving; not relevant for per-Task.mdmirrors. - Stdin (hook mode) — plan markdown embedded in hook JSON payload.
1.5 Persistence model
Section titled “1.5 Persistence model”Plannotator’s storage architecture:
- Annotation drafts (
~/.plannotator/history/…): auto-saved per content hash via/api/draftendpoint; survive server restarts; NOT write-back to source. - Plan snapshots (
~/.plannotator/plans/): saved on approve/deny decision; plan content only, no source mutation. - Config (
~/.plannotator/config.json): user settings (displayName, diffOptions, conventionalComments). Mutated via/api/configPOST.
There is no path from annotation data → source .md file mutation in the current
codebase. The persistence layer is explicitly separated from the rendered source.
§2 — Required Plannotator fork deltas
Section titled “§2 — Required Plannotator fork deltas”To support the per-Task .md mirror use case — view a Task .md file and edit text +
status/priority from a browser UI with write-back to task-list.json — requires three
distinct fork deltas.
Delta A — Edit-in-place of arbitrary text spans (for Subtask details editing)
Section titled “Delta A — Edit-in-place of arbitrary text spans (for Subtask details editing)”What it requires:
- Extend
Viewer.tsxto support an “edit mode” toggle that replaces rendered blocks with editable<textarea>orcontenteditableelements. - On “Save” confirmation, POST the edited markdown to a new
/api/saveserver endpoint. /api/savewrites the mutated content back to the source file path (usingBun.write(filePath, markdown)).- The
startAnnotateServerinpackages/server/annotate.tsneedsfilePathto be a writable path (already passed in), and the server needs a new route handler for the save endpoint.
Rough effort: ~4–6 hours. The React-side block editing (toggling rendered blocks to
<textarea> with auto-resize) is the main UI work. The server endpoint is simple
(Bun.write). No new external dependencies needed.
Caveat: Per-Task .md mirrors are generated files — they are the output of
gen:tasks, not editable directly as canonical source. “Edit-in-place” in the mirror
would need to either: (a) write back to task-list.json directly (preferred — canonical
source), or (b) write to the .md mirror and trigger a reverse generator. Option (a) is
architecturally cleaner; see §2 write-back discussion below.
Delta B — Structured field edit for status and priority enums
Section titled “Delta B — Structured field edit for status and priority enums”What it requires:
- Detect YAML frontmatter fields that match known enum schemas (status, priority). The
frontmatter parser (
packages/ui/utils/parser.ts/Frontmattertype) already extracts frontmatter key-value pairs. - Replace the read-only
FrontmatterCardinViewer.tsxwith an editable version that renders<select>dropdowns for known enum fields and<input>for free-text fields. - On field change, POST to
/api/save-fieldwith{ field, value }. - Server handler updates
task-list.json(canonical) for the matched Task/Subtask ID (parsed from the.mdfilename or an embedded frontmatter fieldtask_id).
Rough effort: ~3–4 hours. Frontmatter detection is already in place; the main work is the editable FrontmatterCard component + server-side JSON patch logic.
Important constraint: For enum edits to reach task-list.json, the server must know
the canonical JSON path (tasks[i].status vs tasks[i].subtasks[j].status). This
requires either: (a) encoding the Task ID in the .md mirror frontmatter (e.g.,
task_id: "20", subtask_id: "2"), or (b) parsing the Task ID from the filename
(ID-20.md → task 20). Option (b) is simpler and avoids frontmatter schema coupling.
Delta C — Write-back target: direct to task-list.json vs to .md mirror
Section titled “Delta C — Write-back target: direct to task-list.json vs to .md mirror”Analysis:
| Write-back target | Pros | Cons |
|---|---|---|
Direct to task-list.json | Canonical source stays authoritative; mirror regenerates on next gen:tasks run; consistent with parseTaskListWithWarnings ingress contract | Requires server-side JSON patch logic (not trivial for nested Subtask details fields with embedded journal blocks); server needs to know task-list.json absolute path |
To .md mirror only | Simpler server logic (Bun.write(filePath, markdown)); Delta A is sufficient | Bidirectional sync needed (reverse generator) — adds complexity + drift risk; violates §5 of design doc (“bidirectional sync is out of scope”) |
Recommendation: Write back directly to task-list.json. The server receives the
mutated field value + task/subtask IDs and performs a targeted JSON patch using
JSON.parse / field mutation / JSON.stringify. The details field with embedded
journal blocks is a string field — replacing it with a new string is a straightforward
patch. Per-Task .md mirrors regenerate via gen:tasks automatically (via Stop hook
after task-list.json is saved).
Rough effort for Delta C (server-side JSON patch): ~2–3 hours. Main complexity is the
nested Subtask lookup (tasks.find(t => t.id === taskId).subtasks.find(s => s.id === subtaskId)). The write is atomic: read JSON → patch → write.
Summary effort table
Section titled “Summary effort table”| Delta | Scope | Effort |
|---|---|---|
| A: Edit-in-place text spans | Viewer.tsx edit mode + /api/save endpoint | ~4–6h |
| B: Structured field edit (status/priority) | Editable FrontmatterCard + /api/save-field endpoint | ~3–4h |
C: Write-back to task-list.json | Server-side JSON patch + task/subtask ID resolution | ~2–3h |
| Total | ~9–13h |
These estimates assume the fork is maintained locally (not upstreamed to the public Plannotator repo). Upstreaming would require making the feature configurable and adding tests — roughly double the effort.
§3 — kh-knowledge-platform legacy state-rendering inventory
Section titled “§3 — kh-knowledge-platform legacy state-rendering inventory”The kh-knowledge-platform worktree (branch: kh-knowledge-platform,
/Users/liamj/Documents/development/knowledge-hub-knowledge-platform) has a
/state route that renders three state-doc tabs: Roadmap, Backlog, and SOP. It is a
KH-coupled Next.js application.
Git status: mostly clean (one deleted .claude/skills/review/SKILL.md, three untracked
spec drafts). No uncommitted production code changes.
3.1 File inventory
Section titled “3.1 File inventory”| File path | Summary | KH-coupling reason | Reusability rating |
|---|---|---|---|
app/state/page.tsx | Tab-switching page for /state route. Three lazy-loaded panels: Roadmap, Backlog, SOP. Uses useSearchParams, useRouter from Next.js; useUserRole hook. Client component. | Next.js App Router, KH auth hook (useUserRole) | Low |
components/state-docs/roadmap-panel.tsx | Renders roadmap_items Supabase table: expandable section cards, filter bar (status + section), sortable item table. Uses TanStack Query (fetchRoadmapItems). Read-only. | Supabase (roadmap_items table), KH query-key + fetcher pattern, KH token CSS (--color-status-error, semantic tokens) | Low |
components/state-docs/backlog-panel.tsx | Renders backlog_items Supabase table: category-grouped items, filter dropdowns (category/status/priority), sortable columns. Read-only. | Supabase (backlog_items table), KH query-key + fetcher pattern | Low |
components/state-docs/sop-panel.tsx | Renders sop_sections Supabase table: hierarchical section list with body markdown rendering. Read-only. | Supabase (sop_sections table), KH query-key + fetcher pattern | Low |
components/state-docs/status-badge.tsx | Reusable StatusBadge + PriorityBadge components. Renders enum values as colour-coded badges using KH semantic tokens. | KH design-system tokens (--color-* vars from app/globals.css); badge colour-to-status mapping is KH-specific | Medium (badge concept is portable; token values are not) |
types/state-docs.ts | TypeScript types for RoadmapItem, BacklogItem, SopSection. Derived from Supabase migration schema. Enum constants for statuses, priorities, categories. | Supabase schema; comment explicitly states “Read-only UI types — no mutation types needed (spec §2: no edit UI).” | Medium (enum definitions portable; Supabase-derived field names not) |
lib/query/fetchers.ts (lines 130–184) | Three async fetchers: fetchRoadmapItems, fetchBacklogItems, fetchSopSections. Query Supabase tables directly via createClient(). | Supabase-js client, KH project ID, user_roles table RLS policy. provenance: 'engineering_seed' constant is KH-specific. | Nil |
lib/query/query-keys.ts (state-docs section) | TanStack Query key factory for stateDocs.roadmap, stateDocs.backlog(opts), stateDocs.sop. | KH query-key convention (queryKeys pattern). | Low |
supabase/migrations/20260422164830_create_state_docs_tables.sql | Creates roadmap_items, backlog_items, sop_sections tables with RLS policies, CHECK constraints, indexes, updated_at trigger. | KH Supabase project, user_roles table dependency, engineering_seed provenance constant. | Nil (migration is non-portable) |
supabase/migrations/20260423224800_create_client_state_doc_tables.sql | Creates client-facing (possibly staging-only) state-doc table variants. | Same KH coupling as above. | Nil |
hooks/use-user-role.ts (indirect) | Auth hook used by app/state/page.tsx to gate loading spinner. | KH auth system, Supabase user_roles table. | Nil |
Files investigated: 11 distinct files (6 source + 2 types + 2 migrations + 1 hook reference). Not all were fully read — SQL migrations examined in full; component files examined in full; fetchers examined in the state-doc section.
§4 — Reusability analysis
Section titled “§4 — Reusability analysis”4.1 Portable components (with caveats)
Section titled “4.1 Portable components (with caveats)”status-badge.tsx — Medium reusability.
The concept (enum value → coloured badge) is generic and useful for a per-Task viewer.
However the token names (--color-status-error, bg-primary, text-muted-foreground)
are KH’s Warm Meridian design system. Reuse in a cross-project tool would require either:
- Porting the token definitions into the tool’s own CSS, or
- Replacing tokens with hardcoded colours (defeats the purpose), or
- Accepting a Warm Meridian dependency in the cross-project tool (problematic).
types/state-docs.ts — Medium portability for the enum concept, nil for schema.
The enum constants (ROADMAP_STATUSES, BACKLOG_STATUSES, etc.) are useful as a
reference for what status values exist. The actual TypeScript types are derived from a
Supabase schema that doesn’t map to task-list.json. For per-Task mirrors, new types
would be needed based on the TaskListSchema from lib/validation/task-list-schema.ts.
4.2 Non-portable components
Section titled “4.2 Non-portable components”roadmap-panel.tsx, backlog-panel.tsx, sop-panel.tsx — Low/nil reusability.
All three are deeply coupled to:
- Supabase as the data source (via TanStack Query fetchers that call
supabase.from(…)) - KH’s
user_roles-based RLS for data access - KH’s Vercel/Next.js deployment environment (route handler, cookies, auth)
- A
provenance: 'engineering_seed'data model that maps to KH’s roadmap/backlog JSON format (a different schema fromtask-list.json)
The state-docs UI was explicitly designed as a read-only view of Supabase tables
(per types/state-docs.ts comment: “Read-only UI types — no mutation types needed”). It
has no edit path whatsoever. Adding edit-back would require building a new mutation layer
from scratch — comparable effort to building Option C (new minimal UI) without the
cross-project portability benefit.
Fetchers — Nil reusability. The Supabase fetchers hard-code table names, provenance constants, and KH auth patterns. They cannot be reused cross-project.
4.3 Reusability summary
Section titled “4.3 Reusability summary”The kh-knowledge-platform state-docs code provides inspiration for UI patterns (tab structure, expandable sections, filter bars, badge components) but is architecturally unsuited for reuse as cross-project tooling. The data layer, auth layer, and deployment environment are all KH-specific. Reusing it would require stripping out approximately 80% of the code and rebuilding the data layer — at which point you are building Option C (new minimal UI) anyway, with added migration complexity.
§5 — Three rendering-surface options
Section titled “§5 — Three rendering-surface options”Option A — Plannotator fork + required deltas
Section titled “Option A — Plannotator fork + required deltas”Description: Fork Plannotator locally (already at /Users/liamj/Documents/development/plannotator), implement Deltas A + B + C from §2, and deploy as a Claude plugin at ~/.claude/plugins/task-list-mirror/. The forked plannotator annotate command becomes task-mirror annotate <ID-N.md>, or the existing /plannotator-annotate skill gains a new code path for .md mirror files.
Implementation cost: ~9–13h (§2 estimates). Maintained as a local fork of an active upstream project.
Cross-project tooling fit (per §7 constraint):
Plannotator is already a cross-project tool (~/.claude/plugins/plannotator or installed
globally). Adding per-Task edit capability either: (a) extends the existing Plannotator
plugin with a task-mirror-specific mode (low friction but pollutes a general tool with
KH-specific schema knowledge), or (b) creates a separate task-list-mirror plugin that
vendors the Plannotator UI layer (higher ceremony but cleaner separation).
Risk register:
- R1: Upstream Plannotator receives breaking API changes that diverge from the fork. Mitigation: pin a fork commit; rebasing required periodically.
- R2: Delta C (JSON patch to
task-list.json) has edge cases around journal blocks with embedded ISO timestamps — thedetailsfield is a free-form string with embedded XML blocks. Patch logic must be tested carefully. - R3: The Plannotator fork adds maintenance surface to a tool that currently requires no maintenance (plugin marketplace install). Liam must own the fork.
- R4: Plannotator is annotation-oriented (feedback → agent). Re-purposing it as a structured editor may feel semantically mismatched.
Fit for §8 edit-back requirement: Full fit once Deltas A + B + C are implemented.
View, text edit, and status/priority field edit all supported. Write-back goes to
task-list.json (Delta C).
Recommended location: ~/.claude/plugins/task-list-mirror/ as a separate plugin that
vendored the minimal Plannotator UI primitives (Viewer, AnnotationPanel stripped of
annotation logic, FrontmatterCard replaced with editable fields). Avoids polluting the
upstream Plannotator plugin.
Option B — kh-knowledge-platform legacy state-rendering reuse
Section titled “Option B — kh-knowledge-platform legacy state-rendering reuse”Description: Extract the read-only state-docs UI components from
kh-knowledge-platform and adapt them for per-Task mirror rendering with an added edit
layer.
Implementation cost: ~15–20h minimum. Extracting from Next.js + Supabase context, replacing the data layer with a local JSON reader, adding edit capability, and packaging for cross-project use all require substantial rework. This is essentially building a new UI with extra steps (unwinding the Supabase/Next.js coupling).
Cross-project tooling fit:
Poor. Every layer of the kh-knowledge-platform state-docs code assumes KH’s Supabase
instance, auth system, and Next.js deploy environment. These are not cross-project by
design. The kh-knowledge-platform branch itself is noted as “being decommissioned” in
CLAUDE.md. Building on decommissioned code is a poor investment.
Risk register:
- R1: Code is being decommissioned — no upstream investment, potential bitrot.
- R2: Data layer rewrite is comparable to building Option C from scratch, with added migration complexity.
- R3: Read-only design (
types/state-docs.ts: “no mutation types needed”) means every edit-back component must be built from scratch — no reuse benefit on the most important feature. - R4: KH’s semantic token dependency (
--color-*) must either be replicated or replaced in the cross-project context.
Fit for §8 edit-back requirement: Nil out of the box. The current code is explicitly read-only. Edit-back requires a complete new mutation layer.
Verdict: Option B is dominated by both Option A (better UI primitives, smaller delta to edit capability) and Option C (cleaner cross-project packaging without legacy debt). Not recommended.
Option C — New minimal UI
Section titled “Option C — New minimal UI”Description: Build a minimal task-viewer + editor UI from scratch, purpose-built for
task-list.json per-Task .md mirror rendering with edit-back. Likely a single-file
Vite + React app or a simple HTML page with vanilla JS.
Implementation cost: ~8–12h for a functional first version covering: markdown
rendering (via marked or similar), frontmatter field editing (status/priority dropdowns),
details field text editing, write-back to task-list.json via a local Node/Bun server.
The UI would be simpler than Plannotator (no annotation system, no sharing, no plan diff),
which reduces scope.
Cross-project tooling fit (per §7 constraint):
Excellent — built from scratch with cross-project use as the primary constraint. No KH
dependencies. Can live at ~/.claude/plugins/task-list-mirror/ or
~/.claude/tools/task-list-mirror/ with a clean plugin manifest.
Risk register:
- R1: No existing UI primitives — markdown rendering, theming, keyboard shortcuts all require implementation from scratch.
- R2: Initial build produces a simpler UI than Plannotator (no block-level annotation, no plan diff). If Liam later wants annotation capability, it must be added separately.
- R3: Bun/Node dependency for the write-back server — requires local runtime; no hosted option without more work.
Fit for §8 edit-back requirement: Full fit — purpose-built for this use case. View,
text edit (per-field or per-section), status/priority dropdown edit, and write-back to
task-list.json can all be first-class features with no legacy constraints.
Recommended location: ~/.claude/plugins/task-list-mirror/ as a self-contained
Claude plugin. Alternatively ~/.claude/tools/task-list-mirror/ if plugin discovery is
not needed (invoked via explicit command from a skill).
§6 — Recommendation framing
Section titled “§6 — Recommendation framing”Leading recommendation: Option C — New minimal UI
Section titled “Leading recommendation: Option C — New minimal UI”Rationale: The per-Task .md mirror feature has a specific, narrow requirement:
render one Task’s content, allow field edits and text edits, write back to
task-list.json. Neither Plannotator nor the kh-platform code serves this requirement
out of the box. Option C reaches the same end state (~10h) with fewer risks: no upstream
divergence (Option A R1), no decommissioned code debt (Option B R1), and clean
cross-project packaging from day one.
The kh-platform reuse option (B) is strictly dominated. The Plannotator fork option (A) is viable but adds ~3h of fork-maintenance overhead relative to Option C, for features (annotation system, plan diff, URL sharing) that are not required for the per-Task viewer use case.
Minimum viable scope for Option C:
- A
Bun.serveHTTP server (single file, ~150 lines) that:- Serves the UI HTML on
GET / - Returns
task-list.jsonparsed Task onGET /api/task/:id - Accepts field patches on
PATCH /api/task/:idand writes back totask-list.json
- Serves the UI HTML on
- A React SPA (Vite, ~300 lines) that:
- Renders the Task frontmatter table with editable dropdowns for
statusandpriority - Renders the Subtask list with
detailsfield editable via<textarea> - Renders
<info added on …>journal blocks read-only
- Renders the Task frontmatter table with editable dropdowns for
- A Claude plugin entry point (skill) at
~/.claude/plugins/task-list-mirror/commands/that invokestask-mirror <ID-N.md>and waits for the user to close the browser.
Second-place fallback: Option A — Plannotator fork
Section titled “Second-place fallback: Option A — Plannotator fork”If Liam prefers to invest in Plannotator (already installed and familiar) rather than
building new tooling, Option A is the fallback. The fork is manageable if kept local.
Key condition: Delta C (JSON patch to task-list.json) must be the write-back target —
writing to the .md mirror without reverse-generation is out of scope per design doc §5.
Dispositive Liam ratification question
Section titled “Dispositive Liam ratification question”The recommendation hinges on one architectural judgement call:
OQ-1 (dispositive): Do you prefer to extend Plannotator (familiar, already installed) with task-editor capability (Option A, ~12–16h total including fork maintenance setup), or build a minimal purpose-built tool (Option C, ~10h, cleaner cross-project boundary)?
§7 — Cross-project tooling location implications
Section titled “§7 — Cross-project tooling location implications”Per per-task-file-mirror-design.md §7 constraint: generator + render surface MUST NOT
live inside knowledge-hub/scripts/ or lib/.
For the leading recommendation (Option C):
| Component | Where it lives | Rationale |
|---|---|---|
Generator script (generate-task-md.ts) | ~/.claude/tools/task-list-mirror/generate.ts | Cross-project script invoked by the Stop hook. KH’s .claude/settings.json wires the PostToolUse hook to invoke it. Invocable by any project that adopts the pattern. |
| Task mirror viewer server | ~/.claude/plugins/task-list-mirror/server/index.ts | Bun server + React SPA as a Claude plugin. Discovered automatically by Claude Code. Can be invoked from any project directory. |
| Plugin manifest | ~/.claude/plugins/task-list-mirror/.claude-plugin/plugin.json | Standard Claude plugin location. |
| Task mirror viewer skill | ~/.claude/skills/task-list-mirror/SKILL.md or ~/.claude/plugins/task-list-mirror/commands/task-mirror.md | Slash command that invokes task-mirror <ID-N.md> — or a zero-arg command that opens the current project’s task list. |
Write-back server (/api/task/:id PATCH) | Part of plugin server (~/.claude/plugins/task-list-mirror/server/) | Server reads/writes task-list.json at the path provided (project-relative). Cross-project: each project supplies the path. |
| Per-project glue | KH’s .claude/settings.json PostToolUse hook + .github/workflows/ci.yml gen:tasks --check step | The hook wiring and CI guard are per-project configuration — they stay in KH but reference the cross-project tool by absolute path or $PATH name. |
Mirror output files (docs/reference/tasks/*.md) | KH’s docs/reference/tasks/ | These are KH-rendered artefacts — per design doc §7, the output stays in KH even if the generator lives outside it. |
What does NOT leave KH:
docs/reference/task-list.json(canonical data, KH-owned)docs/reference/tasks/*.md(generated artefacts, KH-owned).claude/settings.jsonStop hook entry (per-project glue).github/workflows/ci.ymlgen:tasks --checkstep (per-project glue)lib/validation/task-list-schema.tsandparseTaskListWithWarnings(KH validation layer; the cross-project generator imports it or vendors a copy)
For Option A (Plannotator fork fallback):
The forked Plannotator lives at /Users/liamj/Documents/development/plannotator (already
there). The per-Task edit mode additions would be confined to the fork. The plugin
install would point at the local fork directory:
claude --plugin-dir /Users/liamj/Documents/development/plannotator/apps/hookOr, once stable, packaged to ~/.claude/plugins/plannotator-local/ mirroring the fork.
The generator script still lives at ~/.claude/tools/task-list-mirror/generate.ts — same
as Option C.
§8 — Open questions for Liam ratification (gates ID-20.3 PRODUCT.md authoring)
Section titled “§8 — Open questions for Liam ratification (gates ID-20.3 PRODUCT.md authoring)”These questions are in addition to the Q1–Q8 from per-task-file-mirror-design.md §3
(file shape questions, still open). The render-surface OQs are lettered to distinguish
them.
OQ-1 (DISPOSITIVE — gates option choice): Build a new minimal task-viewer + editor (Option C, ~10h, clean cross-project boundary from day one) or extend Plannotator with edit capability (Option A, ~12–16h including fork setup, familiar tool)?
Default proposal: Option C. Rationale: narrower scope, no upstream divergence risk, purpose-built for the task-viewer use case.
OQ-2: For write-back, should edits in the viewer always go directly to
task-list.json (canonical), with the .md mirror regenerating automatically via Stop
hook? Or is it acceptable to write to the .md mirror first and run a reverse generator?
Default proposal: Direct to task-list.json. Reverse generation adds sync complexity
and is out of scope per design doc §5.
OQ-3: Should the task viewer be invoked as a Claude plugin (auto-discovered by Claude
Code, available as a slash command from any session) or as a standalone CLI tool (invoked
explicitly outside Claude Code, e.g., task-mirror ID-20)?
Default proposal: Claude plugin (cross-project slash command). Rationale: consistent with the Plannotator pattern Liam already uses; Claude Code context available during editing (e.g., agent can apply changes after Liam edits).
OQ-4: For structured field edits (status, priority), should the viewer understand the full Task status state machine (pending → in_progress → done → cancelled), or just present all valid enum values without enforcing transitions?
Default proposal: Present all valid values without enforcing transitions. Transition enforcement is a workflow-orchestration concern; the viewer is a lightweight editing surface.
OQ-5: Should the details field editor show the raw text (including embedded
<info added on …> journal blocks), or should journal blocks be rendered read-only with
only the pre-journal content editable?
Default proposal: Full raw details field editable as a single textarea. Journal
blocks are already marked with ISO timestamps; Liam is unlikely to accidentally corrupt
them when editing Subtask description text above them. Separating the two adds UI
complexity.
OQ-6: Cross-project tool location: ~/.claude/plugins/task-list-mirror/ (Claude
plugin, slash-command invocation) vs. ~/.claude/tools/task-list-mirror/ (bare scripts
dir, invoked by name) vs. separate dev-tooling git repo?
Default proposal: ~/.claude/plugins/task-list-mirror/ (Claude plugin). Rationale:
auto-discovered; slash command available from any session; consistent with CLAUDE.md §7
tooling target evaluation.
OQ-7 (new — from §2 Delta C): The details field in Subtasks contains embedded
journal blocks using the <info added on YYYY-MM-DDTHH:MM:SS.sssZ> convention. If Liam
edits the details text via the viewer UI, should the edit be appended as a new journal
block (with a system-generated timestamp prefix) or applied as an in-place mutation of
the pre-journal content?
Default proposal: In-place mutation of pre-journal content only. The editor separates
the static details description (editable) from journal blocks (append-only, not
editable in the viewer). New journal blocks continue to be appended by workflow agents.
End of open questions.