Skip to content

T-VWH3-ts-index-generator-script

Status: closed/done · Impact: medium · Complexity: medium

AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-06-03 at operator request (the task carries no autonomy field). Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.

Make docs/index.md a true build artifact: a deterministic TypeScript (bun) generator that the index skill shells out to, replacing the hand-maintained Reference tables with generated ones. The skill becomes a thin head over the deterministic tail (per S-0004-sdlc-cli-llm-head-deterministic-tail / S-0001-co-locate-first-promote-when-shared).

Deferred from the index PR: tooling is mid-migration from Python to TypeScript, so no generator code was written there. This task is the plan of work; build it in TypeScript/bun once that surface is ready.

docs/index.md today is a hand-authored copy of what the generator should emit. Its shape and rules are already specified in plugin/skills/index/SKILL.md (the Reference section is one ## table per entity type; summary cells transclude each entity’s ^summary block). This task turns that spec into running code.

The 2026-06-02 planning review sweep of docs/index.md added three structural refinements the generator must honour, folded into the Plan below: render through a template fed plain data structures, split each section into two tables (active vs emerging/obsoleted), and render deferred milestones as a table rather than a prose list.

LocationRole today
plugin/skills/index/SKILL.mdThe LLM-head index skill: specifies the Reference-table rules and explicitly defers the deterministic generator to this task (“regenerate by hand … this skill is the head for now”)
docs/index.mdHand-maintained output the generator replaces — declared a never-hand-edited build artifact, but regenerated by the LLM today
plugin/lib/model/authoring.tsExisting Eta render pipeline (entity body templates) — the engine precedent the new template reuses
plugin/lib/util/yaml.tsPromoted YAML helper the extraction module uses for frontmatter parsing
package.jsonShips eta already; no remark/mdast dependency yet
  • A small markdown-extraction module (TS) built on remark/mdast: parse once to AST; extract a ## section body by heading; test for a ^block-id; iterate entity files under a directory (excluding README/index). Frontmatter via remark-frontmatter, parsed with the existing lib/util/yaml helper. remark is a new dependency, but it is the AST substrate B-8FL9 proposes for structural validation and a T-0007 formatter candidate — this module seeds that shared use rather than hand-rolling parsers.

  • A template/renderer using Eta — already a dependency and the engine behind the entity body pipeline (plugin/lib/model/authoring.ts). Separate the two responsibilities: the generator gathers entities into data; the .eta template renders. The template is the single place table layout lives, so generation stays consistent and the skill’s job is reduced to handing it data.

  • A generator that reads docs/planning/ entities, builds those data structures, and writes docs/index.md through the template:

    • Narrative: a fixed pointer block to vision / PR-0001-sdlc / D-VSLI-distributed-work-runner-architecture (no transclusion of long prose).
    • Reference tables, sourced from frontmatter + ^summary: Products, Drivers (by kind then id), Milestones (ascending version, unversioned last), Features (kind: feature), Principles (grouped by the principle/<category> tag), Standards, ADRs (folder ADRs inline, single-file ADRs via ^summary), Technical capabilities (kind: technical).
    • Summary cells emit <id> transclusions.
  • Two tables per entity type, across the board. Each catalogued section (Principles, Standards, ADRs, Capabilities, Drivers, Milestones — not just some) splits its entities by lifecycle status into an Active table and a follow-up Emerging / obsoleted table. Omit a table when its bucket is empty. Conceptual buckets, drawn from the existing per-entity status enums (the implementer finalises the exact boundary per schema — no schema changes needed):

    BucketStatus values
    Active (in effect)open/active, open/accepted, open/published; products active
    Emergingopen/draft, open/proposed, open/planned
    Obsoletedevery closed/*retired, superseded, deprecated, abandoned, partial

    The follow-up table combines Emerging + Obsoleted (with a status column so the two are distinguishable).

  • Deferred milestones render as a table, not the current ·-separated prose list — id · milestone · status columns. This is the Milestones section’s follow-up (emerging/obsoleted) table.

  • Wire the index skill to call the generator; keep the skill body as the human-readable contract. Update the SKILL.md generation rules to document the two-table-per-section shape.

  • Idempotent: re-running against unchanged sources produces no diff.

LocationKindChange
plugin/skills/index/markdown_extract.tsnewremark/mdast extraction helpers: frontmatter → object (via remark-frontmatter + plugin/lib/util/yaml.ts), section body by heading, ^block-id test, entity-file iteration
plugin/skills/index/index_template.etanewEta template owning the index layout: narrative pointer block, two tables per section, deferred-milestones table
plugin/skills/index/generate_index.tsnewGenerator entry: read docs/planning/ entities → plain data structures → render template → write docs/index.md
plugin/skills/index/tests/newFixture test suite: extraction helpers + generator idempotency (per the established plugin/skills/<slug>/tests/ pattern)
plugin/skills/index/SKILL.mdmodifyWire the skill to shell out to the generator; document the two-table-per-section shape in the generation rules
docs/index.mdmodifyFirst generated output replaces the hand-maintained Reference tables
package.jsonmodifyAdd the remark/mdast dependencies (remark-parse, remark-frontmatter, unified)
  • AC-1: bun run plugin/skills/index/generate_index.ts regenerates docs/index.md from entity data passed through the Eta template. (Registering it as the sdlc index generate verb is T-0014’s scope per its 2026-06-03 hand-off note.)
  • AC-2: Every catalogued section emits an Active table and, when its bucket is non-empty, a follow-up Emerging / obsoleted table.
  • AC-3: Deferred milestones render as a table, not a prose list.
  • AC-4: Running the generator twice in a row yields no diff on the second run.
  • AC-5: The extraction helpers carry a fixture test suite under plugin/skills/index/tests/, passing via bun test.
  • Any Python changes (tooling is migrating to TypeScript/bun).
  • Restructuring the entity schemas.

Captured by /sdlc:task-work on 2026-06-03. PR: pending.

  • AC-1: auto — bun run plugin/skills/index/generate_index.ts emits INDEX-GENERATE-OK and writes docs/index.md
  • AC-2: auto — rendered-output tests + parent spot-check (6 follow-up tables present, Active table per section)
  • AC-3: agent-manual — Milestones follow-up renders as a Version · ID · Milestone · Status table (9 rows), prose list gone
  • AC-4: auto — two consecutive runs byte-identical (git diff --stat docs/index.md empty; idempotency test in suite)
  • AC-5: auto — bun test plugin/skills/index/tests/ → 33 pass / 0 fail
  • The auto-define → ensure-ready → task-work loop carried a spec gap to verified-ready and through to implementation in one session
  • Co-located precedents (authoring.ts for Eta config, skills/*/tests/ pattern) made conventions unambiguous for the implementing sub-agent
  • The generator emits its own slug-namespaced INDEX-GENERATE-OK marker, consistent with the marker convention
  • Step 7’s baseline diff flagged 8 false-positive new-drift findings: pre-existing failing suites embed randomized tempdir paths (/tmp/claude-502/append-pr-url-*/) so the differ can’t match them run-to-run — already tracked by T-TWZD-normalize-baseline-diff-nondeterministic-output; link, don’t respawn → T-TWZD-normalize-baseline-diff-nondeterministic-output
  • Fresh worktree had no node_modules; the implementing sub-agent had to run bun install manually before anything worked — already tracked by T-7PXF-declare-worktree-init-bun-install; link, don’t respawn → T-7PXF-declare-worktree-init-bun-install
  • Step 7’s gate resolved the baseline dir from the worktree cwd while Step 3a’s capture wrote to the main repo’s .sdlc/quality-baselines/, costing a failed invocation until --baseline-dir was passed explicitly — run_quality_checks.ts could resolve the baseline dir via the git common dir so worktree invocations find the main repo’s cache without the flag → T-44OO-plugin-scripts-self-discover-project-root
  • Driver and Capability statuses on disk (validated/verified) fall outside the task’s Active/Emerging/Obsoleted bucket table, so both sections render entirely in follow-up tables — a human should either bless those statuses into the Active bucket (generator change) or normalize the source entities’ statuses; needs a decision, not silently fine → T-0RW4-index-generator-status-bucket-coverage
  • Task spec said frontmatter is “parsed with the existing lib/util/yaml helper” but that module is dumper-only; the implementer substituted the yaml package’s parse (repo convention) — spec-authoring drift between intent and the cited helper’s actual surface → T-RKWR-auto-define-cites-real-helper-surface

← Back to Tasks