crewforth 3.1.0 → 3.2.0-rc.1

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 (70) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/README.md +4 -3
  3. package/VERSION +1 -1
  4. package/adopt.sh +91 -11
  5. package/kit/AGENT_TEMPLATE.md +2 -1
  6. package/kit/CLAUDE.md +9 -8
  7. package/kit/agents/crew-commit-agent.md +7 -2
  8. package/kit/agents/crew-docs-agent.md +71 -0
  9. package/kit/agents/crew-planner.md +2 -0
  10. package/kit/agents/crew-refuter.md +77 -0
  11. package/kit/agents/crew-security-expert.md +3 -1
  12. package/kit/agents/crew-session-manager.md +11 -9
  13. package/kit/eval/doctor.sh +91 -3
  14. package/kit/eval/golden-routing.txt +9 -0
  15. package/kit/eval/lib/crew-map/crew-map.sh +26 -0
  16. package/kit/eval/lib/crew-map/detect.mjs +238 -0
  17. package/kit/eval/lib/crew-map/narrative.mjs +98 -0
  18. package/kit/eval/lib/crew-map/parse.mjs +305 -0
  19. package/kit/eval/lib/crew-map/project.mjs +163 -0
  20. package/kit/eval/lib/crew-map/rules/names.json +38 -0
  21. package/kit/eval/lib/crew-map/rules/packages.json +142 -0
  22. package/kit/eval/lib/crew-map/rules/python-stdlib.json +197 -0
  23. package/kit/eval/lib/crew-map/scan.mjs +214 -0
  24. package/kit/eval/lib/crew-map/stacks.mjs +441 -0
  25. package/kit/eval/lib/crew-map/surface.mjs +457 -0
  26. package/kit/eval/lib/findings/findings.mjs +410 -0
  27. package/kit/eval/lib/findings/findings.schema.json +122 -0
  28. package/kit/eval/lib/findings/findings.sh +29 -0
  29. package/kit/eval/lib/settings-json.awk +5 -0
  30. package/kit/eval/lib/star.sh +23 -7
  31. package/kit/eval/lib/statusline.settings.json +6 -0
  32. package/kit/eval/lib/statusline.sh +115 -0
  33. package/kit/eval/smoke-test.sh +465 -38
  34. package/kit/hooks/agent-outcome.sh +6 -3
  35. package/kit/hooks/guard-agent-model.sh +15 -5
  36. package/kit/hooks/guard-bash.sh +240 -182
  37. package/kit/hooks/guard-question.sh +73 -0
  38. package/kit/hooks/guard-write.sh +21 -0
  39. package/kit/hooks/prompt-approval.sh +320 -103
  40. package/kit/hooks/refuter-ledger.sh +80 -0
  41. package/kit/owned-blobs.tsv +4 -1
  42. package/kit/settings.json +33 -0
  43. package/kit/skills/crew-approve/SKILL.md +7 -0
  44. package/kit/skills/crew-doctor/SKILL.md +5 -1
  45. package/kit/skills/crew-handoff/SKILL.md +3 -2
  46. package/kit/skills/crew-map/SKILL.md +44 -0
  47. package/kit/skills/crew-review/SKILL.md +2 -0
  48. package/kit/skills/crew-update/SKILL.md +1 -1
  49. package/kit/skills/incident-runbook/SKILL.md +2 -2
  50. package/kit/skills/security-scan/references/verify.md +6 -3
  51. package/kit/studio/README.md +12 -0
  52. package/kit/studio/server/index.js +114 -11
  53. package/kit/studio/server/lib/crew-map.js +113 -0
  54. package/kit/studio/server/lib/model-outcomes.js +130 -15
  55. package/kit/studio/server/lib/pricing.js +3 -0
  56. package/kit/studio/server/lib/remote.js +99 -0
  57. package/kit/studio/server/lib/usage.js +74 -14
  58. package/kit/studio/web/app.js +214 -12
  59. package/kit/studio/web/canvas.js +44 -120
  60. package/kit/studio/web/graph-plan.js +10 -4
  61. package/kit/studio/web/index.html +10 -5
  62. package/kit/studio/web/map-plan.js +341 -0
  63. package/kit/studio/web/map.js +503 -0
  64. package/kit/studio/web/models-plan.js +61 -7
  65. package/kit/studio/web/models.js +19 -3
  66. package/kit/studio/web/style.css +125 -0
  67. package/kit/studio/web/usage.js +12 -0
  68. package/kit/studio/web/viewport.js +160 -0
  69. package/package.json +6 -5
  70. package/start.sh +10 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,157 @@ Crewforth was named Claude Starter Kit until 3.0.0.
5
5
  Notable changes to this project are recorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/),
6
6
  versioning follows [SemVer](https://semver.org/).
7
7
 
8
+ ## [Unreleased] — 3.2.0
9
+
10
+ ### Added
11
+
12
+ - **In `auto`, approve a commit or a push with one click.** The refusal now also offers a fixed question, asked with
13
+ AskUserQuestion: Commit only, Commit + push, Push only, No. A new gate, `guard-question.sh`, answers `ask` on it,
14
+ so a hook that fills in the answer cannot answer it for you (measured in 34 runs and by hand in the terminal), and
15
+ `prompt-approval.sh` records your click exactly as it records `/crew-approve`: tied to what is staged, `HEAD`, the
16
+ session, for 30 minutes.
17
+ Any other wording, a question with answers already in it, or an answer the hook did not ask for records nothing.
18
+ - **Studio's Models view shows normal work that ran above the table's model.** The gate holds a floor, not a
19
+ ceiling, so this is shown and nothing is blocked: the classes of normal work that ran higher than Sonnet and than
20
+ the floor the gate held them to, with what they cost as run and the same tokens at Sonnet's list price. That
21
+ Sonnet is enough for a class is said only when its last ten verified calls on Sonnet all passed first time, the
22
+ test a held class is offered for lowering by; otherwise the view says it is not measured.
23
+ - **Studio draws a project's crew map.** The Map view (`p`, or **Map** under a project in the navigator, beside its
24
+ number of findings) reads `.crewforth/crew-map.json` and scans nothing. Modules sit in bands from the clients down
25
+ to the external resources, and a module the map does not place goes to Unplaced. Lines are solid where a file
26
+ proves them, dotted where inferred, and marked where the model proposed them. The pane pans and zooms with the
27
+ graph's own code; a card placed by hand stays placed for the project, and modules sharing a folder fold into one
28
+ card. The findings are listed proven first, with the scanner's hidden rules counted until asked for. The inspector
29
+ copies any `file:line` and copies a finding as a task. Compare with matches another project's calls against this
30
+ one's endpoints. On a phone, the findings and a list of the modules stand in for the pane.
31
+ - **A crew map of the project.** `bash .claude/eval/lib/crew-map/crew-map.sh` reads the modules (Node and TypeScript,
32
+ Python, Go, .NET, Java and Kotlin), the external resources they touch, the endpoints they provide and call, and
33
+ writes `.crewforth/crew-map.json`. Every module, line and finding cites the `file:line` it was read from, the same
34
+ tree gives the same bytes (`--check` holds it in CI), and a configuration value is read only to name its kind. Ten
35
+ gap rules: five proven by the lines they cite, five concluded from absence and hidden until their false-positive
36
+ rate is measured.
37
+ - **Findings are held to the lines they cite.** The findings core (`.claude/eval/lib/findings/findings.sh`) checks
38
+ every citation: the file is inside the project, the lines exist, and they are still the lines that were read. A
39
+ document that does not verify is reported entry by entry and never written.
40
+ - **A finding is verified by an agent that tries to refute it.** `crew-refuter` gets one claim and its cited lines,
41
+ not the reasoning, and works on one lens. `refuter-ledger.sh` records its verdict from the agent's own task and
42
+ answer in `.claude/state/crew-refuter.tsv`. With `--verified`, a `medium` finding needs one verdict and a `high` or
43
+ `critical` one three, each a line of that ledger.
44
+ - **`crew-docs-agent` writes the text.** README, guides, release notes, legal and privacy texts and store listings had
45
+ no owner and ran outside model routing. Legal, privacy, consent and terms paths and store metadata are critical
46
+ paths, so that text runs on the critical tier and a legal or privacy text is audited by `crew-privacy-agent` before
47
+ it closes.
48
+ - **Doctor names three more things, none of which changes its verdict.** Both editions enabled at once (every hook
49
+ would run twice), read from `claude plugin list --json`; the advisor, whether it is on, on what and from which
50
+ settings file, or turned off; and, before any update check has run, the published version, said as not measured
51
+ instead of nothing.
52
+ - **Studio shows why work was critical, and says when an agent is outside routing.** The gate records its reason
53
+ (`risk_why`, a new last column of `model-outcomes.tsv`) and the inspector shows it under the task card; an older
54
+ line shows none rather than a guessed one. An agent that is not `crew-*` gets a Model routing note: the gate leaves
55
+ it alone, so it has no card.
56
+ - **Studio counts the advisor in what a session cost.** An advisor call is billed at the advisor model's rates and the
57
+ API keeps it out of a response's top-level usage; Studio now reads it from `usage.iterations` and prices it at its
58
+ own model. The session table has an Advisor line (calls, tokens, cost) when a call is recorded. Measured on a real
59
+ session: Studio read $0.0567 before, $0.2110 after, the same as Claude Code's own figure.
60
+ - **Studio's Models view marks the classes whose agents consulted the advisor.** The cost cell carries an advisor tag
61
+ whose hover says how many calls and how much of the cost they were, already included; a class whose agents consulted
62
+ none has no tag.
63
+ - **Read-only remote viewers for Studio.** The Machines tab makes a viewer link that only reads, lists each viewer
64
+ with its last request, and revokes it. The server still binds to `127.0.0.1`; watching from another device goes
65
+ through a tunnel you run (`ssh -L`, Tailscale).
66
+ - **A visible way to the repository.** `/crew-doctor` now shows the star line once per version under its own marker,
67
+ so a line already shown at install no longer silences it, and a plugin install shows it too, with the plugin's
68
+ version. `/crew-update` keeps it to one line per update. The READMEs and the site's home page have a Star on GitHub
69
+ button.
70
+ - **A status line.** One line under the prompt: the gate's last word in this session, a commit or push approval that
71
+ still holds, the crew agents still running, the measured context fill and the session's cost. The installers set it
72
+ only where no status line is set, in the project or in your own settings, and `/crew-doctor` says which one is in
73
+ use.
74
+ - **`/crew-map`.** It runs the scanner, then has the model describe what it found: at most two sentences per module on
75
+ what it is for, and a short overview, each citing lines the scan itself read. `crew-map.sh --validate` refuses a
76
+ description that cites anything else or nothing, or a scanner field changed by hand; a scan again keeps the
77
+ descriptions that still hold and drops the rest. Studio's Map shows them, and `crew-planner` reads the map on an
78
+ unfamiliar project.
79
+
80
+ ### Changed
81
+
82
+ - **A commit's command is read by one reader.** `guard-bash.sh` paired quotes in three places: the reader of a call,
83
+ an older walk that looks for a path or `-a` in a commit, and the check of a call the user's approval covers. The
84
+ last two now take their text from the first, and keep only their own question. What a call the user approved may
85
+ hold is unchanged. The older walk read a here-document's body and a comment as arguments of the commit; it no
86
+ longer does, so a call that only holds the words of a commit inside a here-document body, or `-a` after a `#`,
87
+ asks as any other commit does instead of being refused.
88
+ - **npm releases are published over OIDC.** The release job proves its identity with a short-lived GitHub Actions
89
+ token (npm Trusted Publishing); the repository holds no npm token, and each version carries a provenance statement.
90
+ - **The session line shows the measured fill.** The last line of a reply was `🔋 Session: low fill · …`: the template
91
+ had no number, so the figure the hook measures every turn stayed out of sight. It is now `🔋 Session %NN → low fill ·
92
+ …`, the hook's own figure, never an estimate.
93
+ - **Studio keeps cards for a longer session.** At Auto density a picture may now run up to half again past the pane
94
+ before its cards become chips; it opens at the zoom cards are read at, from its top, and Fit still shows all of it.
95
+ A compact group card says how its agents went as well as their models (`5 done · 3 Sonnet · 2 Opus`).
96
+ - **The session's own write to a critical path is recorded.** Agents not on Opus are refused a critical path; the
97
+ session's own writes were not looked at at all. They are still not refused (the choice is yours), but each one now
98
+ leaves a NOTE line in the gate log with the path and the rule that made it critical, which Studio's Gates tab and
99
+ the status line show.
100
+
101
+ ### Security
102
+
103
+ - **A `PermissionRequest` hook could approve a commit with nobody at the keyboard.** Such a hook answers a
104
+ permission prompt before the person does, and the answer cannot be told from a click: measured, it approved the
105
+ Bash prompt `guard-bash.sh` raises for a commit in `default`, and it answered the new question too. Before it
106
+ asks, the gate now looks for one in every source it can read (user, project and local settings, the
107
+ managed-settings files, skill, agent and command front matter, installed plugins). Where one may be wired the
108
+ question closes, and so does the gate's own prompt in `default` and `acceptEdits`: `/crew-approve`, which no hook
109
+ can type, is the approval left. The limit: settings passed with `--settings` and managed settings delivered by a
110
+ server cannot be read.
111
+ - **`crewforth.hookschain` is guarded the way `core.hooksPath` is.** The key holds the path of a project hook chain
112
+ the shim reads on every commit, and `git config crewforth.hookschain …` passed. A write is now refused in every form
113
+ `core.hooksPath` is (a value, `--unset`, `-c`, `--config-env`, `GIT_CONFIG_` variables); reading it still passes.
114
+
115
+ ### Fixed
116
+
117
+ - **The crew map reads what two real projects showed it missing.** Scanned before release, they put three rules
118
+ well over a 20% false-positive rate, and one scan refused its own document. Read now: a route with an empty path,
119
+ a decorator whose path is on the next line, `api.get<T>(` and a call split over lines, a client made in one file
120
+ and called in another (it had read as a server), an import ending in a comment, twelve more import names, and a
121
+ string in a test that looks like an import. A call with a run-time host is matched but never reported missing. A
122
+ route a healthcheck or a Kubernetes probe calls has a caller, and a package that works without an import (a
123
+ server started from the command line, a database driver named in a URL, a pytest plugin) is not unused.
124
+ Source in a language the scanner has no reader for, and routes registered with a variable path, are listed in
125
+ `notMeasured` instead of being silently absent. On the Python project the proven `endpoint-missing` went from 2
126
+ real out of 10 to 4 out of 4.
127
+ - **Studio's cards no longer flicker while a window is resized.** At Auto density, an edge dragged across the limit
128
+ between cards and chips redrew every card on every frame (measured: 57 times in 60 frames with 43 agents). The view
129
+ still follows the edge at once; the choice between cards and chips is made once the window has stopped for 150 ms.
130
+ - **`crew-commit-agent` and `crew-session-manager` are called.** Both were exempt from the delegation cue and nothing
131
+ pulled them, so the main thread wrote every commit message and handover itself. The discipline and `/crew-handoff`
132
+ now call them, and smoke fails when a chain-step agent has no caller.
133
+ - **Doctor names the 2.x plugin enabled beside a 3.x install.** The 2.x plugin, under its old name, wires the same
134
+ hooks; the check looked for `crewforth@…` only and said one edition while every hook ran twice.
135
+ - **The crew map no longer reads a regex literal as an import.** `from '[^']*…` inside a regular expression was
136
+ reported as a proven undeclared dependency named `[^`; a specifier counts as a package only when it is named the way
137
+ npm names one.
138
+ - **A cache write the usage split leaves out is counted.** Studio dropped cache writes that the top-level split of a
139
+ response did not account for; they are now the five-minute kind, which is how they were billed.
140
+ - **Doctor's advisor lines match how Claude Code attaches it.** An advisor below the model it would advise is not
141
+ attached at all, so critical work runs without one; the warning said it would advise a stronger model.
142
+ - **`incident-runbook` is picked for an outage that keeps coming back.** Its description named only a production that
143
+ is degraded or broken; a request about a recurring outage picked it in none of three held-out eval runs before, and
144
+ in two of three after.
145
+
146
+ ## [3.1.1] — 2026-10-12
147
+
148
+ ### Fixed
149
+
150
+ - **An update could write this machine's path into `.claude/git-shim`, a file projects commit.** The updater knew
151
+ the kit's own hooks directory only when `core.hooksPath` was spelled `.claude/hooks`. Spelled as an absolute path
152
+ (seen in a real project) it was taken for a hook chain of the project's own, and the shim named
153
+ `$ROOT//Users/<name>/…/.claude/hooks`: a path that does not exist, so nothing ran twice, but the user name and
154
+ the folder layout went into the repository. `core.hooksPath` is now read for what it names (the same directory,
155
+ whatever the spelling), a chain inside the repository is named from the root, and a chain outside it is kept in
156
+ the clone's git config (`crewforth.hookschain`) and read by the shim when it runs. A shim an earlier update
157
+ wrote for that case is removed by the next update.
158
+
8
159
  ## [3.1.0] — 2026-10-12
9
160
 
10
161
  ### Security
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center"><img src="https://raw.githubusercontent.com/Crewforth/crewforth/main/assets/logo.svg" alt="Crewforth" width="420"></p>
2
2
 
3
- **Crewforth is your engineering crew for Claude Code.** It adds subagents, skills, commands and hooks to Claude Code: 12 specialist agents that each own a domain, 40 skills that hold the method, 12 commands you start with `/crew-…`, and hooks that enforce the rules that matter.
3
+ **Crewforth is your engineering crew for Claude Code.** It adds subagents, skills, commands and hooks to Claude Code: 14 specialist agents that each own a domain, 40 skills that hold the method, 13 commands you start with `/crew-…`, and hooks that enforce the rules that matter.
4
4
 
5
5
  ```bash
6
6
  npx crewforth init # set up a new project
@@ -13,6 +13,7 @@ Then open Claude Code and run `/crew-doctor` to confirm the setup. Requires bash
13
13
 
14
14
  [![npm](https://img.shields.io/npm/v/crewforth?style=flat-square&color=6D28D9)](https://www.npmjs.com/package/crewforth)
15
15
  [![License](https://img.shields.io/badge/license-MIT-5c6472?style=flat-square)](https://github.com/Crewforth/crewforth/blob/main/LICENSE)
16
+ [![Star on GitHub](https://img.shields.io/badge/Star_on_GitHub-24292f?style=flat-square&logo=github)](https://github.com/Crewforth/crewforth)
16
17
 
17
18
  <picture>
18
19
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Crewforth/crewforth/main/assets/flow-en-dark.svg">
@@ -28,9 +29,9 @@ Then open Claude Code and run `/crew-doctor` to confirm the setup. Requires bash
28
29
 
29
30
  ## What you get
30
31
 
31
- - **12 specialist agents**, from `crew-planner` to `crew-commit-agent`, each owning one domain.
32
+ - **14 specialist agents**, from `crew-planner` to `crew-commit-agent`, each owning one domain.
32
33
  - **40 skills** holding the method: testing, migrations, API contracts, observability, accessibility, deployment.
33
- - **12 commands**, each started with its `/crew-…` name: `/crew-plan`, `/crew-review`, `/crew-ship`, `/crew-handoff`, `/crew-brainstorm`, `/crew-update`, `/crew-doctor`, `/crew-gates`, `/crew-skill`, `/crew-studio`, `/crew-approve`, `/crew-loosen`.
34
+ - **13 commands**, each started with its `/crew-…` name: `/crew-plan`, `/crew-review`, `/crew-ship`, `/crew-handoff`, `/crew-brainstorm`, `/crew-update`, `/crew-doctor`, `/crew-gates`, `/crew-skill`, `/crew-studio`, `/crew-approve`, `/crew-loosen`, `/crew-map`.
34
35
  - In `auto` mode a commit or a push waits for you: type `/crew-approve` and what was just refused is approved, tied to what is staged.
35
36
  - **Safe adoption.** `adopt` lands everything on a branch, staged and uncommitted; `main` is never touched.
36
37
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.2.0
package/adopt.sh CHANGED
@@ -273,6 +273,8 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
273
273
  "custom hooks and every other permission PRESERVED") s="özel hook'lar ve diğer tüm izinler KORUNDU" ;;
274
274
  "custom hooks/permissions PRESERVED") s="özel hook'lar ve izinler KORUNDU" ;;
275
275
  "settings.json: was missing in the project -> Crewforth's was installed") s="settings.json: projede yoktu -> Crewforth'unki kuruldu" ;;
276
+ "Status line: Crewforth's (the gate's last word, a standing approval, crew agents, fill, cost).") s="Durum satırı: Crewforth'unki (kapının son sözü, geçerli onay, crew ajanları, doluluk, maliyet)." ;;
277
+ "Status line: yours is kept; Crewforth's is one command away (see /crew-doctor).") s="Durum satırı: seninki korundu; Crewforth'unki tek komutla açılır (/crew-doctor'a bak)." ;;
276
278
  "settings.json: hook-aware MERGE (Crewforth hooks refreshed - %s)") s="settings.json: hook'ları gözeten BİRLEŞTİRME (Crewforth hook'ları yenilendi - %s)" ;;
277
279
  "settings.json: %s") s='settings.json: %s' ;;
278
280
  "merge failed -> project setting PRESERVED (not overwritten)") s='birleştirme başarısız -> proje ayarı KORUNDU (üzerine yazılmadı)' ;;
@@ -524,12 +526,53 @@ fi
524
526
  rowm 'git' "$GITKIND" # every GITKIND value is a literal with its own table row
525
527
 
526
528
  # existing hook system (decision #5 — single-hooksPath clash with husky/lefthook)
527
- HOOKSYS="none"; SHIM_CHAIN=""
529
+ # WHAT core.hooksPath NAMES, however it is spelled. It was matched as text against `.claude/hooks`, so the same
530
+ # directory written as an absolute path (seen in a real project) was read as a hook chain of the project's own:
531
+ # the shim then named `$ROOT//Users/<name>/…/.claude/hooks`, a path that does not exist and that carries the
532
+ # machine's user name into a file projects commit; had it existed, every hook would have run twice.
533
+ # Asked of the file system and of git, not of the spelling: `-ef` says whether two names are one directory, and
534
+ # `git rev-parse --show-prefix` says where a directory is inside the repository.
535
+ _hp_class(){ # $1 = a hooks path as configured -> HP_KIND: none | own | shim | in (HP_REL, from the root) | out (HP_ABS)
536
+ local p="$1" top pfx r
537
+ HP_KIND=none; HP_REL=""; HP_ABS=""
538
+ [ -n "$p" ] || return 0
539
+ case "$p" in '~/'*) p="$HOME/${p#\~/}" ;; esac
540
+ while :; do case "$p" in ?*/) p="${p%/}" ;; *) break ;; esac; done
541
+ case "$p" in
542
+ /*|[A-Za-z]:[/\\]*)
543
+ if [ -d "$p" ]; then
544
+ if [ -d .claude/hooks ] && [ "$p" -ef .claude/hooks ]; then HP_KIND=own; return 0; fi
545
+ if [ -d .claude/git-shim ] && [ "$p" -ef .claude/git-shim ]; then HP_KIND=shim; return 0; fi
546
+ top="$(git -C "$p" rev-parse --show-toplevel 2>/dev/null || true)"
547
+ if [ -n "$top" ] && [ "$top" -ef . ]; then
548
+ pfx="$(git -C "$p" rev-parse --show-prefix 2>/dev/null || true)"; pfx="${pfx%/}"
549
+ if [ -n "$pfx" ]; then HP_KIND=in; HP_REL="$pfx"; return 0; fi
550
+ fi
551
+ fi
552
+ # Not there, or git cannot place it (.git/hooks is not in the work tree): the spelling is what is left.
553
+ for r in "$PWD" "$(pwd -P 2>/dev/null || true)"; do
554
+ [ -n "$r" ] || continue
555
+ case "$p" in "$r"/?*) p="${p#"$r"/}"
556
+ case "$p" in .claude/hooks) HP_KIND=own ;; .claude/git-shim) HP_KIND=shim ;; *) HP_KIND=in; HP_REL="$p" ;; esac
557
+ return 0 ;;
558
+ esac
559
+ done
560
+ HP_KIND=out; HP_ABS="$p" ;;
561
+ *)
562
+ while :; do case "$p" in ./?*) p="${p#./}" ;; *) break ;; esac; done
563
+ case "$p" in .claude/hooks) HP_KIND=own; return 0 ;; .claude/git-shim) HP_KIND=shim; return 0 ;; esac
564
+ if [ -d "$p" ] && [ -d .claude/hooks ] && [ "$p" -ef .claude/hooks ]; then HP_KIND=own; return 0; fi
565
+ if [ -d "$p" ] && [ -d .claude/git-shim ] && [ "$p" -ef .claude/git-shim ]; then HP_KIND=shim; return 0; fi
566
+ HP_KIND=in; HP_REL="$p" ;;
567
+ esac
568
+ }
569
+ HOOKSYS="none"; SHIM_CHAIN=""; SHIM_OUT=0; CUR_OUT=0
528
570
  CURHP="$(git config --get core.hooksPath 2>/dev/null || true)"
529
- case "$CURHP" in
530
- "") : ;;
531
- .claude/hooks) HOOKSYS="Crewforth (already armed)"; CURHP="" ;; # kit's OWN path — not a foreign chain (re-adopt must not shim itself)
532
- .claude/git-shim)
571
+ _hp_class "$CURHP"
572
+ case "$HP_KIND" in
573
+ none) : ;;
574
+ own) HOOKSYS="Crewforth (already armed)"; CURHP="" ;; # kit's OWN path — not a foreign chain (re-adopt must not shim itself)
575
+ shim)
533
576
  # Crewforth's shim, from an earlier run: git is pointed at it, and IT holds the project's own chain. That chain
534
577
  # is read back from the shim, or the next lines would see "no chain" and point git straight at .claude/hooks:
535
578
  # measured, the second update of a project with core.hooksPath=myhooks left its own pre-commit never running
@@ -540,14 +583,23 @@ case "$CURHP" in
540
583
  [ -f "$_sf" ] || continue
541
584
  while IFS= read -r _sl || [ -n "$_sl" ]; do
542
585
  _sl="${_sl%$'\r'}"
586
+ # (A shim written before this was fixed can hold `P="$ROOT//abs/path/$H"`: what is left after `$ROOT/` is
587
+ # the absolute path, and it is classed like any other.)
543
588
  case "$_sl" in 'P="'*'/$H"') _sl="${_sl#P=\"}"; _sl="${_sl%/\$H\"}"; _sl="${_sl#\$ROOT/}"
544
- case "$_sl" in ''|.claude/hooks|.claude/git-shim) ;; *) SHIM_CHAIN="$_sl" ;; esac ;;
589
+ _hp_class "$_sl"
590
+ case "$HP_KIND" in in) SHIM_CHAIN="$HP_REL"; SHIM_OUT=0 ;; out) SHIM_CHAIN="$HP_ABS"; SHIM_OUT=1 ;; esac ;;
545
591
  esac
546
592
  done < "$_sf"
547
593
  [ -n "$SHIM_CHAIN" ] && break
548
594
  done
595
+ # A chain outside the repository is not written in the shim: it is in this clone's git config.
596
+ if [ -z "$SHIM_CHAIN" ]; then
597
+ _hp_class "$(git config --get crewforth.hookschain 2>/dev/null || true)"
598
+ case "$HP_KIND" in in) SHIM_CHAIN="$HP_REL"; SHIM_OUT=0 ;; out) SHIM_CHAIN="$HP_ABS"; SHIM_OUT=1 ;; esac
599
+ fi
549
600
  [ -n "$SHIM_CHAIN" ] && HOOKSYS="core.hooksPath=$SHIM_CHAIN" ;;
550
- *) HOOKSYS="core.hooksPath=$CURHP" ;;
601
+ in) CURHP="$HP_REL"; HOOKSYS="core.hooksPath=$CURHP" ;;
602
+ out) CURHP="$HP_ABS"; CUR_OUT=1; HOOKSYS="core.hooksPath=$CURHP" ;;
551
603
  esac
552
604
  [ -d .husky ] && HOOKSYS="husky (.husky/)"
553
605
  { [ -f lefthook.yml ] || [ -f .lefthook.yml ]; } && HOOKSYS="lefthook"
@@ -1623,13 +1675,22 @@ if [ -n "$SET_NOTE" ]; then HAND_SET="NOT merged — ${SET_NOTE%.}"
1623
1675
  elif [ "$SET_FRESH" = 1 ]; then HAND_SET="Crewforth's settings.json installed (the project had none, so nothing was merged)"
1624
1676
  else HAND_SET="hook-aware merge (Crewforth hooks REFRESHED to current — new events + timeouts land; your own custom hooks/permissions PRESERVED${RET_GONE:+, except the retired §4.4 ask rule(s) REMOVED: $RET_GONE — guard-bash.sh asks for these itself})"; fi
1625
1677
 
1678
+ # The status line, only where none is set (a user's own would lose to the project's setting, so it is never
1679
+ # replaced). eval/lib/statusline.sh --install decides and writes; this only says what it did.
1680
+ if [ -f .claude/eval/lib/statusline.sh ]; then
1681
+ case "$(bash .claude/eval/lib/statusline.sh --install 2>/dev/null)" in
1682
+ installed) say "Status line: Crewforth's (the gate's last word, a standing approval, crew agents, fill, cost)." ;;
1683
+ kept) say "Status line: yours is kept; Crewforth's is one command away (see /crew-doctor)." ;;
1684
+ esac
1685
+ fi
1686
+
1626
1687
  # ============ [STAGE 4] GIT-HOOK ARMING (SHIM) + PROOF ============
1627
1688
  h1m 'Stage 4 — arm the git gates (SHIM via husky) + PROOF'
1628
1689
 
1629
1690
  # 4a) location of the existing hook chain (the shim calls this too)
1630
- ORIG_HOOKS=""
1631
- if [ -n "$CURHP" ]; then ORIG_HOOKS="$CURHP"
1632
- elif [ -n "$SHIM_CHAIN" ]; then ORIG_HOOKS="$SHIM_CHAIN" # the chain the shim of an earlier run already runs
1691
+ ORIG_HOOKS=""; ORIG_OUT=0
1692
+ if [ -n "$CURHP" ]; then ORIG_HOOKS="$CURHP"; ORIG_OUT="$CUR_OUT"
1693
+ elif [ -n "$SHIM_CHAIN" ]; then ORIG_HOOKS="$SHIM_CHAIN"; ORIG_OUT="$SHIM_OUT" # the chain the shim of an earlier run already runs
1633
1694
  elif [ -d .husky ]; then ORIG_HOOKS=".husky"
1634
1695
  elif [ -x .git/hooks/pre-commit ] || [ -x .git/hooks/commit-msg ]; then ORIG_HOOKS=".git/hooks"; fi
1635
1696
  # never shim the kit onto its OWN hooks (re-adopt) — the shim would exec itself and recurse on every commit
@@ -1637,9 +1698,28 @@ case "$ORIG_HOOKS" in .claude/hooks|.claude/git-shim) ORIG_HOOKS="" ;; esac
1637
1698
 
1638
1699
  if [ -z "$ORIG_HOOKS" ]; then
1639
1700
  git config core.hooksPath .claude/hooks
1701
+ git config --unset-all crewforth.hookschain 2>/dev/null || true
1702
+ # A shim of an earlier run has no chain to run any more. It is Crewforth's own generated file (its second line
1703
+ # says so), and one written before the fix above can hold a path of this machine: it goes, and nothing else does.
1704
+ for hk in pre-commit commit-msg; do
1705
+ if [ -f ".claude/git-shim/$hk" ] && grep -q '^# kit git-shim:' ".claude/git-shim/$hk" 2>/dev/null; then rm -f ".claude/git-shim/$hk"; fi
1706
+ done
1707
+ [ -d .claude/git-shim ] && rmdir .claude/git-shim 2>/dev/null || true
1640
1708
  say 'core.hooksPath -> .claude/hooks (no existing hook chain)'
1641
1709
  else
1642
1710
  mkdir -p .claude/git-shim
1711
+ # THE SHIM HOLDS NO PATH OF THIS MACHINE. A chain inside the repository is named from the root. One outside it
1712
+ # (a hooks directory in the user's home) is kept in this clone's git config, which is not committed, and the shim
1713
+ # reads it when it runs.
1714
+ if [ "$ORIG_OUT" = 1 ]; then
1715
+ git config crewforth.hookschain "$ORIG_HOOKS"
1716
+ _shp='C="$(git config --get crewforth.hookschain 2>/dev/null || true)"
1717
+ P=""
1718
+ [ -z "$C" ] || P="$C/$H"'
1719
+ else
1720
+ git config --unset-all crewforth.hookschain 2>/dev/null || true
1721
+ _shp="P=\"\$ROOT/$ORIG_HOOKS/\$H\""
1722
+ fi
1643
1723
  for hk in pre-commit commit-msg; do
1644
1724
  cat > ".claude/git-shim/$hk" <<SHIM
1645
1725
  #!/usr/bin/env bash
@@ -1648,7 +1728,7 @@ set -e
1648
1728
  H="\$(basename "\$0")"
1649
1729
  ROOT="\$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
1650
1730
  K="\$ROOT/.claude/hooks/\$H"; [ -x "\$K" ] && "\$K" "\$@"
1651
- P="\$ROOT/$ORIG_HOOKS/\$H"
1731
+ $_shp
1652
1732
  if [ -x "\$P" ]; then "\$P" "\$@"
1653
1733
  elif [ -f "\$P" ]; then bash "\$P" "\$@"; fi
1654
1734
  exit 0
@@ -49,7 +49,8 @@ verify: npm test -- orders
49
49
  `architecture` · `test-run` · `test-write` · `audit`. `verify` is the command that proves the work, or `none`.
50
50
 
51
51
  **Risk class.** `critical` when the change is `migration`, `security`, `architecture` or `fix-unknown`, or a file is
52
- on a critical path (auth, payments, billing, migrations, security, crypto, secrets, `*.sql`, `schema.prisma`; the
52
+ on a critical path (auth, payments, billing, migrations, security, crypto, secrets, legal, privacy, consent, terms,
53
+ `*.sql`, `schema.prisma`, store-listing text under `store/` or `fastlane/metadata/`; the
53
54
  project's own are listed in `.claude/state/crew-critical-paths.auto`, and `.claude/crew-model-rules` adds or removes).
54
55
  Otherwise `normal`.
55
56
 
package/kit/CLAUDE.md CHANGED
@@ -34,12 +34,13 @@ recommend, with the rationale.**
34
34
  code — RISK decides, not size: behaviour changes and a subagent's diff review go to the owner.
35
35
 
36
36
  **Classify before the first tool call:** name the DOMAIN in your own words, then read its owner from
37
- `.claude/agents/`. Classify *intent*, never wording — the request arrives in any language.
37
+ `.claude/agents/`. Classify *intent*, not wording, in any language.
38
38
  What the user sees → **crew-frontend-expert** · server behaviour → **crew-backend-expert** · stored data →
39
39
  **crew-database-expert** · run/deploy/CI → **crew-devops-expert** · **crew-security-expert** ·
40
- **crew-privacy-agent** (personal data) · **crew-test-expert** · **crew-performance-expert** · what to build →
40
+ **crew-privacy-agent** (personal data) · **crew-test-expert** · **crew-performance-expert** · docs/legal/store
41
+ text → **crew-docs-agent** · what to build →
41
42
  **crew-planner** · unknown cause → **general-purpose** + `systematic-debugging`. Two domains → delegate in
42
- sequence. Every agent above is installed; "no owner" is an `ls`, not a default.
43
+ sequence. All are installed: "no owner" is an `ls`.
43
44
 
44
45
  1. **Diagnose, then plan** — unknown cause → `systematic-debugging` first; unclear scope → **crew-planner** (`/crew-plan`).
45
46
  2. **Produce** — the domain owner above.
@@ -49,8 +50,8 @@ sequence. Every agent above is installed; "no owner" is an `ls`, not a default.
49
50
  A finding or a red test goes back to the owner that wrote the code, and after the fix **all of them run
50
51
  again**: the diff they cleared no longer exists.
51
52
  4. **Close — only once 3 is clean.** DoD → **crew-review-agent** (LAST, never first) clean → **crew-commit-agent**
52
- proposes, waits for approval (`/crew-ship`); held items close here.
53
- 5. **Hand off** — phase boundary or full context → **crew-session-manager** → `handoff` → `/clear` (`/crew-handoff`).
53
+ writes the message, not you; waits for approval (`/crew-ship`); held items close here.
54
+ 5. **Hand off** — phase boundary or 🔋 ≥ medium → **crew-session-manager** → `handoff` → `/clear` (`/crew-handoff`).
54
55
 
55
56
  **Model per call — RISK decides, not size.** A `crew-*` task opens with its card, and the call names `model`:
56
57
  `files:` files/globs · `change:` text|feature|fix-known|fix-unknown|refactor|migration|security|architecture|test-run|test-write|audit
@@ -126,9 +127,9 @@ more tokens, because each one re-pays for its own context — plan for that.
126
127
  > **Honest boundary.** Measuring fill **is a gate** (`context-usage.sh` + `session-guard.sh`). The four bullets above
127
128
  > are **model discipline** — no exit code can judge a delegate-or-not call, so they rest on your reasoning.
128
129
 
129
- ## Session management (crew-session-manager)
130
- Last line of a reply, after the next step: `🔋 Session: [low/medium/high fill] · Recommendation: [continue / handoff+clear / new session]`
131
- — **only when you have a reading**; with none there is nothing to report, so omit the line.
130
+ ## Session status line
131
+ Last line of a reply, after the next step: `🔋 Session %NN → [low/medium/high] fill · Recommendation: [continue / handoff+clear / new session]`
132
+ — **only with a reading** (%NN is its measured figure); with none, omit the line.
132
133
 
133
134
  **Never guess the fill.** You cannot run `/context`; the `UserPromptSubmit` hook injects the measured line
134
135
  `🔋 Session %NN.N → level` every turn (`input + cache_read + cache_creation` = the `/context` figure). Use it. Exact
@@ -2,8 +2,8 @@
2
2
  name: crew-commit-agent
3
3
  color: cyan
4
4
  description: |
5
- Commit message specialist (thin trigger). Reads the staged diff and proposes a Conventional Commits message
6
- via the `commit-message` skill. Writes no source; commits only with user approval.
5
+ Use immediately after the DoD is clean, before any git commit: proposes the Conventional Commits message
6
+ from the staged diff (`commit-message`). Commits only with user approval.
7
7
  tools: Read, Grep, Glob, Bash, PowerShell
8
8
  model: haiku
9
9
  metadata:
@@ -102,6 +102,11 @@ write is refused because the file is on a critical path, do not reach it another
102
102
  `/crew-approve push` or `/crew-approve commit+push`. The user approves by sending
103
103
  that command alone; a sentence that says yes is not an approval, and you cannot run `/crew-approve` for them. It covers what is staged on that HEAD. If the gate says the approval path is closed in this session (the
104
104
  recording hook is not wired), say so and offer Shift+Tab or their own terminal instead.
105
+ In `auto` the refusal may also offer the approval question: show the message, then call AskUserQuestion with
106
+ exactly the input the refusal prints (the fixed question and its four answers, no answers of your own). The
107
+ user's click is recorded the same way; read the line Crewforth adds and run what it names. Where a
108
+ `PermissionRequest` hook may be wired the gate closes the question, and in `default` / `acceptEdits` its own
109
+ prompt too, because such a hook can answer either before the user does: then `/crew-approve` is the way.
105
110
  Then run `git commit -m '…'` alone in its call (single-quoted, or from a here-document with a quoted delimiter),
106
111
  and a push as `git push <remote> <branch>`. Never write that message yourself. Under `plan` and
107
112
  `bypassPermissions` the gate fails closed; there the user must switch modes or pre-authorise with
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: crew-docs-agent
3
+ color: yellow
4
+ description: |
5
+ Use proactively for docs: README, guides, release notes, legal, privacy and store texts. Writes no code.
6
+ tools: Read, Grep, Glob, Edit, Write
7
+ metadata:
8
+ stage: produce
9
+ skills: [docs-writer, i18n-integrity]
10
+ ---
11
+
12
+ # Docs Agent
13
+
14
+ <!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
15
+ Trigger phrases: "readme", "user guide", "release notes", "changelog entry", "privacy policy", "terms of service", "terms of use", "cookie notice", "consent text", "legal text", "store listing", "app store description", "play store description", "documentation page"
16
+
17
+ Writes the text; the `docs-writer` skill holds how. Code is someone else's: a sentence inside a component goes to its
18
+ owner, a document beside it comes here.
19
+
20
+ ## Expertise stance (technical writer who has been sued once)
21
+ - **Every claim is checkable.** A number, a flag, a behaviour is read from the code or the tool's own documentation
22
+ before it is written; what could not be checked is written as not checked, or left out.
23
+ - **The reader acts on it.** Lead with what they do, then why; one idea per paragraph, no history in a guide.
24
+ - **Legal and privacy text is not paraphrased into a promise.** It says what the product does, measured from the code
25
+ and the data flow, and nothing the product does not do. Anything that reads as a legal commitment the code does
26
+ not back goes to the user before it is written.
27
+ - **Both languages or neither.** A text the project ships in two languages changes in both in the same change
28
+ (`i18n-integrity`).
29
+
30
+ ## When
31
+ README, guides, docs pages, release notes and CHANGELOG, legal and privacy texts (`legal/`, `privacy*`, `terms*`,
32
+ `consent*`), store listings (`fastlane/metadata/`, `store/`, `store-listing/`, `metadata/android|ios/`).
33
+
34
+ ## How (follow its skill)
35
+ `docs-writer`: structure, what a README states and what it does not, the voice. `i18n-integrity` when the text has a
36
+ translation. The model follows the RISK: legal, privacy and store texts are critical paths, so their card runs on
37
+ the critical tier and the write itself is refused on a lower model.
38
+
39
+ ## Coordination (cross-agent)
40
+ - A legal or privacy text is audited by **crew-privacy-agent** before it closes: what the text says against what the
41
+ code collects, keeps and shares.
42
+ - A sentence about behaviour the code does not have goes to the owner of that code first, not into the text.
43
+
44
+ ## DoD
45
+ Every checkable statement checked; translations in step; the audit above clean for a legal or privacy text.
46
+
47
+ ## Output & context (token)
48
+ To the main thread: what changed, in which files, and every statement that could not be checked. Not the text itself.
49
+
50
+ ## Errors/escalation
51
+ A claim the code contradicts, or a legal commitment nobody has decided: stop and report it, do not soften it into
52
+ the text.
53
+
54
+ ## Example delegation
55
+ - ✅ "update the privacy policy for the new retention period"
56
+ - ❌ "add a consent checkbox to the signup form" (UI: crew-frontend-expert)
57
+
58
+ ## Confidence
59
+ The LAST line of every report is exactly `confidence: high` or exactly `confidence: low`: lower case, nothing else
60
+ on the line, nothing after it. `low` when you guessed, could not verify, or the task was above the model this run
61
+ was given; the caller then repeats it once, one model up. Your task opens with a card (`files:`, `change:`,
62
+ `verify:`): the `verify` command is run when you stop, and if it fails you are asked once to fix the work. If a
63
+ write is refused because the file is on a critical path, do not reach it another way: stop, and put
64
+ `escalate: <the file>` on the line before `confidence: low`.
65
+
66
+ ## Constraints
67
+ - Writes text files only; no source, no configuration, no build files.
68
+ - Does not invent a policy: what the product does is read from the code, what it should do is the user's decision.
69
+
70
+ ## Prohibitions (absolute)
71
+ CLAUDE.md §4 applies: no AI trace, no third-party template name, nothing under `docs/` named in a shipped text.
@@ -33,6 +33,8 @@ before planning. You are read-only: when it ends at "ask", return its questions
33
33
  decision — the main thread asks and records the answer.
34
34
 
35
35
  ## How (applies the `spec-planning` skill)
36
+ - On an unfamiliar project, read `.crewforth/crew-map.json` first when it exists (its modules, what they touch,
37
+ the gaps, each with the lines it rests on); with none, `/crew-map` writes one, and that is the user's call.
36
38
  - Define the problem in a single sentence; clarify what is in scope and out of scope.
37
39
  - Break tasks into atomic steps; derive the dependency order.
38
40
  - For each step, write acceptance criteria (how "done" is judged).
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: crew-refuter
3
+ color: gray
4
+ description: |
5
+ Use proactively on each medium+ finding before it is reported: given one claim and its file:line, refutes it.
6
+ tools: Read, Grep, Glob
7
+ metadata:
8
+ stage: audit
9
+ skills: [security-scan]
10
+ ---
11
+
12
+ # Refuter
13
+
14
+ <!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
15
+ Trigger phrases: "refute this finding", "disprove this claim", "try to prove it wrong", "verify this finding independently", "is this finding a false positive"
16
+
17
+ Read-only. A fresh context is the point: it has not seen the reasoning that produced the finding, so it cannot
18
+ inherit its blind spot. The method is `security-scan` → `references/verify.md`.
19
+
20
+ ## When
21
+ After discovery, before a finding is written to a report or a findings document. Every finding of `medium` or
22
+ higher severity goes through it; `critical` and `high` through three calls, each on a different lens.
23
+
24
+ ## Expertise stance (the colleague whose job is to find the hole in your argument)
25
+ - **Starts from the cited lines, never from the summary.** Reads the code at `cite:` first and only then the claim.
26
+ - **Hunts for why the claim is false**: unreachable input, a guard on every path, a test or dead file, a control
27
+ the claim did not look for. Agreement is what is left when that search fails, not where it starts.
28
+ - **One lens, held to it**: reachability, protection, reproduction, exclusion or blast-radius — the one it is given.
29
+ - **Names the missing fact** instead of guessing: a verdict it cannot reach from the repository is CANNOT_VERIFY.
30
+
31
+ ## How (follow its skill)
32
+ `security-scan` → `references/verify.md`: the verifier stance, the lens, the three verdicts and the exclusion
33
+ rules. The task it is given is exactly this, and nothing of the caller's reasoning. It opens with the card every
34
+ crew agent is called with (`change: audit`, so the model follows the risk of the cited file):
35
+
36
+ ```
37
+ files: <the cited file>
38
+ change: audit
39
+ verify: none
40
+ finding: <the finding's id in the findings document>
41
+ lens: reachability | protection | reproduction | exclusion | blast-radius
42
+ cite: <file>:<line>
43
+ claim: <one sentence>
44
+ ```
45
+
46
+ ## Output & context (token)
47
+ Its last lines are read by `hooks/refuter-ledger.sh`, which records the verdict against the finding and lens of
48
+ the task it was given, so they are exactly:
49
+
50
+ ```
51
+ FINDING: <id>
52
+ LENS: <lens>
53
+ VERDICT: TRUE_POSITIVE | FALSE_POSITIVE | CANNOT_VERIFY
54
+ REASON: <one or two sentences, citing file:line>
55
+ ```
56
+
57
+ ## Confidence
58
+ The LAST line of every report is exactly `confidence: high` or exactly `confidence: low`: lower case, nothing else
59
+ on the line, nothing after it. `low` when you guessed or could not read what the claim needs; the caller then
60
+ repeats the call once, one model up. Your task opens with a card (`files:`, `change: audit`, `verify: none`), so
61
+ nothing is run when you stop. The `FINDING`, `LENS`, `VERDICT` and `REASON` lines come before it.
62
+
63
+ ## Errors/escalation
64
+ A task without `finding:`, `lens:` and `cite:` is answered with `VERDICT: CANNOT_VERIFY` and the reason, never
65
+ with a guess; the ledger records nothing for it.
66
+
67
+ ## Example delegation
68
+ - ✅ "finding: sec-idor-orders · lens: protection · cite: src/orders.ts:88 · claim: any user can read any order"
69
+ - ❌ "audit the orders module for security issues" (discovery: crew-security-expert)
70
+
71
+ ## Constraints
72
+ - Read-only: no Edit, no Write, no Bash, and no `Agent`, so it cannot hand the question to a context that has
73
+ seen more than it should.
74
+ - One claim per call. The caller runs the calls of one finding in parallel, never in one context.
75
+ - HONEST SCOPE: what keeps the record true is the hook writing the ledger from this agent's own task and answer;
76
+ what keeps the answer independent is that the caller gave it the claim and not the reasoning, which is model
77
+ discipline.
@@ -27,7 +27,7 @@ Read-only auditor. The relevant expert (backend/database) makes the fix; this ag
27
27
  - **Prove** each finding: how it's exploited + impact + fix; not a theoretical warning.
28
28
  - Assign a **severity** to every finding; high-impact first — **derived from preconditions × access, not the category** ("real" is not "critical").
29
29
  - **Defense in depth**: don't rely on a single control; add layers.
30
- - **Signal, not noise**: every candidate goes through the **adversarial verify pass** (N independent verifiers that start from the code and hunt for why it's *wrong* → TRUE_POSITIVE / FALSE_POSITIVE / CANNOT_VERIFY). See `security-scan` → `references/verify.md`.
30
+ - **Signal, not noise**: every candidate goes through the **adversarial verify pass** (N independent verifiers that start from the code and hunt for why it's *wrong* → TRUE_POSITIVE / FALSE_POSITIVE / CANNOT_VERIFY). See `security-scan` → `references/verify.md`. This agent has no `Agent` tool: it returns the candidates, and the session runs one **crew-refuter** per finding and lens.
31
31
 
32
32
  ## When
33
33
  On changes touching auth, token/credential, externally exposed endpoints, or sensitive data.
@@ -52,6 +52,8 @@ Each finding: `file:line · risk · suggested fix`, or a "clean on this axis" ra
52
52
 
53
53
  ## Output & context (token)
54
54
  To the main thread: a **severity-ranked findings summary** (area · severity · fix). Full scan output → `docs/SECURITY_FINDINGS.md`, returns a summary + count.
55
+ The candidates also go out as a findings document (`kit/eval/lib/findings`, one `id` and `file:line` evidence each) for the session to verify; the
56
+ verified one is stored with `findings.sh write --verified security <file>` (`eval/lib/findings/` in either edition), which refuses a `medium`-or-higher finding without its refuters.
55
57
 
56
58
  ## Errors/escalation
57
59
  On an exploitable CRITICAL finding, **warn clearly**; don't report a finding you're unsure of as 'certain', add a verifiability note.