@brunosps00/dev-workflow 1.4.0 → 1.6.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 (68) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +6 -5
  3. package/lib/constants.js +6 -4
  4. package/lib/hooks.js +53 -11
  5. package/lib/init.js +49 -1
  6. package/package.json +4 -2
  7. package/scaffold/config/stack-mappings.json +40 -0
  8. package/scaffold/en/commands/dw-adr.md +49 -11
  9. package/scaffold/en/commands/dw-analyze-project.md +10 -1
  10. package/scaffold/en/commands/dw-brainstorm.md +103 -32
  11. package/scaffold/en/commands/dw-claude-run.md +8 -5
  12. package/scaffold/en/commands/dw-context-budget.md +9 -0
  13. package/scaffold/en/commands/dw-harness-audit.md +3 -0
  14. package/scaffold/en/commands/dw-learn.md +41 -0
  15. package/scaffold/en/commands/dw-plan.md +36 -5
  16. package/scaffold/en/commands/dw-review.md +1 -0
  17. package/scaffold/en/commands/dw-run.md +1 -0
  18. package/scaffold/en/templates/adr-template.md +8 -4
  19. package/scaffold/en/templates/idea-onepager.md +44 -2
  20. package/scaffold/pt-br/commands/dw-adr.md +49 -11
  21. package/scaffold/pt-br/commands/dw-analyze-project.md +10 -1
  22. package/scaffold/pt-br/commands/dw-brainstorm.md +105 -32
  23. package/scaffold/pt-br/commands/dw-claude-run.md +8 -5
  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 +36 -5
  28. package/scaffold/pt-br/commands/dw-review.md +1 -0
  29. package/scaffold/pt-br/commands/dw-run.md +1 -0
  30. package/scaffold/pt-br/templates/adr-template.md +8 -4
  31. package/scaffold/pt-br/templates/idea-onepager.md +44 -2
  32. package/scaffold/rules/README.md +22 -0
  33. package/scaffold/rules/common.md +29 -0
  34. package/scaffold/rules/csharp.md +18 -0
  35. package/scaffold/rules/python.md +18 -0
  36. package/scaffold/rules/react.md +18 -0
  37. package/scaffold/rules/rust.md +18 -0
  38. package/scaffold/rules/typescript.md +23 -0
  39. package/scaffold/rules-readme.md +8 -0
  40. package/scaffold/scripts/hooks/session-cost.mjs +126 -0
  41. package/scaffold/scripts/hooks/statusline.mjs +31 -0
  42. package/scaffold/scripts/lib/model-prices.json +12 -0
  43. package/scaffold/skill-registry.json +25 -1
  44. package/scaffold/skills/dw-cli-run/SKILL.md +13 -9
  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-domain-modeling/SKILL.md +84 -0
  49. package/scaffold/skills/dw-domain-modeling/references/adr-policy.md +48 -0
  50. package/scaffold/skills/dw-domain-modeling/references/glossary-format.md +66 -0
  51. package/scaffold/skills/dw-execute-phase/SKILL.md +13 -0
  52. package/scaffold/skills/dw-git-discipline/SKILL.md +1 -0
  53. package/scaffold/skills/dw-grilling/SKILL.md +98 -0
  54. package/scaffold/skills/dw-grilling/references/decision-tree.md +56 -0
  55. package/scaffold/skills/dw-grilling/references/interview-loop.md +52 -0
  56. package/scaffold/skills/dw-llm-eval/SKILL.md +12 -0
  57. package/scaffold/skills/dw-memory/SKILL.md +12 -1
  58. package/scaffold/skills/dw-memory/references/instincts.md +45 -0
  59. package/scaffold/skills/dw-review-rigor/SKILL.md +12 -0
  60. package/scaffold/skills/dw-review-rigor/references/false-positives.md +21 -0
  61. package/scaffold/skills/dw-review-rigor/references/self-eval-rubric.md +38 -0
  62. package/scaffold/skills/dw-silent-failure/SKILL.md +4 -2
  63. package/scaffold/skills/dw-simplification/SKILL.md +13 -0
  64. package/scaffold/skills/dw-simplification/references/dead-code-tools.md +23 -0
  65. package/scaffold/skills/dw-testing-discipline/SKILL.md +17 -0
  66. package/scaffold/skills/dw-ui-discipline/SKILL.md +1 -1
  67. package/scaffold/skills/dw-verify/SKILL.md +1 -1
  68. package/scaffold/skills/dw-verify/references/pre-check-and-status.md +28 -0
@@ -3,8 +3,9 @@ id: NNN
3
3
  status: Proposed
4
4
  title: [Título imperativo curto da decisão]
5
5
  date: YYYY-MM-DD
6
- prd: [slug do PRD, ex: prd-user-auth]
7
- schema_version: "1.0"
6
+ scope: repo | prd
7
+ prd: [slug do PRD para scope=prd, ex: prd-user-auth; "n/a" para scope=repo]
8
+ schema_version: "1.1"
8
9
  supersedes: null
9
10
  superseded_by: null
10
11
  ---
@@ -46,8 +47,11 @@ Proposed | Accepted | Deprecated | Superseded by ADR-XXX
46
47
 
47
48
  ## Related
48
49
 
49
- - PRD: `.dw/spec/[prd-slug]/prd.md`
50
- - TechSpec: `.dw/spec/[prd-slug]/techspec.md` (se aplicável)
50
+ <!-- scope=prd: linke o PRD/TechSpec dono. scope=repo: sem PRD dono — omita as linhas de PRD/TechSpec
51
+ (ou escreva "n/a") e linke `.dw/constitution.md` / `.dw/rules/` só quando genuinamente aplicáveis. -->
52
+
53
+ - PRD: `.dw/spec/[prd-slug]/prd.md` (só scope=prd; "n/a" para scope=repo)
54
+ - TechSpec: `.dw/spec/[prd-slug]/techspec.md` (se aplicável; só scope=prd)
51
55
  - Tasks afetadas: [lista de tasks, se aplicável]
52
56
  - ADRs relacionadas: [lista, se aplicável]
53
57
 
@@ -1,11 +1,19 @@
1
1
  ---
2
2
  type: idea-onepager
3
- schema_version: "1.0"
4
- status: draft
3
+ schema_version: "1.1"
4
+ status: draft | paused | aligned
5
5
  date: YYYY-MM-DD
6
6
  classification: improves | consolidates | new
7
+ alignment:
8
+ confirmed_by_user: false # true SOMENTE quando o usuário confirma explicitamente o shared understanding
9
+ confirmed_on: null # data ISO dessa confirmação
7
10
  ---
8
11
 
12
+ > **Schema 1.1.** O bloco `## Grill Alignment` abaixo é preenchido por uma sessão de grill do `/dw-brainstorm`. Sem
13
+ > Grill, deixe esse bloco vazio e mantenha `status: draft`. `status: aligned` é permitido SOMENTE quando cada branch
14
+ > de dependência está resolvida, contradições glossário/código estão fechadas, nenhuma decisão bloqueante resta, e o
15
+ > usuário confirmou explicitamente o shared understanding — senão use `draft` ou `paused`.
16
+
9
17
  # Ideia: [Título curto e imperativo]
10
18
 
11
19
  ## Problem Statement
@@ -81,6 +89,40 @@ Idealmente 2-4 stories. Se são mais de 5, provavelmente não é MVP.]
81
89
  - [Pergunta 1 que afeta escopo]
82
90
  - [Pergunta 2 que afeta prioridade]
83
91
 
92
+ ## Grill Alignment
93
+
94
+ _(schema 1.1 — preenchido por uma sessão de grill do `/dw-brainstorm`; consumido pelo `/dw-plan`, que NÃO re-pergunta decisões resolvidas. Deixe vazio para um one-pager sem grill.)_
95
+
96
+ ### Resolved Decisions
97
+
98
+ | Decisão | Recomendado | Escolhido | Alternativa rejeitada | Evidência |
99
+ |---------|-------------|-----------|-----------------------|-----------|
100
+ | [a decisão] | [o que o Grill recomendou] | [o que o usuário escolheu] | [a opção rejeitada + seu trade-off] | [fonte no one-pager/repo] |
101
+
102
+ ### Evidence
103
+
104
+ [Fatos descobertos durante o Grill no repo/rules/intel/docs — cada um com sua fonte (`path:line`, doc, ou query de intel) — usados em vez de perguntar ao usuário.]
105
+
106
+ - **[fato]** — fonte: `[path:line ou doc]`
107
+
108
+ ### Canonical Vocabulary
109
+
110
+ [Links para `.dw/domain/**` dos termos de que esta ideia depende. Não repita definições aqui — aponte para o glossário.]
111
+
112
+ - **[Termo]** → `.dw/domain/glossary.md#termo` (ou `.dw/domain/contexts/<slug>.md#termo` num projeto multi-contexto)
113
+
114
+ ### Remaining Decisions
115
+
116
+ [Decisões não-bloqueantes ainda abertas após o alinhamento, com seu dono. Uma decisão bloqueante significa que o one-pager NÃO está `aligned`.]
117
+
118
+ - **[decisão aberta]** — dono: [quem] — bloqueante? [não]
119
+
120
+ ### Alignment State
121
+
122
+ - **State:** `draft` | `paused` | `aligned`
123
+ - **Shared understanding confirmado pelo usuário:** [sim/não — `aligned` exige um sim explícito]
124
+ - **Por que não alinhado (se draft/paused):** [o nó bloqueante ou contradição não fechada que parou o alinhamento]
125
+
84
126
  ## Next Step
85
127
 
86
128
  Escolha UM:
@@ -0,0 +1,22 @@
1
+ # Rules library — curated declarative baseline
2
+
3
+ Curated "what good code SHOULD look like" per stack, installed to `.dw/rules-library/`. This is the third
4
+ leg of the rules model:
5
+
6
+ | Source | Type | Answers | Authored by |
7
+ |---|---|---|---|
8
+ | `.dw/rules-library/` | Curated baseline (this) | what good code looks like in `<stack>` | dev-workflow (shipped) |
9
+ | `.dw/rules/` | Analytical | what THIS code **is** (observed patterns) | `/dw-analyze-project` |
10
+ | `.dw/constitution.md` | Declarative, committed | what THIS project **commits to** | `/dw-analyze-project` (Step 8) |
11
+
12
+ ## Precedence
13
+
14
+ A language file overrides `common` where idioms differ. The project's own `.dw/rules/` and
15
+ `.dw/constitution.md` override the library — they describe THIS project; the library is only the default a
16
+ project starts from.
17
+
18
+ ## Loading (lazy — protect the context budget)
19
+
20
+ Commands load ONLY the files for the **active stack**, resolved via `.dw/config/stack-mappings.json`
21
+ (`common.md` + `<stack>.md`). Never load the whole library. `/dw-analyze-project` uses it to seed
22
+ constitution proposals; `/dw-review` reads the active stack's baseline as the declarative bar.
@@ -0,0 +1,29 @@
1
+ # Baseline rules — common (all languages)
2
+
3
+ Declarative baseline: what code SHOULD be, regardless of stack. Language files override or specialize these
4
+ where idioms differ. Complements `.dw/rules/` (observed) and `.dw/constitution.md` (committed).
5
+
6
+ ## Code style
7
+ - Prefer immutability: return new values, don't mutate inputs. Mutate only in a measured hot path.
8
+ - Name for intent: `overdueInvoices`, not `data`/`tmp`/`x`. A name that needs a comment is the wrong name.
9
+ - Keep functions small and single-purpose; extract when a block needs its own comment to be understood.
10
+ - No magic values: name constants (except self-evident ones like HTTP codes or `0`/`-1` indexes).
11
+
12
+ ## Error handling
13
+ - Never swallow errors. Handle, propagate, or log with context — never an empty catch (see `dw-silent-failure`).
14
+ - Validate untrusted input once, where it enters the system; fail loudly at boundaries.
15
+ - Timeout on every network/IO call; rollback partial transactional work.
16
+
17
+ ## Testing
18
+ - Test behavior, not implementation. A test that breaks on a rename tested the wrong thing.
19
+ - Cover error and edge paths (empty, null, boundary, failure), not just the happy path.
20
+ - New bug → regression test first, then fix (see `dw-testing-discipline`).
21
+
22
+ ## Security
23
+ - No secrets in code or logs. Read from env/secret store; `.env` is gitignored.
24
+ - Validate input at the boundary; encode output at the boundary.
25
+ - Least privilege for every credential, token, and role.
26
+
27
+ ## Git
28
+ - One intent per commit; Conventional Commits subject (see `dw-git-discipline`).
29
+ - Never force-push shared history; rebase only local branches.
@@ -0,0 +1,18 @@
1
+ # Baseline rules — C# / .NET (extends common)
2
+
3
+ ## Style
4
+ - Nullable reference types enabled; warnings as errors. `record`/`readonly` for value types.
5
+ - `async`/`await` end-to-end; never `.Result`/`.Wait()` (deadlocks). Thread `CancellationToken` through.
6
+ - `using` declarations for `IDisposable`; dependency injection over `new` for services.
7
+
8
+ ## Errors
9
+ - Catch specific exceptions; never empty catch. Don't use exceptions for normal control flow.
10
+ - Validate arguments at public boundaries (`ArgumentNullException.ThrowIfNull`).
11
+
12
+ ## Testing
13
+ - xUnit/NUnit; test public behavior. Mock owned boundaries, not the system under test itself.
14
+ - Arrange-Act-Assert; one behavior per test.
15
+
16
+ ## Security
17
+ - Parameterized queries / EF Core — never concatenate SQL. Validate/encode at boundaries.
18
+ - Secrets via configuration / user-secrets / Key Vault, never in source.
@@ -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,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));
@@ -49,6 +49,35 @@ function minimalismMode(cwd) {
49
49
  }
50
50
  }
51
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
+
52
81
  function activeSpec(cwd) {
53
82
  try {
54
83
  const specDir = join(cwd, '.dw', 'spec');
@@ -80,6 +109,8 @@ async function main() {
80
109
  const spec = activeSpec(cwd);
81
110
  if (spec) parts.push(spec);
82
111
  parts.push(`min:${minimalismMode(cwd)}`);
112
+ const cost = costToday(cwd);
113
+ if (cost) parts.push(cost);
83
114
 
84
115
  process.stdout.write(`dw · ${parts.join(' · ')}`);
85
116
  }
@@ -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
+ }
@@ -35,7 +35,7 @@
35
35
  "trigger": "Fire an AI CLI (Codex/Claude/Copilot) in a dedicated git worktree to implement a prepared prompt; resumable per-task session; STOP for the gate",
36
36
  "expected_output": "Audit-logged run with a 0-10 dual evaluation, a resumable session-id sidecar, and a STOP-for-gate Structured Return.",
37
37
  "load_policy": "lazy-references",
38
- "context_limit": 18000,
38
+ "context_limit": 19000,
39
39
  "bundled": true
40
40
  },
41
41
  {
@@ -60,6 +60,30 @@
60
60
  "context_limit": 12000,
61
61
  "bundled": true
62
62
  },
63
+ {
64
+ "name": "dw-domain-modeling",
65
+ "kind": "protocol",
66
+ "tier": "specialty",
67
+ "owner": "dw-brainstorm,dw-analyze-project",
68
+ "trigger": "Native Grill domain vocabulary: challenge fuzzy/overloaded terms, cross-check against code, persist a canonical glossary to .dw/domain",
69
+ "expected_output": "Canonical terms with discouraged synonyms persisted lazily to .dw/domain (single or multi-context), or fuzzy-term findings.",
70
+ "load_policy": "lazy-references",
71
+ "context_limit": 12000,
72
+ "exportable": false,
73
+ "bundled": true
74
+ },
75
+ {
76
+ "name": "dw-grilling",
77
+ "kind": "protocol",
78
+ "tier": "specialty",
79
+ "owner": "dw-brainstorm,dw-plan",
80
+ "trigger": "Native Grill decision-tree interview: sharpen a plan/PRD to shared understanding, exactly one decision per turn with a recommended answer",
81
+ "expected_output": "A dependency-ordered decision tree, resolved decisions with recommendations, and an explicit shared-understanding gate.",
82
+ "load_policy": "lazy-references",
83
+ "context_limit": 12000,
84
+ "exportable": false,
85
+ "bundled": true
86
+ },
63
87
  {
64
88
  "name": "dw-debug-protocol",
65
89
  "kind": "protocol",
@@ -125,21 +125,25 @@ 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
- ## Dual evaluation: the CLI auto-gates (MAX effort) → Claude re-gates (compares scores)
132
+ ## Dual evaluation: the CLI auto-gates (max effort) → the parent 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
132
- **Claude audits independently** and **compares the scores** (catches an inflated self-score).
134
+ **the parent/orchestrator audits independently** and **compares the scores** (catches an inflated self-score). The
135
+ parent is the dispatching session — possibly Claude itself; the re-gate is provider-neutral.
133
136
  1. **CLI auto-gate (loop, MAX effort).** The prompt MUST instruct: after implementing, **run the SAME gate** and
134
137
  give a **self-score 0–10**; **fix and re-run while the self-score <9 or the gate isn't green**, at max effort.
135
138
  Stop at self-score ≥9 + green gate (or report `blockers`). The final report carries the self-score + per
136
139
  criterion.
137
- 2. **Claude re-gate (independent).** When the CLI declares pass, the parent **re-runs the SAME gate** (fan-out,
138
- prefer Workflow → `/workflows`) and gives its **own 0–10** — without trusting the self-score.
139
- 3. **Compare + decide.** Claude ≥9 and small gap → **PASS** (ready for the owner's merge decision). Claude <9 OR a
140
+ 2. **Parent re-gate (independent).** When the CLI declares pass, the parent/orchestrator **re-runs the SAME gate**
141
+ (fan-out, prefer Workflow → `/workflows`) and gives its **own 0–10** — without trusting the self-score (a worker
142
+ never signs off on its own exam, even when that worker is Claude).
143
+ 3. **Compare + decide.** Parent ≥9 and small gap → **PASS** (ready for the owner's merge decision). Parent <9 OR a
140
144
  large gap (CLI overestimated) → re-execute: hand the gaps back via **session resume** (step 5, max effort) and
141
- repeat 1→2→3 until it converges. **The score that counts for acceptance is Claude's**; the self-score is signal
142
- + an inflation detector. Always record both scores + the gap in the Structured Return.
145
+ repeat 1→2→3 until it converges. **The score that counts for acceptance is the parent's**; the self-score is
146
+ signal + an inflation detector. Always record both scores + the gap in the Structured Return.
143
147
 
144
148
  ## Graded escalation on failure
145
149
  If the score is low / the gate failed / the CLI didn't finish, **re-run the SAME task one notch up** — gradual, no
@@ -197,7 +201,7 @@ The stream ends with the adapter's `USAGE` block (token counts). Extract it from
197
201
  right worktree, ready for the gate; `FINDINGS` = ran, score 6–8 or caveats (uncommitted/out-of-fence/partial);
198
202
  `BLOCKED` = missing CLI/credential/worktree/prompt/adapter table, or escalation exhausted without reaching 9;
199
203
  `NOT_APPLICABLE` = no worktree+prompt → still planning.
200
- - **Score:** **Claude's score** (0–10 — counts for acceptance, bar ≥9) + the **CLI self-score** + the **gap**;
204
+ - **Score:** **the parent's score** (0–10 — counts for acceptance, bar ≥9) + the **CLI self-score** + the **gap**;
201
205
  per-criterion rationale (conformance/gate/completeness/hygiene/quality). Large gap = inflated self-score.
202
206
  - **Scope:** worktree + branch + prompt used; CLI + model + effort + sandbox/auto mode; escalation notches taken.
203
207
  - **Evidence:** path of the durable audit log + final-message file; stream JSONL excerpt; `git status`/diff of the
@@ -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.