From 4e72971929974e77b689ae82579b3a174cd2c082 Mon Sep 17 00:00:00 2001 From: Thierry Templier Date: Tue, 6 Oct 2026 09:53:46 +0200 Subject: [PATCH] feat(aidd-dev): stamp the document type in the plan and phase templates The aidd-pm templates already declare `type` (task, story, epic, spike, defect); the aidd-dev ones did not, so every plan and phase lands on the kanban board as `type: unknown` - 49 of 49 parents on this repository's own board - and the board's --type filter has nothing to match. The plan templates now stamp `type: plan`, the phase template `type: phase`, and the for-sure init-tracking action names the field it must fill. A ratchet test keeps the next status-bearing template from shipping untyped: every plugin template under assets/ that stamps `status:` must name its `type:` in the same frontmatter. --- .../skills/01-plan/assets/phase-template.md | 1 + .../skills/01-plan/assets/plan-template.md | 1 + .../09-for-sure/actions/01-init-tracking.md | 2 +- .../09-for-sure/assets/plan-template.md | 1 + ...-that-tracks-status-names-its-type.test.js | 73 +++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 scripts/__tests__/a-template-that-tracks-status-names-its-type.test.js diff --git a/plugins/aidd-dev/skills/01-plan/assets/phase-template.md b/plugins/aidd-dev/skills/01-plan/assets/phase-template.md index 21bba1457..ebfa18dd9 100644 --- a/plugins/aidd-dev/skills/01-plan/assets/phase-template.md +++ b/plugins/aidd-dev/skills/01-plan/assets/phase-template.md @@ -1,4 +1,5 @@ --- +type: phase status: pending --- diff --git a/plugins/aidd-dev/skills/01-plan/assets/plan-template.md b/plugins/aidd-dev/skills/01-plan/assets/plan-template.md index 79301f70f..2109598ac 100644 --- a/plugins/aidd-dev/skills/01-plan/assets/plan-template.md +++ b/plugins/aidd-dev/skills/01-plan/assets/plan-template.md @@ -1,4 +1,5 @@ --- +type: plan objective: "{What must be true when done. One sentence.}" status: pending --- diff --git a/plugins/aidd-dev/skills/09-for-sure/actions/01-init-tracking.md b/plugins/aidd-dev/skills/09-for-sure/actions/01-init-tracking.md index a96224abe..fb0ef283d 100644 --- a/plugins/aidd-dev/skills/09-for-sure/actions/01-init-tracking.md +++ b/plugins/aidd-dev/skills/09-for-sure/actions/01-init-tracking.md @@ -23,7 +23,7 @@ The tracking file at `aidd_docs/tasks/.md`, marked created or resumed 6. **Pre-flight.** For each step, list tools, secrets, API access, data, and permissions. Mark `[✓]` already satisfied, `[~]` soft (the agent self-serves), `[!]` hard (only the user can provide it). Collect every `[!]` now; when any stays unresolved, stop before the next step. 7. **Map.** Project the whole path as an ASCII map of steps, dependencies, tools, and blockers. Ask the user to confirm and iterate until they do. 8. **Scaffold.** Load [plan-template.md](../assets/plan-template.md), creating `aidd_docs/tasks/` when missing. -9. **Create.** Write `aidd_docs/tasks/.md` from the template. Fill the frontmatter (`objective`, `success_condition`, `iteration: 0`, `status: pending`), the phases with their tasks and acceptance criteria, and the journey map. +9. **Create.** Write `aidd_docs/tasks/.md` from the template. Fill the frontmatter (`type: plan`, `objective`, `success_condition`, `iteration: 0`, `status: pending`), the phases with their tasks and acceptance criteria, and the journey map. 10. **Spawn.** Read the orchestrator recipe from [03-autonomous-loop.md](./03-autonomous-loop.md) and hand it to the Agent tool with `` filled in. ## Test diff --git a/plugins/aidd-dev/skills/09-for-sure/assets/plan-template.md b/plugins/aidd-dev/skills/09-for-sure/assets/plan-template.md index e5f274b24..5b33dd82e 100644 --- a/plugins/aidd-dev/skills/09-for-sure/assets/plan-template.md +++ b/plugins/aidd-dev/skills/09-for-sure/assets/plan-template.md @@ -1,4 +1,5 @@ --- +type: plan objective: "{What must be true when done. One sentence.}" success_condition: "{Runnable command that proves done. Example: 'npm test exits 0 AND coverage > 80%'}" iteration: 0 diff --git a/scripts/__tests__/a-template-that-tracks-status-names-its-type.test.js b/scripts/__tests__/a-template-that-tracks-status-names-its-type.test.js new file mode 100644 index 000000000..81b471757 --- /dev/null +++ b/scripts/__tests__/a-template-that-tracks-status-names-its-type.test.js @@ -0,0 +1,73 @@ +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const path = require("node:path"); +const test = require("node:test"); + +const root = path.resolve(__dirname, "../.."); + +// A template that stamps `status:` feeds the kanban board, and the board's type badge and +// --type filter read `type:` from the same frontmatter. aidd-dev shipped three templates +// without it and every plan and phase landed as `type: unknown` - 49 of 49 parents on this +// repository's own board. This ratchet keeps the next status-bearing template from +// repeating that silently. + +/** The leading frontmatter block's lines, CRLF tolerated; undefined without one. */ +function frontmatterLines(content) { + const lines = content.split(/\r?\n/u); + if (lines[0] !== "---") return undefined; + + const closing = lines.indexOf("---", 1); + if (closing === -1) return undefined; + + return lines.slice(1, closing); +} + +function templateFiles(directory) { + const files = []; + for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { + const entryPath = path.join(directory, entry.name); + if (entry.isDirectory()) { + files.push(...templateFiles(entryPath)); + } else if (entry.name.endsWith(".md")) { + files.push(entryPath); + } + } + return files; +} + +function assetDirectories() { + const directories = []; + const pluginsPath = path.join(root, "plugins"); + + for (const plugin of fs.readdirSync(pluginsPath)) { + const skillsPath = path.join(pluginsPath, plugin, "skills"); + if (!fs.existsSync(skillsPath)) continue; + + for (const skill of fs.readdirSync(skillsPath)) { + const assetsPath = path.join(skillsPath, skill, "assets"); + if (fs.existsSync(assetsPath)) directories.push(assetsPath); + } + } + + return directories; +} + +test("every plugin template that stamps a status also names its type", () => { + const untyped = []; + let statusBearing = 0; + + for (const assetsPath of assetDirectories()) { + for (const filePath of templateFiles(assetsPath)) { + const lines = frontmatterLines(fs.readFileSync(filePath, "utf-8")); + if (lines === undefined || !lines.some((line) => /^status:/u.test(line))) continue; + + statusBearing += 1; + if (!lines.some((line) => /^type:/u.test(line))) { + untyped.push(path.relative(root, filePath)); + } + } + } + + assert.ok(statusBearing > 0, "no status-bearing template found; the walk is broken"); + assert.deepEqual(untyped, []); +});