@brunosps00/dev-workflow 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +31 -0
  3. package/README.md +24 -4
  4. package/lib/build-plugin.js +79 -0
  5. package/lib/constants.js +19 -14
  6. package/lib/doctor.js +33 -0
  7. package/lib/hooks.js +179 -0
  8. package/lib/init.js +68 -0
  9. package/lib/skill-registry.js +13 -0
  10. package/lib/uninstall.js +22 -3
  11. package/lib/validate.js +62 -0
  12. package/lib/wrappers.js +1 -1
  13. package/package.json +6 -1
  14. package/scaffold/config/stack-mappings.json +40 -0
  15. package/scaffold/en/commands/dw-analyze-project.md +5 -1
  16. package/scaffold/en/commands/dw-context-budget.md +9 -0
  17. package/scaffold/en/commands/dw-harness-audit.md +3 -0
  18. package/scaffold/en/commands/dw-learn.md +41 -0
  19. package/scaffold/en/commands/dw-plan.md +3 -0
  20. package/scaffold/en/commands/dw-review.md +2 -0
  21. package/scaffold/en/commands/dw-run.md +2 -0
  22. package/scaffold/en/commands/dw-update.md +4 -1
  23. package/scaffold/pt-br/commands/dw-analyze-project.md +5 -1
  24. package/scaffold/pt-br/commands/dw-context-budget.md +9 -0
  25. package/scaffold/pt-br/commands/dw-harness-audit.md +3 -0
  26. package/scaffold/pt-br/commands/dw-learn.md +41 -0
  27. package/scaffold/pt-br/commands/dw-plan.md +3 -0
  28. package/scaffold/pt-br/commands/dw-review.md +2 -0
  29. package/scaffold/pt-br/commands/dw-run.md +2 -0
  30. package/scaffold/pt-br/commands/dw-update.md +4 -1
  31. package/scaffold/rules/README.md +22 -0
  32. package/scaffold/rules/common.md +29 -0
  33. package/scaffold/rules/csharp.md +18 -0
  34. package/scaffold/rules/python.md +18 -0
  35. package/scaffold/rules/react.md +18 -0
  36. package/scaffold/rules/rust.md +18 -0
  37. package/scaffold/rules/typescript.md +23 -0
  38. package/scaffold/rules-readme.md +8 -0
  39. package/scaffold/scripts/hooks/git-guardrails.mjs +79 -0
  40. package/scaffold/scripts/hooks/session-cost.mjs +126 -0
  41. package/scaffold/scripts/hooks/statusline.mjs +118 -0
  42. package/scaffold/scripts/lib/model-prices.json +12 -0
  43. package/scaffold/skill-registry.json +27 -0
  44. package/scaffold/skills/dw-cli-run/SKILL.md +3 -1
  45. package/scaffold/skills/dw-codebase-intel/SKILL.md +3 -1
  46. package/scaffold/skills/dw-debug-protocol/SKILL.md +4 -0
  47. package/scaffold/skills/dw-debug-protocol/references/agent-degradation.md +30 -0
  48. package/scaffold/skills/dw-execute-phase/SKILL.md +13 -0
  49. package/scaffold/skills/dw-git-discipline/SKILL.md +1 -0
  50. package/scaffold/skills/dw-llm-eval/SKILL.md +12 -0
  51. package/scaffold/skills/dw-memory/SKILL.md +12 -1
  52. package/scaffold/skills/dw-memory/references/instincts.md +45 -0
  53. package/scaffold/skills/dw-minimalism/SKILL.md +98 -0
  54. package/scaffold/skills/dw-review-rigor/SKILL.md +12 -0
  55. package/scaffold/skills/dw-review-rigor/references/false-positives.md +21 -0
  56. package/scaffold/skills/dw-review-rigor/references/self-eval-rubric.md +38 -0
  57. package/scaffold/skills/dw-silent-failure/SKILL.md +4 -2
  58. package/scaffold/skills/dw-simplification/SKILL.md +13 -0
  59. package/scaffold/skills/dw-simplification/references/dead-code-tools.md +23 -0
  60. package/scaffold/skills/dw-testing-discipline/SKILL.md +17 -0
  61. package/scaffold/skills/dw-ui-discipline/SKILL.md +1 -1
  62. package/scaffold/skills/dw-verify/SKILL.md +1 -1
  63. package/scaffold/skills/dw-verify/references/pre-check-and-status.md +28 -0
@@ -0,0 +1,18 @@
1
+ # Baseline rules — Python (extends common)
2
+
3
+ ## Style
4
+ - Type hints on public functions; run mypy/pyright in CI. `@dataclass(frozen=True)` for value types.
5
+ - Follow PEP 8; format with black/ruff. Explicit over implicit.
6
+ - Context managers (`with`) for every resource (files, connections, locks).
7
+
8
+ ## Errors
9
+ - Catch the narrowest exception; never bare `except:`. Re-raise with context (`raise ... from e`).
10
+ - Don't return `None` to signal failure where an exception or a typed result is clearer.
11
+
12
+ ## Testing
13
+ - pytest; fixtures over setup/teardown boilerplate. Parametrize input variations.
14
+ - Test behavior; avoid asserting internal call counts unless the calls ARE the contract.
15
+
16
+ ## Security
17
+ - Parameterized queries only — never f-string/format SQL. Normalize/validate input at the edge.
18
+ - `secrets` for tokens, not `random`. Never log credentials or PII.
@@ -0,0 +1,18 @@
1
+ # Baseline rules — React (extends common + typescript)
2
+
3
+ ## Components
4
+ - Function components + hooks only. Keep components small; lift state only as far as it's shared.
5
+ - Derive, don't duplicate, state. Compute during render instead of syncing via effects.
6
+ - List keys are stable ids, never the array index for dynamic lists.
7
+
8
+ ## Effects & data
9
+ - `useEffect` synchronizes with external systems — not for deriving values or handling events.
10
+ - Data fetching via a library (React Query / RTK Query / route loader) over ad-hoc effects; handle loading/error/empty.
11
+
12
+ ## Rendering
13
+ - Every list/screen designs its empty, loading, and error states (see `dw-ui-discipline`).
14
+ - Memoize (`useMemo`/`useCallback`/`memo`) only after measuring a real cost — not by default.
15
+
16
+ ## Security & a11y
17
+ - Never `dangerouslySetInnerHTML` with unsanitized input; escape user content.
18
+ - Semantic HTML + WCAG 2.2 AA floor: labels, focus-visible, keyboard nav, contrast (see `dw-ui-discipline`).
@@ -0,0 +1,18 @@
1
+ # Baseline rules — Rust (extends common)
2
+
3
+ ## Style
4
+ - `Result<T, E>` for fallible ops; reserve `panic!`/`unwrap`/`expect` for truly unrecoverable code or tests.
5
+ - Prefer borrowing over cloning; clone deliberately, not to silence the borrow checker.
6
+ - Model the domain with enums + exhaustive `match`; make illegal states unrepresentable.
7
+
8
+ ## Errors
9
+ - Propagate with `?`; enrich context (`thiserror` for libs, `anyhow` for apps) at boundaries.
10
+ - No silent `let _ =` on a real error.
11
+
12
+ ## Testing
13
+ - `#[cfg(test)]` unit tests near code; integration tests in `tests/`. Test the public API.
14
+ - Meaningful `assert_eq!` messages; property tests (proptest) for invariants.
15
+
16
+ ## Security
17
+ - Avoid `unsafe`; when unavoidable, encapsulate it and comment the invariants it upholds.
18
+ - Don't `unwrap` on data you don't control; validate external input.
@@ -0,0 +1,23 @@
1
+ # Baseline rules — TypeScript (extends common)
2
+
3
+ ## Types
4
+ - `strict: true`. No `any` — use `unknown` + narrowing, generics, or a precise type. `as` casts are a smell.
5
+ - Make illegal states unrepresentable: discriminated unions over boolean flags; `readonly` for immutability.
6
+ - Export the narrowest surface; keep public types precise at boundaries.
7
+
8
+ ## Style
9
+ - `const` by default; `let` only when reassigned; never `var`.
10
+ - `async`/`await` over raw promise chains; always handle rejection.
11
+ - Immutability via spread / `Readonly<T>`; avoid in-place array/object mutation.
12
+
13
+ ## Errors
14
+ - Model expected failures as values (`Result`/union) where it aids callers; throw for truly exceptional.
15
+ - Never `catch (e) {}` — at minimum log with context and rethrow or handle.
16
+
17
+ ## Testing
18
+ - Vitest/Jest; type-level tests where a public generic contract matters.
19
+ - Assert exported behavior, not private internals.
20
+
21
+ ## Security
22
+ - Validate external input with a schema (zod/valibot) at the boundary; `as` does not enforce shape at runtime.
23
+ - No `eval`/`Function` on user input; sanitize before `innerHTML`.
@@ -23,3 +23,11 @@ Run the `/dw-analyze-project` command inside your AI assistant to scan your code
23
23
  These rules are automatically read by workflow commands (`create-prd`, `create-techspec`, `run-task`, etc.) to ensure generated artifacts follow your project's conventions.
24
24
 
25
25
  Re-run `/dw-analyze-project` whenever your stack or conventions change significantly.
26
+
27
+ ## Related: the curated baseline library
28
+
29
+ This directory (`.dw/rules/`) is **analytical** — what the code IS. It is complemented by
30
+ `.dw/rules-library/` — a **curated declarative baseline** (what good code SHOULD look like) shipped per
31
+ stack, loaded lazily via `.dw/config/stack-mappings.json` — and by `.dw/constitution.md` — the principles
32
+ your team commits to. `/dw-analyze-project` references the library to seed constitution proposals; you don't
33
+ edit the library here. See `.dw/rules-library/README.md`.
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-workflow git guardrails — Claude Code PreToolUse hook (matcher: Bash).
4
+ *
5
+ * Blocks irreversible git commands (force push, hard reset, clean -f, branch
6
+ * deletion, remote branch deletion). Everything else is allowed.
7
+ *
8
+ * Enforces the destructive-command rules from the `dw-git-discipline` skill at
9
+ * the harness level instead of relying on prose. Inspired by mattpocock/skills
10
+ * `git-guardrails-claude-code` (MIT).
11
+ *
12
+ * Contract: reads the PreToolUse payload as JSON on stdin, emits a
13
+ * permissionDecision via hookSpecificOutput. Fails OPEN — any parsing/runtime
14
+ * error allows the command, so a hook bug never blocks the user.
15
+ */
16
+
17
+ const DANGEROUS = [
18
+ { re: /\bgit\s+push\b[^\n|&;]*(--force\b|--force-with-lease\b|\s-f\b)/, why: 'force push rewrites remote history' },
19
+ { re: /\bgit\s+push\b[^\n|&;]*(--delete\b|\s-d\b|\s:\S)/, why: 'deletes a remote branch' },
20
+ { re: /\bgit\s+reset\b[^\n|&;]*--hard\b/, why: 'hard reset discards uncommitted work and rewrites the branch' },
21
+ { re: /\bgit\s+clean\b[^\n|&;]*-[a-z]*f/, why: 'git clean -f permanently deletes untracked files' },
22
+ { re: /\bgit\s+branch\b[^\n|&;]*\s-D\b/, why: 'force-deletes a branch that may be unmerged' },
23
+ { re: /\bgit\s+checkout\b[^\n|&;]*\s(--\s+\.|\.\s*$)/, why: 'discards all local changes in the working tree' },
24
+ ];
25
+
26
+ function readStdin() {
27
+ return new Promise((resolve) => {
28
+ let data = '';
29
+ if (process.stdin.isTTY) return resolve('');
30
+ process.stdin.setEncoding('utf-8');
31
+ process.stdin.on('data', (chunk) => (data += chunk));
32
+ process.stdin.on('end', () => resolve(data));
33
+ process.stdin.on('error', () => resolve(''));
34
+ });
35
+ }
36
+
37
+ function allow() {
38
+ process.exit(0);
39
+ }
40
+
41
+ function deny(reason) {
42
+ process.stdout.write(
43
+ JSON.stringify({
44
+ hookSpecificOutput: {
45
+ hookEventName: 'PreToolUse',
46
+ permissionDecision: 'deny',
47
+ permissionDecisionReason: reason,
48
+ },
49
+ }) + '\n',
50
+ );
51
+ process.exit(0);
52
+ }
53
+
54
+ async function main() {
55
+ let payload;
56
+ try {
57
+ payload = JSON.parse(await readStdin());
58
+ } catch {
59
+ return allow(); // fail open
60
+ }
61
+
62
+ const command = payload && payload.tool_input && typeof payload.tool_input.command === 'string'
63
+ ? payload.tool_input.command
64
+ : '';
65
+ if (!command) return allow();
66
+
67
+ for (const rule of DANGEROUS) {
68
+ if (rule.re.test(command)) {
69
+ return deny(
70
+ `Blocked by dev-workflow git guardrails: ${rule.why}. ` +
71
+ `If this is intentional, run it yourself outside the agent, or adjust .claude/settings.json hooks.`,
72
+ );
73
+ }
74
+ }
75
+
76
+ return allow();
77
+ }
78
+
79
+ main().catch(() => allow());
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-workflow session cost tracker — Claude Code SessionEnd hook.
4
+ *
5
+ * On session end, parses the session transcript (JSONL at `transcript_path`),
6
+ * sums token usage per model, estimates USD via ../lib/model-prices.json, and
7
+ * appends ONE cumulative row to .dw/metrics/costs.jsonl. The statusline and
8
+ * `/dw-context-budget` (Part B) read that file to report real spend.
9
+ *
10
+ * Token counts are what Claude Code records per assistant turn (each turn bills
11
+ * the full input, mostly cache_read); summing across turns matches billing.
12
+ * USD is a best-effort ESTIMATE from a local price table — tokens are exact.
13
+ *
14
+ * Contract: reads the SessionEnd payload as JSON on stdin. Fails SAFE — any
15
+ * error exits 0 without writing, so a hook bug never disrupts the session.
16
+ */
17
+
18
+ import { readFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
19
+ import { join, dirname, resolve } from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+
22
+ const HOOK_DIR = dirname(fileURLToPath(import.meta.url)); // <root>/.dw/scripts/hooks
23
+ const DW_DIR = resolve(HOOK_DIR, '..', '..'); // <root>/.dw
24
+ const PRICES_PATH = join(HOOK_DIR, '..', 'lib', 'model-prices.json'); // .dw/scripts/lib/
25
+
26
+ function readStdin() {
27
+ return new Promise((resolve_) => {
28
+ let data = '';
29
+ if (process.stdin.isTTY) return resolve_('');
30
+ process.stdin.setEncoding('utf-8');
31
+ process.stdin.on('data', (c) => (data += c));
32
+ process.stdin.on('end', () => resolve_(data));
33
+ process.stdin.on('error', () => resolve_(''));
34
+ });
35
+ }
36
+
37
+ function loadPrices() {
38
+ try {
39
+ return JSON.parse(readFileSync(PRICES_PATH, 'utf-8')).models || {};
40
+ } catch {
41
+ return {};
42
+ }
43
+ }
44
+
45
+ function priceFor(models, model) {
46
+ return models[model] || models['_default'] || { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 };
47
+ }
48
+
49
+ // Sum per-turn usage from the transcript, grouped by model.
50
+ function tallyTranscript(transcriptPath) {
51
+ const byModel = {};
52
+ let raw;
53
+ try {
54
+ raw = readFileSync(transcriptPath, 'utf-8');
55
+ } catch {
56
+ return byModel;
57
+ }
58
+ for (const line of raw.split('\n')) {
59
+ if (!line.trim()) continue;
60
+ let obj;
61
+ try {
62
+ obj = JSON.parse(line);
63
+ } catch {
64
+ continue;
65
+ }
66
+ const msg = obj && obj.message;
67
+ const u = msg && msg.usage;
68
+ if (!u || obj.type !== 'assistant') continue;
69
+ const model = (msg && msg.model) || '_unknown';
70
+ const m = (byModel[model] ||= { input: 0, output: 0, cache_write: 0, cache_read: 0 });
71
+ m.input += u.input_tokens || 0;
72
+ m.output += u.output_tokens || 0;
73
+ m.cache_write += u.cache_creation_input_tokens || 0;
74
+ m.cache_read += u.cache_read_input_tokens || 0;
75
+ }
76
+ return byModel;
77
+ }
78
+
79
+ async function main() {
80
+ let payload;
81
+ try {
82
+ payload = JSON.parse(await readStdin());
83
+ } catch {
84
+ return process.exit(0); // fail safe
85
+ }
86
+ const transcriptPath = payload && payload.transcript_path;
87
+ if (!transcriptPath || !existsSync(transcriptPath)) return process.exit(0);
88
+
89
+ const byModel = tallyTranscript(transcriptPath);
90
+ const modelNames = Object.keys(byModel);
91
+ if (modelNames.length === 0) return process.exit(0); // nothing to record
92
+
93
+ const prices = loadPrices();
94
+ let totalUsd = 0;
95
+ let totalTokens = 0;
96
+ const models = {};
97
+ for (const name of modelNames) {
98
+ const t = byModel[name];
99
+ const p = priceFor(prices, name);
100
+ const usd =
101
+ (t.input * p.input + t.output * p.output + t.cache_write * p.cache_write + t.cache_read * p.cache_read) / 1e6;
102
+ totalUsd += usd;
103
+ totalTokens += t.input + t.output + t.cache_write + t.cache_read;
104
+ models[name] = { ...t, usd: Number(usd.toFixed(4)) };
105
+ }
106
+
107
+ const row = {
108
+ ts: new Date().toISOString(),
109
+ session_id: (payload && payload.session_id) || null,
110
+ reason: (payload && payload.reason) || null,
111
+ total_usd: Number(totalUsd.toFixed(4)),
112
+ total_tokens: totalTokens,
113
+ models,
114
+ };
115
+
116
+ try {
117
+ const metricsDir = join(DW_DIR, 'metrics');
118
+ if (!existsSync(metricsDir)) mkdirSync(metricsDir, { recursive: true });
119
+ appendFileSync(join(metricsDir, 'costs.jsonl'), JSON.stringify(row) + '\n');
120
+ } catch {
121
+ /* fail safe — never disrupt the session */
122
+ }
123
+ process.exit(0);
124
+ }
125
+
126
+ main().catch(() => process.exit(0));
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-workflow statusline — Claude Code statusLine command.
4
+ *
5
+ * Prints one line: git branch · dev-workflow active spec (if any) · minimalism
6
+ * mode. Reads the session payload (JSON on stdin) for the working dir; falls
7
+ * back to process.cwd(). Fails SAFE — any error prints a minimal line so the
8
+ * statusline never breaks the session.
9
+ *
10
+ * Minimalism mode is read from .dw/minimalism.json (see the dw-minimalism skill).
11
+ */
12
+
13
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { execSync } from 'node:child_process';
16
+
17
+ function readStdin() {
18
+ return new Promise((resolve) => {
19
+ let data = '';
20
+ if (process.stdin.isTTY) return resolve('');
21
+ process.stdin.setEncoding('utf-8');
22
+ process.stdin.on('data', (c) => (data += c));
23
+ process.stdin.on('end', () => resolve(data));
24
+ process.stdin.on('error', () => resolve(''));
25
+ });
26
+ }
27
+
28
+ function gitBranch(cwd) {
29
+ try {
30
+ return execSync('git rev-parse --abbrev-ref HEAD', {
31
+ cwd,
32
+ stdio: ['ignore', 'pipe', 'ignore'],
33
+ })
34
+ .toString()
35
+ .trim();
36
+ } catch {
37
+ return '';
38
+ }
39
+ }
40
+
41
+ function minimalismMode(cwd) {
42
+ try {
43
+ const file = join(cwd, '.dw', 'minimalism.json');
44
+ if (!existsSync(file)) return 'full';
45
+ const mode = JSON.parse(readFileSync(file, 'utf-8')).mode;
46
+ return typeof mode === 'string' ? mode : 'full';
47
+ } catch {
48
+ return 'full';
49
+ }
50
+ }
51
+
52
+ function costToday(cwd) {
53
+ // Sum today's estimated spend from .dw/metrics/costs.jsonl (written by the
54
+ // session-cost SessionEnd hook). Dedupe by session (latest row wins). Fail SAFE.
55
+ try {
56
+ const file = join(cwd, '.dw', 'metrics', 'costs.jsonl');
57
+ if (!existsSync(file)) return '';
58
+ const today = new Date().toISOString().slice(0, 10);
59
+ const bySession = new Map();
60
+ for (const line of readFileSync(file, 'utf-8').split('\n')) {
61
+ if (!line.trim()) continue;
62
+ let r;
63
+ try {
64
+ r = JSON.parse(line);
65
+ } catch {
66
+ continue;
67
+ }
68
+ if (!r || typeof r.total_usd !== 'number' || !r.ts) continue;
69
+ const key = r.session_id || r.ts;
70
+ const prev = bySession.get(key);
71
+ if (!prev || r.ts > prev.ts) bySession.set(key, { ts: r.ts, usd: r.total_usd });
72
+ }
73
+ let sum = 0;
74
+ for (const v of bySession.values()) if (v.ts.slice(0, 10) === today) sum += v.usd;
75
+ return sum > 0 ? `~$${sum.toFixed(2)}/d` : '';
76
+ } catch {
77
+ return '';
78
+ }
79
+ }
80
+
81
+ function activeSpec(cwd) {
82
+ try {
83
+ const specDir = join(cwd, '.dw', 'spec');
84
+ if (!existsSync(specDir)) return '';
85
+ const dirs = readdirSync(specDir, { withFileTypes: true })
86
+ .filter((e) => e.isDirectory())
87
+ .map((e) => e.name);
88
+ return dirs.length ? `spec:${dirs.length}` : '';
89
+ } catch {
90
+ return '';
91
+ }
92
+ }
93
+
94
+ async function main() {
95
+ let cwd = process.cwd();
96
+ try {
97
+ const payload = JSON.parse(await readStdin());
98
+ cwd =
99
+ (payload && payload.workspace && payload.workspace.current_dir) ||
100
+ (payload && payload.cwd) ||
101
+ cwd;
102
+ } catch {
103
+ /* use process.cwd() */
104
+ }
105
+
106
+ const parts = [];
107
+ const branch = gitBranch(cwd);
108
+ if (branch) parts.push(`⎇ ${branch}`);
109
+ const spec = activeSpec(cwd);
110
+ if (spec) parts.push(spec);
111
+ parts.push(`min:${minimalismMode(cwd)}`);
112
+ const cost = costToday(cwd);
113
+ if (cost) parts.push(cost);
114
+
115
+ process.stdout.write(`dw · ${parts.join(' · ')}`);
116
+ }
117
+
118
+ main().catch(() => process.stdout.write('dw'));
@@ -0,0 +1,12 @@
1
+ {
2
+ "_note": "USD per 1M tokens. Best-effort estimates for cost display only; token counts are exact. Sync from the claude-api skill when prices change. Unknown models fall back to '_default'. cache_write ~= 1.25x input, cache_read ~= 0.1x input (standard Anthropic caching).",
3
+ "_updated": "2026-07-01",
4
+ "models": {
5
+ "claude-opus-4-8": { "input": 15, "output": 75, "cache_write": 18.75, "cache_read": 1.5 },
6
+ "claude-opus-4-8[1m]": { "input": 15, "output": 75, "cache_write": 18.75, "cache_read": 1.5 },
7
+ "claude-sonnet-4-6": { "input": 3, "output": 15, "cache_write": 3.75, "cache_read": 0.3 },
8
+ "claude-haiku-4-5-20251001": { "input": 1, "output": 5, "cache_write": 1.25, "cache_read": 0.1 },
9
+ "claude-fable-5": { "input": 3, "output": 15, "cache_write": 3.75, "cache_read": 0.3 },
10
+ "_default": { "input": 3, "output": 15, "cache_write": 3.75, "cache_read": 0.3 }
11
+ }
12
+ }
@@ -10,6 +10,8 @@
10
10
  "expected_output": "A selected API testing recipe and evidence path for the active stack.",
11
11
  "load_policy": "lazy-references",
12
12
  "context_limit": 12000,
13
+ "invocation": "explicit",
14
+ "exportable": true,
13
15
  "bundled": true
14
16
  },
15
17
  {
@@ -21,6 +23,8 @@
21
23
  "expected_output": "A selected service recipe and composition guidance.",
22
24
  "load_policy": "lazy-references",
23
25
  "context_limit": 12000,
26
+ "invocation": "explicit",
27
+ "exportable": true,
24
28
  "bundled": true
25
29
  },
26
30
  {
@@ -122,6 +126,19 @@
122
126
  "context_limit": 12000,
123
127
  "bundled": true
124
128
  },
129
+ {
130
+ "name": "dw-minimalism",
131
+ "kind": "protocol",
132
+ "tier": "core",
133
+ "owner": "dw-run,dw-plan,dw-review",
134
+ "trigger": "Before writing new code, infrastructure, or abstractions: YAGNI decision ladder and lite/full/ultra intensity",
135
+ "expected_output": "A minimalism verdict (build/reuse/inline/skip) with the ladder rung that justified it.",
136
+ "load_policy": "always-small",
137
+ "context_limit": 8000,
138
+ "invocation": "model",
139
+ "exportable": true,
140
+ "bundled": true
141
+ },
125
142
  {
126
143
  "name": "dw-review-rigor",
127
144
  "kind": "protocol",
@@ -142,6 +159,7 @@
142
159
  "expected_output": "Adopt/wrap/compose/build decision with checked search channels.",
143
160
  "load_policy": "always-small",
144
161
  "context_limit": 6000,
162
+ "exportable": true,
145
163
  "bundled": true
146
164
  },
147
165
  {
@@ -164,6 +182,7 @@
164
182
  "expected_output": "Simplification target, risk, behavior-preserving checks, and recommendation.",
165
183
  "load_policy": "lazy-references",
166
184
  "context_limit": 12000,
185
+ "exportable": true,
167
186
  "bundled": true
168
187
  },
169
188
  {
@@ -208,6 +227,7 @@
208
227
  "expected_output": "Fresh verification report with cited command evidence.",
209
228
  "load_policy": "always-small",
210
229
  "context_limit": 12000,
230
+ "exportable": true,
211
231
  "bundled": true
212
232
  },
213
233
  {
@@ -219,6 +239,8 @@
219
239
  "expected_output": "Rewritten prose with AI-writing tells removed and voice preserved.",
220
240
  "load_policy": "lazy-references",
221
241
  "context_limit": 6000,
242
+ "invocation": "explicit",
243
+ "exportable": true,
222
244
  "bundled": true
223
245
  },
224
246
  {
@@ -230,6 +252,8 @@
230
252
  "expected_output": "Selected Remotion rule references and implementation guidance.",
231
253
  "load_policy": "lazy-references",
232
254
  "context_limit": 8000,
255
+ "invocation": "explicit",
256
+ "exportable": true,
233
257
  "bundled": true
234
258
  },
235
259
  {
@@ -241,6 +265,7 @@
241
265
  "expected_output": "High-confidence security findings or explicit pass.",
242
266
  "load_policy": "lazy-references",
243
267
  "context_limit": 14000,
268
+ "exportable": true,
244
269
  "bundled": true
245
270
  },
246
271
  {
@@ -252,6 +277,8 @@
252
277
  "expected_output": "Selected performance rule references and metric-driven recommendations.",
253
278
  "load_policy": "lazy-references",
254
279
  "context_limit": 12000,
280
+ "invocation": "explicit",
281
+ "exportable": true,
255
282
  "bundled": true
256
283
  }
257
284
  ]
@@ -125,7 +125,9 @@ complete delivery). Do not skip it — it is the signal that decides gate vs esc
125
125
  - **Quality / no regression.**
126
126
  **Acceptance bar: score ≥9** (owner's decision). Bands: **≥9** = acceptable (ready for the human gate); **6–8** =
127
127
  `FINDINGS` → **escalate** to reach ≥9; **<6** = failure → **escalate**. `BLOCKED` if the ladder is exhausted
128
- without reaching 9. Always show the score + a short per-criterion rationale.
128
+ without reaching 9. Always show the score + a short per-criterion rationale — and for any criterion below its
129
+ ceiling, **cite the specific gap** (the failing test, the file left out of fence), not just a label. Same
130
+ evidence discipline as the five-axis rubric in `dw-review-rigor/references/self-eval-rubric.md`.
129
131
 
130
132
  ## Dual evaluation: the CLI auto-gates (MAX effort) → Claude re-gates (compares scores)
131
133
  The CLI **can't grade its own exam alone** — hence two layers. The CLI auto-gates cheaply (close to the work) and
@@ -80,7 +80,7 @@ For human-readable analysis (architecture overview, module conventions, anti-pat
80
80
  3. Search `files.json` for matching exports.
81
81
  4. Search `arch.md` (full-text) for the keyword.
82
82
  5. Cross-reference with `deps.json` if the query is about a library.
83
- 6. Return a structured answer with file paths cited.
83
+ 6. Return a structured answer with file paths cited, each tagged with a `relevance` (0–1, or high/medium/low) and a one-line reason — so the calling command reads the high-relevance files first and skips the rest, instead of loading the whole result set. Ordering by relevance is what keeps intel queries token-lean.
84
84
 
85
85
  If no `.dw/intel/` exists at all, `/dw-intel` falls back to `.dw/rules/` (seeded by `/dw-new-project` or `/dw-analyze-project`) and direct grep over the codebase.
86
86
 
@@ -105,6 +105,8 @@ If no `.dw/intel/` exists at all, `/dw-intel` falls back to `.dw/rules/` (seeded
105
105
 
106
106
  - **Iterative retrieval**: subagents should not load broad file sets blindly. They perform at most three search/evaluate/refine cycles, then read only high-relevance files.
107
107
  - **Codemaps**: `.dw/intel/codemaps/*.md` stores short maps optimized for agent context instead of forcing repeated source reads.
108
+ - **Data-flow tracing**: `arch.md` includes at least one end-to-end request lifecycle for a representative feature — entry → validation → auth → service → data → response, with the `file:function` at each hop — so agents learn *how data moves*, not just where files sit. This is the single highest-leverage entry for reducing broad exploratory reads.
109
+ - **Relevance-scored answers**: `/dw-intel` query results are ranked by relevance (see "How `/dw-intel` Reads This", step 6) so callers act on the top hits and skip the tail.
108
110
 
109
111
  ## Inspired by
110
112
 
@@ -70,6 +70,10 @@ Bugs cluster into a small number of categories. Knowing the category narrows whe
70
70
  | Configuration | Works dev, fails prod | Env vars, secrets, build flags, infra config |
71
71
  | Logic | Branch returns wrong result | Guard conditions, off-by-one, boolean polarity |
72
72
 
73
+ ## When the agent is the bug (not the code)
74
+
75
+ Sometimes the failure is in the agent's own run — it loops, drifts off the objective, claims an edit it never made, or its reasoning degraded — not in the code under test. That is a different diagnosis. See `references/agent-degradation.md` for the symptom→cause table and the ordered recovery heuristics (restate objective → verify world state → shrink scope → one discriminating check → retry once).
76
+
73
77
  ## Non-reproducible bugs
74
78
 
75
79
  Some bugs only happen in production, only on certain users, only at certain times. Don't fix them on intuition. The protocol in `references/non-reproducible-strategy.md` covers:
@@ -0,0 +1,30 @@
1
+ # Agent Degradation — when the AGENT is the bug, not the code
2
+
3
+ Use when the failure is in the agent's own run — it loops, drifts off the objective, "did" something it
4
+ didn't, or its reasoning quality collapsed — rather than in the code under test. Complements the six-step
5
+ triage (which assumes a code defect). Condensed from ECC's `agent-architecture-audit` (12-layer stack) and
6
+ `agent-introspection-debugging`. Also referenced by `/dw-harness-audit` for runtime triage.
7
+
8
+ ## Symptom → likely cause → first check
9
+
10
+ | Symptom | Likely cause | First check |
11
+ |---|---|---|
12
+ | Same command/tool call repeated; hits the tool-call ceiling | Loop / no exit condition | Inspect the last N tool calls for repetition; add a stop condition |
13
+ | Reasoning degrades, requirements dropped mid-run | Context overflow / bloat | Measure active context; drop unrelated loaded files; compact memory |
14
+ | Claims a file was written but it isn't there | Hallucinated execution, wrong cwd, or branch drift | Re-verify path, `pwd`, `git status`, the actual filesystem |
15
+ | "Fixed" but the tests still fail identically | Wrong hypothesis, never re-read production | Isolate the exact failing test; re-read the production code first |
16
+ | Answer contradicts the tool output it just received | Misread / ignored tool result | Add a post-tool validation step; quote the tool output before acting |
17
+ | Objective has quietly shifted from the original ask | Prompt drift across a long session | Restate the original objective in one sentence; compare to the current action |
18
+ | A silent second pass produced a different answer | Hidden repair / fallback loop | Audit for undeclared retry paths; make them explicit |
19
+ | Reuses a stale summary or cached artifact as fact | Distillation / persistence staleness | Check freshness timestamps; refresh the summary before trusting it |
20
+
21
+ ## Recovery heuristics (in order — cheapest and most diagnostic first)
22
+
23
+ 1. **Restate the real objective** in one sentence. Half of agent failures are drift.
24
+ 2. **Verify world state** (filesystem, branch, service up) instead of assuming it.
25
+ 3. **Shrink the failing scope** to one test / one file / one command.
26
+ 4. **Run one discriminating check** that would confirm or kill the current hypothesis.
27
+ 5. **Only then retry** — and at most once before escalating.
28
+
29
+ Never claim auto-healing you can't show. If two recovery attempts don't converge, stop and surface what
30
+ was tried — same as the six-step protocol's "escalate after 60 minutes stuck" rule.
@@ -94,6 +94,19 @@ Closes RF-XX (partial — see tasks.md).
94
94
 
95
95
  The commit message format is consistent across waves so `/dw-generate-pr` can build a clean PR body. See `references/atomic-commits.md`.
96
96
 
97
+ ## De-Sloppify Pass (optional cleanup task)
98
+
99
+ LLM implementers are thorough but leave slop — tests of framework/language behavior, defensive checks the type system already enforces, leftover `console.log`, over-handled impossible states. Constraining the implementer with "don't over-engineer" degrades its output unpredictably; a **separate cleanup pass in a fresh context** works better. Two focused agents beat one constrained agent.
100
+
101
+ To request one, a task in `tasks.md` may carry `Cleanup After: <N>` in its frontmatter. The executor runs it as its own task (own commit) right after task N's wave lands, scoped to task N's diff:
102
+
103
+ - Remove tests that assert language/framework behavior rather than business logic.
104
+ - Remove type checks and guards the type system or a prior validation already guarantees.
105
+ - Remove `console.log`/debug prints and commented-out code.
106
+ - Keep every business-logic test and every real error path — when unsure, keep it.
107
+
108
+ The cleanup task commits as `refactor(<scope>): de-slop <task N title>` and must leave the gate green (lint + tests + build) — it never changes behavior. Opt-in: no `Cleanup After:` means no cleanup task runs.
109
+
97
110
  ## Checkpoint Protocol
98
111
 
99
112
  If the executor exhausts its context budget mid-phase OR the user signals stop:
@@ -78,6 +78,7 @@ When wired into `/dw-generate-pr`, every PR must:
78
78
  | Branch is 5 days old, tons of conflicts | Rebase from main daily; don't let drift compound |
79
79
  | PR has 2,000 lines | Split. Identify natural seams: schema, backend, frontend, tests |
80
80
  | Stuck mid-merge with conflicts | Resolve, don't `git checkout --theirs` blindly. The conflict is information |
81
+ | Rebase or merge to integrate? | Local-only branch → **rebase** onto `main` (linear history). Shared/pushed branch → **merge** (never rebase history others have pulled) |
81
82
 
82
83
  ## What this skill does NOT do
83
84
 
@@ -66,6 +66,17 @@ Structure:
66
66
 
67
67
  See `references/reference-dataset.md` for case-design principles, sampling from production, and when to expand the set.
68
68
 
69
+ ## Consistency (run-to-run stability)
70
+
71
+ The oracle ladder measures whether an output is *correct*; it does not measure whether the feature is *stable*. Because LLMs are non-deterministic, a case that passes once may fail the next run. Measure it:
72
+
73
+ - Re-run each case **N times** (default 3) against the same code + model + prompt.
74
+ - Record `pass_rate` (e.g. `2/3`) and `consistency` (fraction agreeing with the majority verdict) per case in `runs/<YYYY-MM-DD>.jsonl`.
75
+ - A case with pass_rate strictly between `1/N` and `(N-1)/N` is **flaky** — a signal, not a pass. A 2/3 pass hides a one-in-three production failure; investigate before shipping.
76
+ - Sample at the **temperature the feature uses in production** — consistency measured at temp 0 lies about a temp-0.7 feature.
77
+
78
+ Deterministic rungs (1–3) are usually stable; consistency matters most for rung 4 (LLM-as-judge) and agent trajectories. Record the run count `N` in the run log so later comparisons are apples-to-apples.
79
+
69
80
  ## RAG evaluation
70
81
 
71
82
  Three orthogonal metrics — measure all three, not just one:
@@ -120,6 +131,7 @@ See `references/agent-eval.md` for examples and the decision tree.
120
131
  - **Synthetic-only dataset.** No traceable production-failure source for any case. REJECTED — confirm at least 20% of cases come from real user inputs.
121
132
  - **"Looks good to me" QA.** No reference dataset, no metric, no rubric — just sampling output and calling it good. REJECTED.
122
133
  - **Coverage as metric.** Quoting "we tested 50 prompts" without saying what was measured. The number is meaningless without the metric.
134
+ - **Single-run eval on a non-deterministic feature.** Each case run once at production temperature; a lucky pass reported as a pass. REJECTED — report `pass_rate` over N runs (see Consistency).
123
135
 
124
136
  ## Integration with dev-workflow commands
125
137