diff --git a/.changeset/silly-voles-snooze.md b/.changeset/silly-voles-snooze.md
new file mode 100644
index 000000000..579a4a7c4
--- /dev/null
+++ b/.changeset/silly-voles-snooze.md
@@ -0,0 +1,5 @@
+---
+type: Fixed
+pr: 2381
+---
+**Todo severity is now captured and surfaced end-to-end** — `/gsd-capture` (add-todo) now confirms a severity (blocker/major/minor/cosmetic) before writing a todo instead of silently omitting it, and `gsd-tools list-todos` / `init todos` now include the `severity` field in their JSON output (omitted for older todos that have none), so a backlog can be triaged by severity instead of by re-reading every file. (#2337)
diff --git a/gsd-core/workflows/add-todo.md b/gsd-core/workflows/add-todo.md
index 53499058d..7f1cfbfce 100644
--- a/gsd-core/workflows/add-todo.md
+++ b/gsd-core/workflows/add-todo.md
@@ -61,6 +61,34 @@ Infer area from file paths:
Use existing area from step 2 if similar match exists.
+
+Infer a **suggested** severity from the same blocker/major/minor/cosmetic taxonomy `verify-work.md`'s `severity_inference` uses — then CONFIRM it with the user before writing. Never silently auto-assign: a mis-tagged severity silently corrupts backlog triage, which is exactly the signal this field exists to provide.
+
+Suggest from the user's natural-language description:
+
+| User says | Suggest |
+|-----------|---------|
+| "crashes", "error", "exception", "fails completely", "data loss" | blocker |
+| "doesn't work", "nothing happens", "wrong behavior" | major |
+| "works but...", "slow", "weird", "minor issue" | minor |
+| "color", "spacing", "alignment", "looks off" | cosmetic |
+
+Default the suggestion to **major** if unclear.
+
+**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace the `AskUserQuestion` below with a plain-text numbered list of the four options and ask the user to type their choice number. Required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is unavailable.
+
+Confirm with AskUserQuestion (present the suggested value first):
+- header: "Severity?"
+- question: "Suggested severity: [suggested]. Confirm or change:"
+- options:
+ - "blocker" — breaks a workflow or loses data; fix first
+ - "major" — wrong behavior with no workaround
+ - "minor" — works, but with a workaround or annoyance
+ - "cosmetic" — visual/polish only
+
+Carry the confirmed value into `severity` in the create_file frontmatter.
+
+
```bash
# Search for key words from title in existing todos
@@ -97,6 +125,7 @@ Write to `.planning/todos/pending/${date}-${slug}.md`:
created: [timestamp]
title: [title]
area: [area]
+severity: [blocker|major|minor|cosmetic — confirmed in infer_severity step]
files:
- [file:lines]
---
diff --git a/src/commands.cts b/src/commands.cts
index 0d8c60e81..006d405f0 100644
--- a/src/commands.cts
+++ b/src/commands.cts
@@ -165,7 +165,7 @@ function cmdListTodos(cwd: string, area: string | undefined, raw: boolean): void
const pendingDir = path.join(planningDir(cwd), 'todos', 'pending');
let count = 0;
- const todos: Array<{ file: string; created: string; title: string; area: string; path: string }> = [];
+ const todos: Array<{ file: string; created: string; title: string; area: string; path: string; severity?: string }> = [];
try {
const files = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md'));
@@ -176,6 +176,9 @@ function cmdListTodos(cwd: string, area: string | undefined, raw: boolean): void
const createdMatch = content.match(/^created:\s*(.+)$/m);
const titleMatch = content.match(/^title:\s*(.+)$/m);
const areaMatch = content.match(/^area:\s*(.+)$/m);
+ // #2337: surface severity when present. Omit the key entirely for todos
+ // with no severity line so existing consumers of this JSON are unaffected.
+ const severityMatch = content.match(/^severity:\s*(.+)$/m);
const todoArea = areaMatch ? areaMatch[1].trim() : 'general';
@@ -189,6 +192,7 @@ function cmdListTodos(cwd: string, area: string | undefined, raw: boolean): void
title: titleMatch ? titleMatch[1].trim() : 'Untitled',
area: todoArea,
path: toPosixPath(path.relative(cwd, path.join(pendingDir, file))),
+ ...(severityMatch ? { severity: severityMatch[1].trim() } : {}),
});
}
} catch { /* intentionally empty */ }
diff --git a/src/init.cts b/src/init.cts
index a4548a8a1..c61e35854 100644
--- a/src/init.cts
+++ b/src/init.cts
@@ -1164,6 +1164,9 @@ function cmdInitTodos(cwd: string, area: string | undefined, raw: boolean): void
const createdMatch = content.match(/^created:\s*(.+)$/m);
const titleMatch = content.match(/^title:\s*(.+)$/m);
const areaMatch = content.match(/^area:\s*(.+)$/m);
+ // #2337: kept in parity with cmdListTodos — surface severity when
+ // present, omit the key entirely for todos with no severity line.
+ const severityMatch = content.match(/^severity:\s*(.+)$/m);
const todoArea = areaMatch ? areaMatch[1].trim() : 'general';
if (area && todoArea !== area) continue;
@@ -1180,6 +1183,7 @@ function cmdInitTodos(cwd: string, area: string | undefined, raw: boolean): void
path.join(planningDir(cwd), 'todos', 'pending', file),
),
),
+ ...(severityMatch ? { severity: severityMatch[1].trim() } : {}),
});
} catch {
/* intentionally empty */
diff --git a/tests/commands.test.cjs b/tests/commands.test.cjs
index 60541bfd6..0e5c42fef 100644
--- a/tests/commands.test.cjs
+++ b/tests/commands.test.cjs
@@ -1053,6 +1053,43 @@ describe('list-todos command', () => {
assert.strictEqual(output.todos[0].area, 'general', 'missing area defaults to general');
assert.strictEqual(output.todos[0].created, 'unknown', 'missing created defaults to unknown');
});
+
+ // ── #2337: severity must be surfaced when present, omitted when absent ──────
+ // cmdListTodos parsed created/title/area but silently dropped severity, so
+ // audit-open and status summaries could not triage by blocker/major/minor/
+ // cosmetic even for todos an agent had correctly hand-tagged.
+ test('surfaces severity when the frontmatter carries it (#2337)', () => {
+ const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending');
+ fs.mkdirSync(pendingDir, { recursive: true });
+
+ fs.writeFileSync(path.join(pendingDir, 'crash.md'),
+ 'title: Fix data-loss crash\narea: core\ncreated: 2026-02-01\nseverity: blocker\n');
+
+ const result = runGsdTools('list-todos', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ const todo = output.todos.find(t => t.file === 'crash.md');
+ assert.ok(todo, 'crash.md should be in results');
+ assert.strictEqual(todo.severity, 'blocker', 'severity must be surfaced verbatim');
+ });
+
+ test('omits the severity key for todos with no severity line — backward compatible (#2337)', () => {
+ const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending');
+ fs.mkdirSync(pendingDir, { recursive: true });
+
+ fs.writeFileSync(path.join(pendingDir, 'legacy.md'),
+ 'title: Legacy todo\narea: docs\ncreated: 2026-02-02\n');
+
+ const result = runGsdTools('list-todos', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ const todo = output.todos.find(t => t.file === 'legacy.md');
+ assert.ok(todo, 'legacy.md should be in results');
+ assert.ok(!('severity' in todo),
+ 'severity key must be ABSENT (not null/empty) for a todo with no severity line');
+ });
});
// ─────────────────────────────────────────────────────────────────────────────
diff --git a/tests/fix-2337-add-todo-severity.test.cjs b/tests/fix-2337-add-todo-severity.test.cjs
new file mode 100644
index 000000000..6d597edd1
--- /dev/null
+++ b/tests/fix-2337-add-todo-severity.test.cjs
@@ -0,0 +1,64 @@
+// allow-test-rule: source-text-is-the-product #2337
+//
+// The add-todo.md workflow IS the runtime contract an agent follows when
+// capturing a todo. #2337: it had no severity step and no severity frontmatter
+// field, so severity discipline depended on an out-of-band convention the agent
+// had to remember — and production todos landed with no `severity:` at all.
+//
+// These assertions guard the specific must-haves (issue #2337 acceptance
+// criteria 1): a confirm-based `infer_severity` step positioned BEFORE the todo
+// is written, and a `severity` field in the create_file frontmatter template.
+// The golden-install-parity hash catches ANY change to this file, but not
+// WHICH change — this test pins the two structural guarantees so a future edit
+// can't silently drop the step while still updating the golden.
+
+'use strict';
+
+const { describe, test } = require('node:test');
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const path = require('node:path');
+
+const ADD_TODO = path.join(__dirname, '..', 'gsd-core', 'workflows', 'add-todo.md');
+
+describe('add-todo.md severity capture (#2337)', () => {
+ const md = fs.readFileSync(ADD_TODO, 'utf8');
+
+ test('defines an infer_severity step', () => {
+ assert.match(md, //,
+ 'add-todo.md must define an infer_severity step');
+ });
+
+ test('infer_severity runs before the file is written (create_file)', () => {
+ const sev = md.indexOf('');
+ const create = md.indexOf('');
+ assert.ok(sev !== -1 && create !== -1, 'both steps must exist');
+ assert.ok(sev < create,
+ 'infer_severity must precede create_file so severity is confirmed before write');
+ });
+
+ test('infer_severity confirms with the user — never a silent auto-assignment', () => {
+ const stepStart = md.indexOf('');
+ const stepEnd = md.indexOf('', stepStart);
+ const step = md.slice(stepStart, stepEnd);
+ assert.match(step, /AskUserQuestion/,
+ 'severity must be confirmed via AskUserQuestion (with the TEXT_MODE fallback), not auto-assigned');
+ assert.match(step, /TEXT_MODE/,
+ 'must provide the non-Claude-runtime numbered-list fallback');
+ });
+
+ test('the create_file frontmatter template carries a severity field', () => {
+ assert.match(md, /^severity:\s*\[blocker\|major\|minor\|cosmetic/m,
+ 'create_file frontmatter template must include a severity field');
+ });
+
+ test('severity taxonomy matches verify-work.md (blocker/major/minor/cosmetic)', () => {
+ const stepStart = md.indexOf('');
+ const stepEnd = md.indexOf('', stepStart);
+ const step = md.slice(stepStart, stepEnd);
+ for (const level of ['blocker', 'major', 'minor', 'cosmetic']) {
+ assert.match(step, new RegExp(level),
+ `severity taxonomy must include "${level}" to stay aligned with verify-work.md's severity_inference`);
+ }
+ });
+});
diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json
index 726d6185b..bddb14e05 100644
--- a/tests/fixtures/golden-install-parity/antigravity.json
+++ b/tests/fixtures/golden-install-parity/antigravity.json
@@ -200,7 +200,7 @@
"gsd-core/workflows/add-backlog.md": "8cc2e884a021b79b",
"gsd-core/workflows/add-phase.md": "f82afa2f93be19fa",
"gsd-core/workflows/add-tests.md": "ad6d49634e04ca7c",
- "gsd-core/workflows/add-todo.md": "de1ac76acfcc0133",
+ "gsd-core/workflows/add-todo.md": "e410a72c168e43c0",
"gsd-core/workflows/ai-integration-phase.md": "22f5466085c47c00",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "3ecffffe78021e0a",
diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json
index 0f8d8ba50..5a27a26e0 100644
--- a/tests/fixtures/golden-install-parity/augment.json
+++ b/tests/fixtures/golden-install-parity/augment.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
- "gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
+ "gsd-core/workflows/add-todo.md": "953314cdfef56bc4",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/claude-local.json b/tests/fixtures/golden-install-parity/claude-local.json
index 3ab9268b9..928b2ea72 100644
--- a/tests/fixtures/golden-install-parity/claude-local.json
+++ b/tests/fixtures/golden-install-parity/claude-local.json
@@ -270,7 +270,7 @@
"gsd-core/workflows/add-backlog.md": "8d05775f367d1e48",
"gsd-core/workflows/add-phase.md": "7285e6e8894a41c5",
"gsd-core/workflows/add-tests.md": "ffbaf55a25f455a2",
- "gsd-core/workflows/add-todo.md": "1fc850476cd8340d",
+ "gsd-core/workflows/add-todo.md": "0a0f4d718ea50bea",
"gsd-core/workflows/ai-integration-phase.md": "3503f52a7356caf0",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "816c71b0ef2d8c1d",
diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json
index 35b1bbd21..6f91d499d 100644
--- a/tests/fixtures/golden-install-parity/claude.json
+++ b/tests/fixtures/golden-install-parity/claude.json
@@ -199,7 +199,7 @@
"gsd-core/workflows/add-backlog.md": "485c4b6673ddf832",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "c4b133a102d36a27",
- "gsd-core/workflows/add-todo.md": "8488f10f56ac24ca",
+ "gsd-core/workflows/add-todo.md": "2c5176263ad4ae8f",
"gsd-core/workflows/ai-integration-phase.md": "a898d99b8d844215",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json
index 85a84bb71..c9bf87995 100644
--- a/tests/fixtures/golden-install-parity/cline.json
+++ b/tests/fixtures/golden-install-parity/cline.json
@@ -203,7 +203,7 @@
"gsd-core/workflows/add-backlog.md": "6c4393113fa9d209",
"gsd-core/workflows/add-phase.md": "cac9b86f66ab26f5",
"gsd-core/workflows/add-tests.md": "0354376bffdc0080",
- "gsd-core/workflows/add-todo.md": "b71e80304fe484eb",
+ "gsd-core/workflows/add-todo.md": "aa89018230c87f13",
"gsd-core/workflows/ai-integration-phase.md": "5159c6bdf102f74b",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "9bb427cd3b4075d5",
diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json
index 495457431..998851fd9 100644
--- a/tests/fixtures/golden-install-parity/codebuddy.json
+++ b/tests/fixtures/golden-install-parity/codebuddy.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
- "gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
+ "gsd-core/workflows/add-todo.md": "953314cdfef56bc4",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json
index 6403b27dc..9b55870c7 100644
--- a/tests/fixtures/golden-install-parity/codex.json
+++ b/tests/fixtures/golden-install-parity/codex.json
@@ -306,7 +306,7 @@
"gsd-core/workflows/add-backlog.md": "62431b27424ff1ff",
"gsd-core/workflows/add-phase.md": "27f282bf5b8af59d",
"gsd-core/workflows/add-tests.md": "5cdb15508c4b6075",
- "gsd-core/workflows/add-todo.md": "097e171f797737df",
+ "gsd-core/workflows/add-todo.md": "776e554602e3d1b7",
"gsd-core/workflows/ai-integration-phase.md": "1dfa15d8f28c022d",
"gsd-core/workflows/analyze-dependencies.md": "f799abc00907377f",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json
index 0921d6cb4..39abc4365 100644
--- a/tests/fixtures/golden-install-parity/copilot.json
+++ b/tests/fixtures/golden-install-parity/copilot.json
@@ -201,7 +201,7 @@
"gsd-core/workflows/add-backlog.md": "98f2dbb7c7cc93c2",
"gsd-core/workflows/add-phase.md": "34e6dd38c25b5b61",
"gsd-core/workflows/add-tests.md": "7f5da4d2a9fa8a1a",
- "gsd-core/workflows/add-todo.md": "10253591112b7d06",
+ "gsd-core/workflows/add-todo.md": "f86f4efa25a5d0d6",
"gsd-core/workflows/ai-integration-phase.md": "ef0c2474cee76b00",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "9fdfe8a7fbcd1417",
diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json
index d2b540eb0..3f1f6e7a5 100644
--- a/tests/fixtures/golden-install-parity/cursor.json
+++ b/tests/fixtures/golden-install-parity/cursor.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "2926457ed07b4500",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "37f031c4b3236a32",
- "gsd-core/workflows/add-todo.md": "d483ebe8edcf193a",
+ "gsd-core/workflows/add-todo.md": "1ee0525664fb062f",
"gsd-core/workflows/ai-integration-phase.md": "b79f320d59a68773",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json
index 8955f3477..584d48859 100644
--- a/tests/fixtures/golden-install-parity/hermes.json
+++ b/tests/fixtures/golden-install-parity/hermes.json
@@ -200,7 +200,7 @@
"gsd-core/workflows/add-backlog.md": "79a453a2270483fb",
"gsd-core/workflows/add-phase.md": "b4c4b32c111a116a",
"gsd-core/workflows/add-tests.md": "02f9e11ad2371c00",
- "gsd-core/workflows/add-todo.md": "5fe3ddc3227e0e22",
+ "gsd-core/workflows/add-todo.md": "8ba173153be63a40",
"gsd-core/workflows/ai-integration-phase.md": "ddab2912d025db65",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "2f21bd575bcedaf1",
diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json
index 5690e9054..2574ab408 100644
--- a/tests/fixtures/golden-install-parity/kilo.json
+++ b/tests/fixtures/golden-install-parity/kilo.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "485c4b6673ddf832",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "6e54f3d71a202671",
- "gsd-core/workflows/add-todo.md": "5082510bf0449204",
+ "gsd-core/workflows/add-todo.md": "31bc520d77b14139",
"gsd-core/workflows/ai-integration-phase.md": "c85c6afdc64f0da6",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json
index 887ae42b6..583838a3b 100644
--- a/tests/fixtures/golden-install-parity/kimi.json
+++ b/tests/fixtures/golden-install-parity/kimi.json
@@ -264,7 +264,7 @@
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
- "gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
+ "gsd-core/workflows/add-todo.md": "953314cdfef56bc4",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json
index 369592b74..47cb7a0e3 100644
--- a/tests/fixtures/golden-install-parity/opencode.json
+++ b/tests/fixtures/golden-install-parity/opencode.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "08289088a88d410f",
"gsd-core/workflows/add-phase.md": "5154a5fb3e0d4370",
"gsd-core/workflows/add-tests.md": "b4deb88c74d491c7",
- "gsd-core/workflows/add-todo.md": "72fcef9fd0cafda5",
+ "gsd-core/workflows/add-todo.md": "36b6d7fec9631ec7",
"gsd-core/workflows/ai-integration-phase.md": "90e5f97018715b18",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "2871c5d0a4b671fa",
diff --git a/tests/fixtures/golden-install-parity/pi.json b/tests/fixtures/golden-install-parity/pi.json
index cd7fd0e54..6987ec514 100644
--- a/tests/fixtures/golden-install-parity/pi.json
+++ b/tests/fixtures/golden-install-parity/pi.json
@@ -167,7 +167,7 @@
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
- "gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
+ "gsd-core/workflows/add-todo.md": "953314cdfef56bc4",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json
index acd3549a2..9e4c29dd0 100644
--- a/tests/fixtures/golden-install-parity/qwen.json
+++ b/tests/fixtures/golden-install-parity/qwen.json
@@ -200,7 +200,7 @@
"gsd-core/workflows/add-backlog.md": "cee41d5fc52d4e37",
"gsd-core/workflows/add-phase.md": "cc63a3e108c9a065",
"gsd-core/workflows/add-tests.md": "7cd945118e83246e",
- "gsd-core/workflows/add-todo.md": "5e037b444e657557",
+ "gsd-core/workflows/add-todo.md": "83426131a56b8e77",
"gsd-core/workflows/ai-integration-phase.md": "afdc7c15f03a95fc",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "aaa1b74e001322b6",
diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json
index 81c8d0b25..195556b23 100644
--- a/tests/fixtures/golden-install-parity/trae.json
+++ b/tests/fixtures/golden-install-parity/trae.json
@@ -200,7 +200,7 @@
"gsd-core/workflows/add-backlog.md": "1851e97996d445d5",
"gsd-core/workflows/add-phase.md": "09bdebbc62f3310e",
"gsd-core/workflows/add-tests.md": "8ad08d29415f8f43",
- "gsd-core/workflows/add-todo.md": "0fe07cbd29ec252b",
+ "gsd-core/workflows/add-todo.md": "fefa923ed596225f",
"gsd-core/workflows/ai-integration-phase.md": "d469eb120e52de0f",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "76607add3257cb37",
diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json
index 993ac9591..6b339eb8b 100644
--- a/tests/fixtures/golden-install-parity/windsurf.json
+++ b/tests/fixtures/golden-install-parity/windsurf.json
@@ -200,7 +200,7 @@
"gsd-core/workflows/add-backlog.md": "61169973a10b1321",
"gsd-core/workflows/add-phase.md": "f9b1f7cc229b57f8",
"gsd-core/workflows/add-tests.md": "d51e69bb433535a4",
- "gsd-core/workflows/add-todo.md": "cab0d8215579fbdd",
+ "gsd-core/workflows/add-todo.md": "50d17053c6cfbc31",
"gsd-core/workflows/ai-integration-phase.md": "8948988717506320",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
"gsd-core/workflows/audit-fix.md": "aa052e15623d759d",
diff --git a/tests/fixtures/golden-install-parity/zcode.json b/tests/fixtures/golden-install-parity/zcode.json
index 739e11b42..4c920fa53 100644
--- a/tests/fixtures/golden-install-parity/zcode.json
+++ b/tests/fixtures/golden-install-parity/zcode.json
@@ -271,7 +271,7 @@
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
- "gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
+ "gsd-core/workflows/add-todo.md": "953314cdfef56bc4",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
"gsd-core/workflows/audit-fix.md": "eedb2da4bffb7575",
diff --git a/tests/init.test.cjs b/tests/init.test.cjs
index 635050878..abba1e92b 100644
--- a/tests/init.test.cjs
+++ b/tests/init.test.cjs
@@ -1013,6 +1013,28 @@ describe('cmdInitTodos', () => {
assert.strictEqual(task1.path, '.planning/todos/pending/task-1.md');
});
+ // ── #2337: init todos must surface severity too, in parity with list-todos ──
+ test('surfaces severity when present, omits the key when absent (#2337)', () => {
+ const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending');
+ fs.mkdirSync(pendingDir, { recursive: true });
+
+ fs.writeFileSync(path.join(pendingDir, 'tagged.md'),
+ 'title: Crash on save\narea: core\ncreated: 2026-02-25\nseverity: blocker');
+ fs.writeFileSync(path.join(pendingDir, 'untagged.md'),
+ 'title: Old todo\narea: docs\ncreated: 2026-02-24');
+
+ const result = runGsdTools('init todos', tmpDir);
+ assert.ok(result.success, `Command failed: ${result.error}`);
+
+ const output = JSON.parse(result.output);
+ const tagged = output.todos.find(t => t.file === 'tagged.md');
+ const untagged = output.todos.find(t => t.file === 'untagged.md');
+ assert.ok(tagged && untagged, 'both todos should be present');
+ assert.strictEqual(tagged.severity, 'blocker', 'severity surfaced verbatim when present');
+ assert.ok(!('severity' in untagged),
+ 'severity key ABSENT (backward compatible) for a todo with no severity line');
+ });
+
test('area filter returns only matching todos', () => {
const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending');
fs.mkdirSync(pendingDir, { recursive: true });
diff --git a/tests/workflow-size-baseline.json b/tests/workflow-size-baseline.json
index 059b7f92f..e9c0a3940 100644
--- a/tests/workflow-size-baseline.json
+++ b/tests/workflow-size-baseline.json
@@ -2,7 +2,7 @@
"add-backlog.md": 7176,
"add-phase.md": 7247,
"add-tests.md": 16961,
- "add-todo.md": 8996,
+ "add-todo.md": 10734,
"ai-integration-phase.md": 14805,
"analyze-dependencies.md": 3887,
"audit-fix.md": 11717,