@oneie/claude 0.7.0 โ†’ 0.9.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 (263) hide show
  1. package/agents/abm-strategist.md +89 -0
  2. package/agents/ads-meta.md +97 -0
  3. package/agents/analyst.md +173 -0
  4. package/agents/architect.md +280 -0
  5. package/agents/brand-guardian.md +88 -0
  6. package/agents/brand-strategist.md +92 -0
  7. package/agents/campaign-content.md +90 -0
  8. package/agents/campaign-email.md +88 -0
  9. package/agents/campaign-sms.md +87 -0
  10. package/agents/campaign-social.md +87 -0
  11. package/agents/cco.md +99 -0
  12. package/agents/ceo.md +106 -0
  13. package/agents/chairman.md +120 -0
  14. package/agents/cmo.md +101 -0
  15. package/agents/community-greeter.md +89 -0
  16. package/agents/community-moderator.md +92 -0
  17. package/agents/compliance.md +92 -0
  18. package/agents/copywriter.md +100 -0
  19. package/agents/creative-strategist.md +95 -0
  20. package/agents/cro.md +98 -0
  21. package/agents/cto.md +267 -0
  22. package/agents/customer-interviewer.md +93 -0
  23. package/agents/customer-researcher.md +89 -0
  24. package/agents/customer-success-manager.md +90 -0
  25. package/agents/customer-trainer.md +90 -0
  26. package/agents/cxo.md +100 -0
  27. package/agents/demand-creator.md +89 -0
  28. package/agents/demo-mover.md +83 -0
  29. package/agents/demo-specialist.md +89 -0
  30. package/agents/demo-thai-family-law.md +87 -0
  31. package/agents/designer.md +112 -0
  32. package/agents/discovery-caller.md +98 -0
  33. package/agents/doctor.md +139 -0
  34. package/agents/educate-coach.md +100 -0
  35. package/agents/elevate-tutor.md +83 -0
  36. package/agents/email-lifecycle-marketer.md +91 -0
  37. package/agents/engage-specialist.md +99 -0
  38. package/agents/events-coordinator.md +90 -0
  39. package/agents/foundation-builder.md +94 -0
  40. package/agents/funnel-architect.md +89 -0
  41. package/agents/gift-creator.md +100 -0
  42. package/agents/google-ads.md +103 -0
  43. package/agents/guide.md +292 -0
  44. package/agents/helpdesk-dispatcher.md +88 -0
  45. package/agents/hook-specialist.md +91 -0
  46. package/agents/identify-optimizer.md +101 -0
  47. package/agents/implementer.md +305 -0
  48. package/agents/incident-commander.md +120 -0
  49. package/agents/insights-lead.md +93 -0
  50. package/agents/journey-runner.md +113 -0
  51. package/agents/linkedin-ads.md +123 -0
  52. package/agents/live-sales-chat.md +90 -0
  53. package/agents/market-researcher.md +88 -0
  54. package/agents/media-buyer.md +96 -0
  55. package/agents/movers-customer-researcher.md +95 -0
  56. package/agents/movers-foundation-builder.md +96 -0
  57. package/agents/movers-market-researcher.md +97 -0
  58. package/agents/movers-pricing-strategist.md +171 -0
  59. package/agents/nurture-architect.md +99 -0
  60. package/agents/offer-architect.md +92 -0
  61. package/agents/onboarder.md +222 -0
  62. package/agents/onboarding-specialist.md +92 -0
  63. package/agents/operations-dashboard.md +98 -0
  64. package/agents/perf-engineer.md +348 -0
  65. package/agents/playbook-writer.md +71 -0
  66. package/agents/plg-strategist.md +91 -0
  67. package/agents/positioning-architect.md +88 -0
  68. package/agents/press-officer.md +89 -0
  69. package/agents/pricing-strategist.md +94 -0
  70. package/agents/privacy-officer.md +92 -0
  71. package/agents/referral-manager.md +91 -0
  72. package/agents/refine-analyst.md +102 -0
  73. package/agents/release-manager.md +261 -0
  74. package/agents/renewals-upsell-rep.md +90 -0
  75. package/agents/review-engineer.md +327 -0
  76. package/agents/rewards-steward.md +82 -0
  77. package/agents/sales-call-coach.md +94 -0
  78. package/agents/sales-closer.md +98 -0
  79. package/agents/security-auditor.md +313 -0
  80. package/agents/sell-closer.md +101 -0
  81. package/agents/share-amplifier.md +99 -0
  82. package/agents/social-media-manager.md +92 -0
  83. package/agents/storyteller.md +250 -0
  84. package/agents/strategist.md +83 -0
  85. package/agents/strategy-aligner.md +94 -0
  86. package/agents/support-agent.md +90 -0
  87. package/agents/tagger.md +245 -0
  88. package/agents/tech-writer.md +216 -0
  89. package/agents/test-engineer.md +243 -0
  90. package/agents/tiktok-ads.md +105 -0
  91. package/agents/tracking-engineer.md +92 -0
  92. package/agents/upsell-strategist.md +101 -0
  93. package/agents/voice.md +85 -0
  94. package/agents/w4-tools.md +153 -0
  95. package/agents/w4-verify.md +20 -138
  96. package/agents/workflow-optimiser.md +84 -0
  97. package/commands/close.md +814 -160
  98. package/commands/create.md +2 -2
  99. package/commands/deploy.md +554 -175
  100. package/commands/do.md +119 -109
  101. package/commands/fast.md +67 -0
  102. package/commands/improve.md +2 -2
  103. package/commands/one.md +418 -0
  104. package/commands/see.md +1 -1
  105. package/commands/sync.md +1 -1
  106. package/commands/tasks.md +222 -0
  107. package/commands/vespio.md +110 -0
  108. package/commands/vespio.remote.md +227 -0
  109. package/hooks/hooks.json +25 -79
  110. package/hooks/lib/governor-escape-match.sh +88 -0
  111. package/hooks/lib/hook.sh +4 -13
  112. package/hooks/lib/signal.sh +9 -2
  113. package/hooks/scripts/branch-pin.sh +6 -0
  114. package/hooks/scripts/config-protect.sh +6 -0
  115. package/hooks/scripts/do-outcome-gate.sh +19 -2
  116. package/hooks/scripts/git-add-guard.sh +43 -2
  117. package/hooks/scripts/governor-escape.sh +96 -0
  118. package/hooks/scripts/load-guard.sh +127 -0
  119. package/hooks/scripts/post-edit-check.sh +36 -29
  120. package/hooks/scripts/session-start.sh +34 -80
  121. package/hooks/scripts/task-complete-verify.sh +46 -40
  122. package/package.json +1 -1
  123. package/rules/documentation.md +9 -0
  124. package/scripts/ad-copy-lint.sh +656 -0
  125. package/scripts/agentverse-audit.sh +256 -0
  126. package/scripts/asi-walk.sh +435 -0
  127. package/scripts/astro-build-cached.sh +143 -0
  128. package/scripts/blocks-manifest-cached.sh +100 -0
  129. package/scripts/blocks-manifest.mjs +497 -0
  130. package/scripts/blocks-ratchet.sh +116 -0
  131. package/scripts/blocks-render-probe.mjs +529 -0
  132. package/scripts/blocks-usage.mjs +334 -0
  133. package/scripts/chat-context-check.sh +89 -0
  134. package/scripts/close-metrics.sh +558 -0
  135. package/scripts/close-owner.sh +268 -0
  136. package/scripts/db-sync-lock-check.sh +116 -0
  137. package/scripts/deploy-dev.sh +35 -0
  138. package/scripts/deploy-emit.sh +298 -0
  139. package/scripts/deploy-gate-check.sh +128 -0
  140. package/scripts/deploy-ready.sh +78 -0
  141. package/scripts/deploy-record.sh +605 -0
  142. package/scripts/deploy.sh +1273 -0
  143. package/scripts/do-auto.sh +598 -48
  144. package/scripts/do-brief.sh +113 -0
  145. package/scripts/do-close.sh +1137 -0
  146. package/scripts/do-commit.sh +75 -0
  147. package/scripts/do-consumer-sweep.sh +18 -1
  148. package/scripts/do-cycle-shape-check.sh +160 -0
  149. package/scripts/do-decide.sh +476 -0
  150. package/scripts/do-derive-check.sh +436 -0
  151. package/scripts/do-fleet.sh +106 -28
  152. package/scripts/do-folder.sh +10 -1
  153. package/scripts/do-next.sh +106 -0
  154. package/scripts/do-orchestrate.sh +17 -5
  155. package/scripts/do-plan-json.mjs +201 -0
  156. package/scripts/do-plan-json.sh +8 -0
  157. package/scripts/do-preflight.sh +117 -0
  158. package/scripts/do-project.sh +157 -0
  159. package/scripts/do-prove-selftest.sh +108 -0
  160. package/scripts/do-prove.sh +295 -23
  161. package/scripts/do-rank.py +31 -1
  162. package/scripts/do-recon-cache.sh +7 -1
  163. package/scripts/do-recon-pack.sh +196 -0
  164. package/scripts/do-reconcile.sh +121 -4
  165. package/scripts/do-signal.sh +280 -23
  166. package/scripts/do-smoke.sh +18 -1
  167. package/scripts/do-test-gate.sh +80 -0
  168. package/scripts/do-tick.sh +102 -0
  169. package/scripts/do-tier.sh +6 -0
  170. package/scripts/do-triage.sh +182 -0
  171. package/scripts/do-ui-gate.sh +1 -1
  172. package/scripts/do-w4-gates.sh +451 -0
  173. package/scripts/do-walk.sh +12 -1
  174. package/scripts/env-sync.sh +173 -0
  175. package/scripts/factory-brief-check.sh +330 -0
  176. package/scripts/factory-check.sh +68 -14
  177. package/scripts/factory-close-check.sh +257 -0
  178. package/scripts/factory-emit.sh +211 -0
  179. package/scripts/factory-executor-check.mjs +353 -0
  180. package/scripts/factory-peak.sh +301 -0
  181. package/scripts/factory-repo.sh +118 -3
  182. package/scripts/factory-review-check.mjs +61 -0
  183. package/scripts/factory-ship.sh +61 -0
  184. package/scripts/factory-tasks-check.sh +18 -1
  185. package/scripts/factory-turn.sh +326 -0
  186. package/scripts/factory-walk.sh +396 -0
  187. package/scripts/factory-width.sh +57 -0
  188. package/scripts/fade-toxic.sh +4 -3
  189. package/scripts/fixtures/factory-brief-real.md +44 -0
  190. package/scripts/fixtures/triage-dupe.md +5 -0
  191. package/scripts/fleet-manifest.mjs +108 -0
  192. package/scripts/fleet-status.sh +110 -0
  193. package/scripts/full-suite-paths-check.sh +144 -0
  194. package/scripts/gate-reaper-check.sh +98 -0
  195. package/scripts/gate-reaper.sh +125 -0
  196. package/scripts/gate-run.sh +73 -0
  197. package/scripts/gc-content-check.sh +140 -0
  198. package/scripts/gen-dev-config.py +20 -0
  199. package/scripts/govern-bound-check.sh +60 -0
  200. package/scripts/govern-claims-check.sh +233 -0
  201. package/scripts/govern-mem-check.sh +290 -0
  202. package/scripts/governor-doors-check.sh +362 -0
  203. package/scripts/governor-escape-check.sh +171 -0
  204. package/scripts/health.sh +413 -0
  205. package/scripts/id-inventory.mjs +418 -0
  206. package/scripts/land.sh +551 -0
  207. package/scripts/lib/gc-finished.sh +77 -0
  208. package/scripts/lib/govern.sh +361 -0
  209. package/scripts/lib/govern.ts +756 -0
  210. package/scripts/lighthouse-run.sh +187 -0
  211. package/scripts/livekit-live-check.sh +61 -0
  212. package/scripts/livekit-ratchet.sh +46 -0
  213. package/scripts/load-guard-check.sh +49 -0
  214. package/scripts/machine-check.sh +102 -0
  215. package/scripts/machine-watch.sh +177 -0
  216. package/scripts/one-agents.mjs +415 -0
  217. package/scripts/one-resume.sh +78 -0
  218. package/scripts/orphan-baseline.json +182 -0
  219. package/scripts/orphan-modules.mjs +179 -0
  220. package/scripts/pr-body.sh +335 -0
  221. package/scripts/preview-fd-check.sh +289 -0
  222. package/scripts/promise-manifest.mjs +24 -1
  223. package/scripts/release.sh +322 -0
  224. package/scripts/roles-check.sh +946 -0
  225. package/scripts/sdk-build-cached.sh +64 -0
  226. package/scripts/signal-watch.sh +241 -0
  227. package/scripts/skills-publish.sh +94 -0
  228. package/scripts/speed-cache-check.sh +173 -0
  229. package/scripts/speed-check.mjs +907 -0
  230. package/scripts/speed-parity-check.sh +648 -0
  231. package/scripts/speed-waterfall-check.sh +355 -0
  232. package/scripts/substrate-env-parity.mjs +156 -0
  233. package/scripts/tasks-claim-race.mjs +108 -0
  234. package/scripts/tasks-loop.sh +185 -0
  235. package/scripts/test-cached.sh +255 -0
  236. package/scripts/test-full.sh +87 -0
  237. package/scripts/test-honesty.mjs +137 -0
  238. package/scripts/test-lanes.sh +166 -0
  239. package/scripts/test-speed.sh +94 -0
  240. package/scripts/triage-shape-check.sh +149 -0
  241. package/scripts/tsc-cached.sh +179 -0
  242. package/scripts/typedb-flake-check.sh +197 -0
  243. package/scripts/urls-lint.sh +15 -0
  244. package/scripts/verify-fast.sh +445 -0
  245. package/scripts/vespio-sync.sh +149 -0
  246. package/scripts/wf-check.mjs +104 -1
  247. package/scripts/worktree-preview.sh +879 -0
  248. package/scripts/worktree-up.sh +208 -0
  249. package/skills/livekit-agents/SKILL.md +285 -0
  250. package/skills/livekit-agents/references/freshness-rules.md +168 -0
  251. package/skills/shadcn/SKILL.md +1 -1
  252. package/skills/signal/SKILL.md +0 -1
  253. package/skills/voice/SKILL.md +94 -6
  254. package/skills/voice/corpus-check.sh +87 -0
  255. package/hooks/scripts/compact-hint.sh +0 -35
  256. package/hooks/scripts/gate-guard.sh +0 -83
  257. package/hooks/scripts/read-tracker.sh +0 -26
  258. package/hooks/scripts/session-end-verify.sh +0 -51
  259. package/hooks/scripts/stop-reflect.sh +0 -140
  260. package/hooks/scripts/sync-priority-todo.sh +0 -57
  261. package/hooks/scripts/sync-todo-docs.sh +0 -46
  262. package/hooks/scripts/tool-signal.sh +0 -48
  263. package/scripts/do-tasks-bridge.py +0 -366
@@ -120,8 +120,25 @@ if ! printf '%s' "$OUTCOME" | grep -qE '^[-a-zA-Z0-9 /._&=|]+$'; then
120
120
  fi
121
121
 
122
122
  echo "do-outcome-gate: running plan kill-switch for $SLUG..." >&2
123
- bash -c "$OUTCOME" 2>&1
124
- EXIT=$?
123
+ # THROUGH THE GOVERNOR. Fifteen text/*-todo.md carry `bun run verify` in
124
+ # outcome:, and one of them was already ticked โ€” so an edit to that file fired a
125
+ # FULL suite plus an ungoverned bare vitest from a PostToolUse hook, with nothing
126
+ # asking for a slot (text/factory-do.md ยง Deploy is the ship gate, measured
127
+ # 2026-09-03). The hook is the N-agents-N-full-suites shape exactly when twelve
128
+ # sessions each tick a box. gate-run.sh queues it behind the machine-wide cap
129
+ # and prices it against real free memory; the wait is bounded so a hook never
130
+ # holds an editor for the governor's default 30 minutes. No slot inside the
131
+ # bound is NOT a pass โ€” the box is the claim, and the box is not ticked: exit 1
132
+ # says so and names the fix. `bun run verify` inside the string is itself
133
+ # wrapped, and gate-run is re-entrant, so this is one slot, not two.
134
+ GATE="$CLAUDE_PROJECT_DIR/.claude/scripts/gate-run.sh"
135
+ if [ -f "$GATE" ]; then
136
+ GOVERN_QUEUE_WAIT="${DO_OUTCOME_GATE_WAIT:-120}" bash "$GATE" outcome-gate -- bash -c "$OUTCOME" 2>&1
137
+ EXIT=$?
138
+ else
139
+ bash -c "$OUTCOME" 2>&1
140
+ EXIT=$?
141
+ fi
125
142
 
126
143
  if [ $EXIT -eq 0 ]; then
127
144
  emit_signal "hook:do-outcome-gate:ok" 1 "slug=$SLUG"
@@ -22,6 +22,12 @@ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/hook.sh"
22
22
  is_hook_disabled "hook:git-add-guard" && exit 0
23
23
 
24
24
  PAYLOAD="${1:-}"
25
+ # Claude Code delivers the hook payload on STDIN; argv stays first so any
26
+ # caller that passes it positionally still works. Bounded read: a bare $(cat)
27
+ # would hang forever on a tty or an idle pipe, freezing every Bash call.
28
+ if [[ -z "$PAYLOAD" && ! -t 0 ]]; then
29
+ IFS= read -r -d '' -t 2 PAYLOAD <&0 || true
30
+ fi
25
31
  [[ -z "$PAYLOAD" ]] && exit 0
26
32
 
27
33
  TOOL=$(printf '%s' "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -48,7 +54,18 @@ _is_blanket() {
48
54
  fi
49
55
  return 1
50
56
  }
51
- _is_blanket "$CMD" || exit 0
57
+ # Any commit on the shared main tree โ€” main receives PRs, never development.
58
+ # QUOTE-STRIPPED and POSITION-ANCHORED, so a command that merely NAMES a commit
59
+ # โ€” a grep for "git commit", a commit message quoting it, a doc being written โ€”
60
+ # is never refused. Measured 2026-09-07: the unanchored form denied
61
+ # `grep -rn "git commit" text/`.
62
+ _is_commit() {
63
+ local bare
64
+ bare=$(printf '%s' "$1" | sed -E "s/'[^']*'//g; s/\"[^\"]*\"//g")
65
+ printf '%s' "$bare" | grep -qE '(^|[;&|][[:space:]]*)git[^;&|]*[[:space:]]commit([[:space:]]|$)'
66
+ }
67
+
68
+ _is_blanket "$CMD" || _is_commit "$CMD" || exit 0
52
69
 
53
70
  # Carve-out: deploy/release publishes the whole tree on purpose.
54
71
  printf '%s' "$CMD" | grep -qE '\bgit\b[^|&;]*\bpush\b' && exit 0
@@ -72,7 +89,31 @@ GITDIR=$(git -C "$TARGET" rev-parse --absolute-git-dir 2>/dev/null)
72
89
  [[ -z "$GITDIR" ]] && exit 0 # not a git repo โ€” let git handle it
73
90
  case "$GITDIR" in */worktrees/*) exit 0 ;; esac # isolated linked worktree โ€” blanket is safe
74
91
 
75
- # Main tree โ†’ deny.
92
+ # Main tree โ†’ deny. A plain commit gets the dev-only rule; a blanket action gets
93
+ # the sweep warning. Both refuse; the reason must name the right one.
94
+ if ! _is_blanket "$CMD"; then
95
+ CMSG="[git-add-guard] Development on the shared main tree is blocked โ€” we develop on dev.
96
+
97
+ main receives a PULL REQUEST from dev and nothing else. It is never edited,
98
+ never committed to directly, and production only ever ships from a sha that
99
+ arrived on main that way.
100
+
101
+ Do this instead:
102
+ bash .claude/scripts/worktree-up.sh <name> --no-dev # cut from dev
103
+ ...edit, then...
104
+ bash .claude/scripts/land.sh feat/<name> # lands on DEV
105
+ gh pr create --base main --head dev # the only door to main
106
+
107
+ Why: on 2026-09-07 four commits went straight onto main while seven sessions
108
+ shared this tree, and an hour-cold ad conversion map plus its test sat
109
+ uncommitted in it โ€” invisible to every branch, and one blanket stage away from
110
+ landing in a stranger's commit.
111
+
112
+ Override once, for a deliberate rescue: ECC_DISABLED_HOOKS=hook:git-add-guard"
113
+ jq -nc --arg r "$CMSG" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":$r}}'
114
+ exit 0
115
+ fi
116
+
76
117
  MSG="[git-add-guard] Blanket staging/stashing on the shared main tree is blocked.
77
118
 
78
119
  Multiple windows share this working tree. A blanket add (-A, bare dot, commit -a)
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+ # GOVERNOR-ESCAPE โ€” PreToolUse(Bash): an executor cannot escape the governor.
3
+ #
4
+ # WHY (measured 2026-08-18): several sessions each ran the full verify on one
5
+ # 10-core/24GB Mac. Six tsc and three vitest pools ran at once -> load 57, swap
6
+ # 15.7/16 GB. Starved by paging, each gate took 18 MINUTES instead of ~2, so
7
+ # sessions hit their Bash timeout and launched MORE. The box got slower the
8
+ # harder it was pushed. gate-run.sh (slot + wall-bound + group-reap) is the fix.
9
+ #
10
+ # Four doors bypass all of it, and an agent taking one is invisible to every
11
+ # arbiter in factory-do.md's ยง Concurrency:
12
+ # verify:raw ยท test:raw ยท a directly-typed vitest ยท GOVERN_DISABLE=1 / CI=1
13
+ #
14
+ # They exist for a HUMAN who means it. This hook is about an EXECUTOR reaching
15
+ # for one. hook:load-guard already refuses an ungoverned heavy command -- but
16
+ # only once load > 2x cores, free mem < 10%, or 3 gates are already burning.
17
+ # That is a check that fires AFTER the damage. This one fires INSTEAD of it.
18
+ #
19
+ # WHY A HOOK AND NOT A POST-HOC CHECK: the Bash tool does not carry shell state
20
+ # between calls, so an `export GOVERN_DISABLE=1` in one call is gone by the
21
+ # next. An env bypass therefore has to appear INLINE in the command string --
22
+ # which is precisely the surface a PreToolUse hook reads. For that class of
23
+ # escape prevention is not merely stronger than detection, it is complete.
24
+ # (Honest limit: this reads the command string, not an inherited environment.
25
+ # A GOVERN_DISABLE exported into the whole session's env is out of its view.)
26
+ #
27
+ # What counts as an escape lives in ONE place, shared with the checker:
28
+ # .claude/hooks/lib/governor-escape-match.sh
29
+ # Proof, both directions (it bites / it does not false-positive):
30
+ # bash .claude/scripts/governor-escape-check.sh
31
+ #
32
+ # Disable (the deliberate human override): ECC_DISABLED_HOOKS=hook:governor-escape
33
+
34
+ # shellcheck source=lib/hook.sh
35
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/hook.sh"
36
+ is_hook_disabled "hook:governor-escape" && exit 0
37
+ # shellcheck source=lib/governor-escape-match.sh
38
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/governor-escape-match.sh"
39
+
40
+ PAYLOAD="${1:-}"
41
+ # Claude Code delivers the hook payload on STDIN; argv stays first so any
42
+ # caller that passes it positionally still works. Bounded read: a bare $(cat)
43
+ # would hang forever on a tty or an idle pipe, freezing every Bash call.
44
+ if [[ -z "$PAYLOAD" && ! -t 0 ]]; then
45
+ IFS= read -r -d '' -t 2 PAYLOAD <&0 || true
46
+ fi
47
+ [[ -z "$PAYLOAD" ]] && exit 0
48
+
49
+ TOOL=$(printf '%s' "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
50
+ [[ "$TOOL" == "Bash" ]] || exit 0
51
+
52
+ CMD=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null)
53
+ [[ -z "$CMD" ]] && exit 0
54
+
55
+ KIND=$(gov_escape_kind "$CMD") || exit 0
56
+
57
+ case "$KIND" in
58
+ env) WHAT="an inline GOVERN_DISABLE= / CI= in front of a gate. gate-run.sh:26
59
+ execs straight through on either, so the slot, the wall-clock bound and the
60
+ process-group reap are all skipped." ;;
61
+ raw) WHAT="a \`:raw\` package script (verify:raw / test:raw). Those exist to
62
+ bypass the cap -- that is their whole definition." ;;
63
+ vitest) WHAT="vitest invoked directly. It takes no slot, has no wall-clock
64
+ bound, and its forks reparent to launchd if the session dies." ;;
65
+ esac
66
+
67
+ MSG="[governor-escape] That command bypasses the machine governor.
68
+
69
+ Detected: $WHAT
70
+
71
+ Measured 2026-08-18, before the governor existed: concurrent ungoverned gates
72
+ took this box to load 57 and swap 15.7/16 GB, and each gate ran 18 minutes
73
+ instead of ~2. The box gets SLOWER the harder it is pushed, so one agent's
74
+ shortcut is every other agent's stall.
75
+
76
+ Run the same thing governed -- it queues for a free slot instead of piling on:
77
+
78
+ bash .claude/scripts/gate-run.sh test -- bunx vitest run <path>
79
+ bash .claude/scripts/gate-run.sh verify -- bun run verify
80
+
81
+ The cheap rungs of the gate ladder are still cheap: gate-run.sh is re-entrant
82
+ and returns immediately when a slot is free, so a targeted 0.4s test stays a
83
+ targeted 0.4s test.
84
+
85
+ Or use the governed package scripts, which already do this:
86
+ bun run verify:fast (the dev lane)
87
+ bun run verify (the review gate)
88
+
89
+ See what is holding the box:
90
+ bash .claude/scripts/machine-check.sh --watch
91
+
92
+ Deliberate human override (session-level, on purpose, and visible):
93
+ ECC_DISABLED_HOOKS=hook:governor-escape"
94
+
95
+ jq -nc --arg r "$MSG" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":$r}}'
96
+ exit 0
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bash
2
+ # LOAD-GUARD โ€” PreToolUse(Bash): refuse to pile a heavy gate onto a thrashing box.
3
+ #
4
+ # WHY (measured 2026-08-18): several sessions each ran `bun run verify`
5
+ # (tsc --noEmit + vitest over 875 files) at the same time on a 10-core/24GB Mac.
6
+ # Six tsc and three vitest pools ran concurrently -> load 57, swap 15.7/16 GB,
7
+ # ~26 MB/s of swapins. Starved by paging, each gate took 18 min instead of ~2,
8
+ # so sessions hit their Bash timeout and launched MORE. The box got slower the
9
+ # harder it was pushed.
10
+ #
11
+ # package.json's verify/test/check now route through gate-run.sh, but a session
12
+ # can still type `bunx tsc --noEmit` or `vitest run` straight into Bash and
13
+ # bypass the governor entirely (observed: pid 88114). This hook is the backstop
14
+ # for that path โ€” it is the difference between a convention and a guarantee.
15
+ #
16
+ # Rule: a heavy, UNGOVERNED command is denied while the machine is already
17
+ # saturated. It is never denied on a healthy box, and never for the governed
18
+ # form (which queues by design rather than piling on).
19
+ #
20
+ # Disable: ECC_DISABLED_HOOKS=hook:load-guard
21
+
22
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/hook.sh"
23
+ is_hook_disabled "hook:load-guard" && exit 0
24
+
25
+ PAYLOAD="${1:-}"
26
+ # Claude Code delivers the hook payload on STDIN; argv stays first so any
27
+ # caller that passes it positionally still works. Bounded read: a bare $(cat)
28
+ # would hang forever on a tty or an idle pipe, freezing every Bash call.
29
+ if [[ -z "$PAYLOAD" && ! -t 0 ]]; then
30
+ IFS= read -r -d '' -t 2 PAYLOAD <&0 || true
31
+ fi
32
+ [[ -z "$PAYLOAD" ]] && exit 0
33
+ TOOL=$(printf '%s' "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
34
+ [[ "$TOOL" == "Bash" ]] || exit 0
35
+ CMD=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null)
36
+ [[ -z "$CMD" ]] && exit 0
37
+
38
+ # Already governed (gate-run.sh queues rather than piling) โ€” always allow.
39
+ printf '%s' "$CMD" | grep -q 'gate-run\.sh' && exit 0
40
+ # Explicit opt-out for a deliberate full-throughput run.
41
+ [[ "${GOVERN_DISABLE:-0}" == "1" ]] && exit 0
42
+
43
+ # Heavy = spawns a long multi-core, multi-hundred-MB job.
44
+ # Heavy only when INVOKED, never when merely NAMED. The previous pattern
45
+ # matched the bare word anywhere in the command string, so `sed -n 1,60p
46
+ # vitest.config.ts` and a `git commit -m` whose message quoted `bun run
47
+ # verify` were both refused as though they were gates. Reading a config is
48
+ # not running a suite; both cost a retry through a temp file while fixing the
49
+ # engine on 2026-08-31.
50
+ #
51
+ # Anchor to command POSITION: start of the command or just after a shell
52
+ # operator, allowing leading env assignments and a bunx/npx/bun/npm runner.
53
+ # This decides only WHETHER the command is a heavy gate -- it still falls
54
+ # through to the saturation check below, which is what actually denies.
55
+ HEAVY_POS='(^|[;&|(]|&&|\|\|)[[:space:]]*([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*'
56
+ _is_heavy=0
57
+ printf '%s' "$CMD" | grep -qE "${HEAVY_POS}((bunx|npx|bun|npm)[[:space:]]+)?(tsc[[:space:]]+--noEmit|vitest([[:space:]]+run)?|astro[[:space:]]+check|playwright[[:space:]]+test)" && _is_heavy=1
58
+ printf '%s' "$CMD" | grep -qE "${HEAVY_POS}(bun|npm)[[:space:]]+run[[:space:]]+(verify|test|typecheck|check)(:[a-z]+)?([[:space:]]|$)" && _is_heavy=1
59
+ [ "$_is_heavy" = "1" ] || exit 0
60
+ # `bun run test:raw` etc. are the deliberate escape hatches โ€” still guarded, since
61
+ # their whole point is bypassing the cap.
62
+
63
+ CORES=$(sysctl -n hw.ncpu 2>/dev/null || echo 8)
64
+ LOAD1=$(uptime | sed -E 's/.*load averages?: //' | awk '{print int($1)}')
65
+ # FREE is the share of RAM genuinely available. It used to read memory_pressure's
66
+ # "free percentage", which counts file cache, purgeable and already-compressed
67
+ # pages as free: measured 2026-09-03, that said 74% while vm_stat said 1.3% and
68
+ # 9.4 GB sat in swap โ€” so this guard's MEMORY arm could not fire on a thrashing
69
+ # box; only its load arm ever did. gate_mem_avail_mb is the honest quantity โ€”
70
+ # two vm_stat/sysctl reads, no ps, no sleep, nothing on stdin (see the bounded
71
+ # -read scar above). Unreadable probe -> 100, i.e. this arm stays silent.
72
+ #
73
+ # The probe runs in a SUBSHELL with `set +eu`, and its failure is swallowed. A
74
+ # PreToolUse hook that dies mid-stream emits no deny decision, so the heavy gate
75
+ # proceeds โ€” a guard that crashes fails OPEN. lib/hook.sh sets no -e/-u today,
76
+ # but nothing stops it from doing so tomorrow, and this arm must not be the
77
+ # reason a block silently stops happening. Anything other than a plain integer
78
+ # leaves FREE at 100, i.e. this arm stays silent.
79
+ FREE=100
80
+ _gv_lib="$CLAUDE_PROJECT_DIR/.claude/scripts/lib/govern.sh"
81
+ if [[ -r "$_gv_lib" ]]; then
82
+ _pct=$( set +eu
83
+ # shellcheck source=../scripts/lib/govern.sh
84
+ source "$_gv_lib" 2>/dev/null || exit 0
85
+ _a=$(gate_mem_avail_mb 2>/dev/null) || exit 0
86
+ _t=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1048576 ))
87
+ [ -n "$_a" ] && [ "$_t" -gt 0 ] && echo $(( _a * 100 / _t ))
88
+ exit 0 ) 2>/dev/null
89
+ [[ "$_pct" =~ ^[0-9]+$ ]] && FREE="$_pct"
90
+ fi
91
+
92
+ # How many heavy gates are already burning cores right now?
93
+ RUNNING=$(ps -Ao command= 2>/dev/null | grep -cE '(\.bin/tsc --noEmit|\.bin/vitest run|astro check)' || echo 0)
94
+
95
+ # Thresholds are env-tunable so they can be exercised in both directions โ€”
96
+ # a guard that has only ever been seen to fire is an unproven guard.
97
+ LOAD_FACTOR="${GUARD_LOAD_FACTOR:-2}"
98
+ MIN_FREE="${GUARD_MIN_FREE_PCT:-10}"
99
+ MAX_RUNNING="${GUARD_MAX_RUNNING:-3}"
100
+
101
+ SAT=""
102
+ [[ "$LOAD1" -gt $(( CORES * LOAD_FACTOR )) ]] && SAT="load ${LOAD1} on ${CORES} cores"
103
+ [[ "$FREE" -lt "$MIN_FREE" ]] && SAT="${SAT:+$SAT; }free memory ${FREE}%"
104
+ [[ "$RUNNING" -ge "$MAX_RUNNING" ]] && SAT="${SAT:+$SAT; }${RUNNING} heavy gates already running"
105
+ [[ -z "$SAT" ]] && exit 0 # healthy box โ€” proceed
106
+
107
+ MSG="[load-guard] This machine is saturated ($SAT).
108
+
109
+ Starting another ungoverned build gate makes it slower, not faster: the box is
110
+ already paging, so every running gate takes ~10x longer and yours would too.
111
+
112
+ Run it through the governor instead โ€” it queues for a free slot rather than
113
+ piling on, and kills its whole process group if it overruns:
114
+
115
+ bash .claude/scripts/gate-run.sh verify -- <your command>
116
+
117
+ Or use the governed package script, which already does this:
118
+ bun run verify (in one.ie/web)
119
+
120
+ See what is holding the box:
121
+ bash .claude/scripts/machine-check.sh --watch
122
+
123
+ Deliberate override: GOVERN_DISABLE=1 <command>
124
+ Disable this hook: ECC_DISABLED_HOOKS=hook:load-guard"
125
+
126
+ jq -nc --arg r "$MSG" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":$r}}'
127
+ exit 0
@@ -1,48 +1,55 @@
1
1
  #!/bin/bash
2
- # POST-EDIT CHECK โ€” The deterministic sandwich around every file change
2
+ # POST-EDIT-CHECK โ€” PostToolUse(Write|Edit): two cheap reads, never a block.
3
3
  #
4
- # Runs after Write/Edit. Checks the touched file with biome.
5
- # Non-blocking (exit 0) โ€” reports issues as warnings, doesn't halt.
6
- # The W4 verify step does the blocking check.
7
- # Emits hook:post-edit:{ok,warn} signal per Rule 1 (see .claude/skills/signal.md).
4
+ # 1. .claude/workflows/*.js โ†’ wf-check.mjs. A syntax error here does not fail
5
+ # loudly โ€” it stops the workflow launching at all โ€” and `node --check`
6
+ # exits 0 on any file that starts with `export`.
7
+ # 2. *.ts/*.tsx/*.js/*.jsx โ†’ biome, ONLY when the repo carries a biome
8
+ # config. Measured 2026-09-05: biome was not installed, so every edit
9
+ # printed a false "biome: ? issues" line into the model's context and
10
+ # emitted a warn signal for a tool that never ran. An absent tool is
11
+ # silent; it is never a verdict.
12
+ #
13
+ # Emits hook:post-edit:{ok,warn} (Rule 1). Exit 0 always โ€” inform, don't gate.
8
14
 
9
15
  # shellcheck source=lib/signal.sh
10
16
  source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
11
17
 
12
18
  FILE=$(echo "$1" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
19
+ [[ -z "$FILE" ]] && exit 0
20
+ [[ "$FILE" =~ \.(ts|tsx|js|jsx)$ ]] || exit 0
21
+ BASE=$(basename "$FILE")
13
22
 
14
- # Only check .ts/.tsx/.js/.jsx files
15
- if [[ -z "$FILE" ]] || [[ ! "$FILE" =~ \.(ts|tsx|js|jsx)$ ]]; then
16
- exit 0
17
- fi
18
-
19
- # Dynamic workflow scripts fail SILENTLY โ€” a syntax error doesn't error, it stops
20
- # the workflow from launching at all. biome won't catch the two structural traps
21
- # (a second top-level export; a missing `meta` block), and `node --check` exits 0
22
- # for any file starting with `export`. wf-check parses the body as the runtime
23
- # does. Non-blocking, per this hook's contract โ€” but loud, because the default
24
- # failure mode is silence.
23
+ # 1 โ€” workflow scripts
25
24
  if [[ "$FILE" == *".claude/workflows/"*.js ]]; then
26
25
  if ! WF=$(cd "$CLAUDE_PROJECT_DIR" && node .claude/scripts/wf-check.mjs "$FILE" 2>&1); then
27
26
  echo "wf-check: $(echo "$WF" | grep FAIL || echo "$WF")" >&2
28
27
  echo "wf-check: this workflow will NOT launch until fixed" >&2
29
- emit_signal "hook:post-edit:warn" -1 "file=$(basename "$FILE") wf-check=fail"
28
+ emit_signal "hook:post-edit:warn" -1 "file=$BASE wf-check=fail"
30
29
  exit 0
31
30
  fi
32
- emit_signal "hook:post-edit:ok" 1 "file=$(basename "$FILE") wf-check=ok"
31
+ emit_signal "hook:post-edit:ok" 1 "file=$BASE wf-check=ok"
32
+ exit 0
33
33
  fi
34
34
 
35
- # Quick biome lint on the single file (non-blocking)
36
- RESULT=$(cd "$CLAUDE_PROJECT_DIR" && bun biome check "$FILE" 2>&1)
37
- EXIT=$?
35
+ # 2 โ€” biome, only if the tree actually configures it
36
+ _biome_root() {
37
+ local d; d=$(dirname "$FILE")
38
+ while [[ "$d" != "/" && "$d" != "." ]]; do
39
+ [[ -f "$d/biome.json" || -f "$d/biome.jsonc" ]] && { printf '%s' "$d"; return 0; }
40
+ d=$(dirname "$d")
41
+ done
42
+ return 1
43
+ }
44
+ ROOT=$(_biome_root) || exit 0
45
+ BIOME="$ROOT/node_modules/.bin/biome"
46
+ [[ -x "$BIOME" ]] || exit 0
38
47
 
39
- BASE=$(basename "$FILE")
40
- if [ $EXIT -ne 0 ]; then
41
- ISSUES=$(echo "$RESULT" | grep -c "โ”โ”โ”" 2>/dev/null || echo "?")
42
- echo "biome: ${ISSUES} issues in $BASE" >&2
43
- emit_signal "hook:post-edit:warn" -0.5 "file=$BASE issues=$ISSUES"
44
- else
48
+ if RESULT=$("$BIOME" check "$FILE" 2>&1); then
45
49
  emit_signal "hook:post-edit:ok" 1 "file=$BASE"
50
+ else
51
+ ISSUES=$(echo "$RESULT" | grep -c "โ”โ”โ”" 2>/dev/null || echo 0)
52
+ echo "biome: ${ISSUES} issue(s) in $BASE" >&2
53
+ emit_signal "hook:post-edit:warn" -0.5 "file=$BASE issues=$ISSUES"
46
54
  fi
47
-
48
- exit 0 # Never block โ€” just inform
55
+ exit 0
@@ -1,94 +1,48 @@
1
- #!/usr/bin/env bash
2
- # SESSION-START โ€” surface unaddressed proposals + module-specific context.
1
+ #!/bin/bash
2
+ # SESSION-START โ€” one job: reclaim what a dead session left behind, then tell
3
+ # the world a session began.
3
4
  #
4
- # Closes the loop with stop-reflect.sh: stop emits proposals into
5
- # .claude/improvements.queue.md; this surfaces them at next session start so
6
- # they don't rot. Article calls this the self-improving harness pattern.
5
+ # `_gv_reap` only fires when someone CONTENDS for a lock, so a session that died
6
+ # holding a governor slot would shrink GOVERN_MAX_GATES until the next contender
7
+ # happened along; region claims evaporate the same way. Sweep both here. Silent
8
+ # unless something was actually reclaimed.
7
9
  #
8
- # Also prints the relevant skill list for the most recently touched module,
9
- # so dev context loads dynamically rather than every-session.
10
+ # What this hook no longer does (removed 2026-09-05, each measured): a find+stat
11
+ # over eight modules for a "hot module" hint (5.8s of the session's first
12
+ # prompt, decorative), and a count of .claude/improvements.queue.md proposals
13
+ # (a queue nothing consumed โ€” 445 unaddressed, zero git history of a drain).
10
14
  #
11
- # Output is brief โ€” every line costs context.
15
+ # Emits hook:session-start:ok and world session:start (Rule 1).
12
16
 
13
17
  # shellcheck source=lib/signal.sh
14
18
  source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
15
-
16
19
  cd "$CLAUDE_PROJECT_DIR" || exit 0
17
20
 
18
- QUEUE="$CLAUDE_PROJECT_DIR/.claude/improvements.queue.md"
19
- PROPOSAL_COUNT=0
20
-
21
- # Count unaddressed proposals (every "-" bullet under a session header)
22
- if [ -f "$QUEUE" ]; then
23
- PROPOSAL_COUNT=$(grep -c '^- \*\*' "$QUEUE" 2>/dev/null || echo 0)
21
+ SWEPT=0; CLAIMS_SWEPT=0
22
+ if [ -f "$CLAUDE_PROJECT_DIR/.claude/scripts/lib/govern.sh" ]; then
23
+ . "$CLAUDE_PROJECT_DIR/.claude/scripts/lib/govern.sh" 2>/dev/null || true
24
+ SWEPT=$(gate_sweep_stale 2>/dev/null || echo 0)
25
+ CLAIMS_SWEPT=$(claim_sweep 2>/dev/null || echo 0)
24
26
  fi
25
27
 
26
- # Detect most-recently touched module โ€” use mtime on a stable per-module marker.
27
- # Falls back silently if nothing identifiable.
28
- HOT_MODULE=""
29
- HOT_HINT=""
30
-
31
- # Parallel arrays โ€” bash 3.2 compatible (macOS default).
32
- MODULES=(
33
- "one.ie/web"
34
- "agents"
35
- "api"
36
- "packages/sdk"
37
- "packages/mcp"
38
- "packages/cli"
39
- "sync"
40
- "backup"
41
- )
42
- # `/name` = a registered directory skill, invocable via the Skill tool.
43
- # `name.md` = a flat file under .claude/skills/ โ€” canon, but NOT registered in
44
- # this tree, so it cannot be invoked by name. Read it by path. (The published
45
- # oneie-claude plugin registers these, because sync-claude-mirror.sh converts
46
- # each flat file into <name>/SKILL.md on the way out.)
47
- HINTS=(
48
- "/astro ยท /react19 ยท /shadcn ยท dev.md ยท build.md"
49
- "cloudflare.md ยท /ai-sdk ยท signal.md"
50
- "cloudflare.md ยท /typedb ยท signal.md"
51
- "typecheck.md ยท signal.md (substrate verbs)"
52
- "typecheck.md ยท MCP server"
53
- "typecheck.md ยท CLI bins"
54
- "cloudflare.md (Scheduled Worker)"
55
- "cloudflare.md (Scheduled Worker)"
56
- )
57
-
58
- NEWEST_TS=0
59
- i=0
60
- while [ $i -lt ${#MODULES[@]} ]; do
61
- module="${MODULES[$i]}"
62
- if [ -d "$module" ]; then
63
- TS=$(find "$module" -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.astro' -o -name '*.md' \) \
64
- -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.wrangler/*' \
65
- 2>/dev/null | head -200 | xargs -I{} stat -f '%m' {} 2>/dev/null | sort -rn | head -1)
66
- if [ -n "$TS" ] && [ "$TS" -gt "$NEWEST_TS" ]; then
67
- NEWEST_TS=$TS
68
- HOT_MODULE=$module
69
- HOT_HINT="${HINTS[$i]}"
70
- fi
71
- fi
72
- i=$((i + 1))
73
- done
74
-
75
- # Render โ€” keep it short. Only print if there's something to say.
76
- OUT=""
77
-
78
- if [ "$PROPOSAL_COUNT" -gt 0 ]; then
79
- OUT+="๐Ÿ“‹ ${PROPOSAL_COUNT} unaddressed proposal(s) in .claude/improvements.queue.md\n"
80
- fi
81
-
82
- if [ -n "$HOT_MODULE" ]; then
83
- OUT+="๐Ÿ”ฅ hot module: \`${HOT_MODULE}\` โ€” relevant skills: ${HOT_HINT}\n"
28
+ # An `astro dev` / `wrangler dev` that dies leaves its workerd child reparented
29
+ # to launchd, holding ~40MB until the box reboots. gate-reaper.sh has known that
30
+ # shape since 2026-09-05 but nothing ever RAN it: on 2026-09-05 nineteen of them
31
+ # had accumulated over 13 hours and the box sat at 20.5G of 21.5G swap with
32
+ # 997MB free. Reaping them returned swap to 7.8G used / 8.6G free in three
33
+ # seconds. The reaper is 60ms, silent when there is nothing to do, and gated on
34
+ # ppid==1 + a gate shape + a 15-minute age floor, so it can never touch live work.
35
+ REAPED=0
36
+ if [ -x "$CLAUDE_PROJECT_DIR/.claude/scripts/gate-reaper.sh" ]; then
37
+ REAPED=$(bash "$CLAUDE_PROJECT_DIR/.claude/scripts/gate-reaper.sh" --once 2>/dev/null | grep -c '^\[gate-reaper\] reaped' || true)
84
38
  fi
85
39
 
86
- if [ -n "$OUT" ]; then
87
- echo ""
88
- printf "%b" "$OUT"
89
- echo ""
90
- fi
40
+ OUT=""
41
+ [ "${REAPED:-0}" -gt 0 ] 2>/dev/null && OUT+="๐Ÿงน reaped ${REAPED} abandoned gate(s)/workerd orphan(s) burning the box\n"
42
+ [ "${SWEPT:-0}" -gt 0 ] 2>/dev/null && OUT+="๐Ÿงน reclaimed ${SWEPT} stale build slot(s) from a dead session\n"
43
+ [ "${CLAIMS_SWEPT:-0}" -gt 0 ] 2>/dev/null && OUT+="๐Ÿงน reclaimed ${CLAIMS_SWEPT} region claim(s) from a dead worker\n"
44
+ [ -n "$OUT" ] && { echo ""; printf "%b" "$OUT"; echo ""; }
91
45
 
92
- emit_signal "hook:session-start:ok" 1 "proposals=$PROPOSAL_COUNT hot=$HOT_MODULE"
93
- emit_world "session:start" "fyi" "session,start" "session started" "proposals=$PROPOSAL_COUNT" "hot=$HOT_MODULE"
46
+ emit_signal "hook:session-start:ok" 1 "swept=${SWEPT:-0} claims=${CLAIMS_SWEPT:-0} reaped=${REAPED:-0}"
47
+ emit_world "session:start" "fyi" "session,start" "session started" "swept=${SWEPT:-0}" "claims=${CLAIMS_SWEPT:-0}" "reaped=${REAPED:-0}"
94
48
  exit 0
@@ -1,52 +1,58 @@
1
1
  #!/bin/bash
2
- # TASK-COMPLETE VERIFY โ€” The W4 Gate
2
+ # TASK-COMPLETE VERIFY โ€” a READER, not a runner. Fires on TaskCompleted.
3
3
  #
4
- # Runs on TaskCompleted. Blocks task mark if tests regress.
5
- # This is the gate between signal and pheromone โ€” prevents corruption.
4
+ # WHAT IT USED TO DO, measured 2026-09-04: `cd "$CLAUDE_PROJECT_DIR" && bun run
5
+ # verify`. The repo root has no package.json, so bun answered "Script not found",
6
+ # EXIT was 1, and this hook โ€” wired `blocking: true` on matcher `*` โ€” printed
7
+ # "W4 GATE BLOCKED: Verification failed" for EVERY task completion in every
8
+ # session, over a suite that never ran (plus an `integer expression expected`
9
+ # from parsing that output). A false red on every task is a gate nobody reads.
6
10
  #
7
- # Exit 0 = task marked (all checks pass)
8
- # Exit 1 = task blocked (tests regressed or type errors detected)
9
- # Emits hook:w4-verify:{ok,fail} signal per Rule 1 (see .claude/skills/signal.md).
11
+ # And the version that "worked" would be worse. TaskCompleted fires per subagent
12
+ # task, in every session; twelve sessions completing tasks is twelve full suites
13
+ # โ€” the N-agents-N-full-suites shape that thrashed this box on 2026-08-18
14
+ # (load 57, swap full, 18-minute gates). text/factory-do.md ยง Deploy is the ship
15
+ # gate names this hook as "one file away from running the full suite on every
16
+ # task completion", and the fix is not a governed run โ€” it is that this hook
17
+ # does not run a suite at all. The suite has owners: W4 runs `verify:fast`
18
+ # through the governor and the memo; /close and ./deploy run the full lane.
19
+ #
20
+ # WHAT IT DOES NOW โ€” two deterministic reads, milliseconds, no gate:
21
+ # 1. the deferred-pin ledger: an outstanding debt on `main` means a plan
22
+ # landed without reaching its close gate. Named, never paid here.
23
+ # 2. the governor: a slot held by a DEAD pid is an orphaned gate. Named.
24
+ # Both are reports. Neither blocks โ€” a task is not the place to pay a plan's
25
+ # debt, and a hook that blocks on a neighbour's orphan punishes the wrong
26
+ # session. Exit 0 always, with the facts in the signal.
27
+ #
28
+ # Emits hook:w4-verify:{ok,debt,orphan} per Rule 1 (see .claude/skills/signal.md).
10
29
 
11
30
  # shellcheck source=lib/signal.sh
12
31
  source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
13
32
 
14
- cd "$CLAUDE_PROJECT_DIR" || exit 1
15
-
16
- # Run the full verify suite
17
- RESULT=$(bun run verify 2>&1)
18
- EXIT=$?
19
-
20
- # Extract test counts (used in both branches for the signal payload)
21
- PASSED=$(echo "$RESULT" | grep -o '[0-9]* pass' | head -1 | awk '{print $1}')
22
- FAILED=$(echo "$RESULT" | grep -o '[0-9]* fail' | head -1 | awk '{print $1}')
23
- ERRORS=$(echo "$RESULT" | grep -c "error TS" || echo "0")
33
+ DEBT_FILE="${VERIFY_FAST_DEBT_FILE:-${TMPDIR:-/tmp}/one-verify-fast-debt}"
34
+ GOVERN_DIR="${GOVERN_DIR:-${TMPDIR:-/tmp}/one-govern}"
24
35
 
25
- if [ $EXIT -eq 0 ]; then
26
- # All checks passed โ€” mark with deterministic receipts (Rule 3)
27
- emit_signal "hook:w4-verify:ok" 1 "passed=${PASSED:-0} failed=0 errors=0"
28
- exit 0
36
+ notes=""
37
+ if [ -s "$DEBT_FILE" ]; then
38
+ n=$(wc -l < "$DEBT_FILE" | tr -d ' ')
39
+ br=$(git -C "$CLAUDE_PROJECT_DIR" branch --show-current 2>/dev/null || echo "?")
40
+ echo "verify: $n deferred pin run(s) owed (branch $br) โ€” paid by a GREEN full lane at /close or ./deploy, never here" >&2
41
+ emit_signal "hook:w4-verify:debt" 0 "deferred_pins=$n branch=$br"
42
+ notes="debt=$n"
29
43
  fi
30
44
 
31
- # Verify failed โ€” extract details
32
- echo "W4 GATE BLOCKED: Verification failed" >&2
33
- echo "" >&2
34
-
35
- if [ -n "$FAILED" ] && [ "$FAILED" -gt 0 ]; then
36
- echo "Tests: $FAILED failed" >&2
37
- fi
38
- if [ "$ERRORS" -gt 0 ]; then
39
- echo "TypeScript: $ERRORS type errors" >&2
45
+ orphans=0
46
+ for d in "$GOVERN_DIR"/lock-slot-*; do
47
+ [ -d "$d" ] || continue
48
+ pid=$(cat "$d/pid" 2>/dev/null)
49
+ [ -n "$pid" ] && ! kill -0 "$pid" 2>/dev/null && orphans=$((orphans + 1))
50
+ done
51
+ if [ "$orphans" -gt 0 ]; then
52
+ echo "verify: $orphans governor slot(s) held by a dead pid โ€” reaped on the next contender; \`bash .claude/scripts/gate-reaper.sh --dry-run\` to see them" >&2
53
+ emit_signal "hook:w4-verify:orphan" 0 "stale_slots=$orphans"
54
+ notes="${notes:+$notes }orphans=$orphans"
40
55
  fi
41
56
 
42
- # Show last 15 lines of output for context
43
- echo "" >&2
44
- echo "Last output:" >&2
45
- echo "$RESULT" | tail -15 >&2
46
- echo "" >&2
47
- echo "Fix baseline before marking task done." >&2
48
-
49
- # Full warn โ€” tests actually regressed, agent produced nothing usable
50
- emit_signal "hook:w4-verify:fail" -1 "passed=${PASSED:-0} failed=${FAILED:-0} errors=${ERRORS:-0}"
51
-
52
- exit 1
57
+ [ -z "$notes" ] && emit_signal "hook:w4-verify:ok" 1 "reader: no debt, no orphaned slot"
58
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneie/claude",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "ONE Claude Code plugin โ€” /do lifecycle, W0-W4 BUILD engine, 7-canon reconcile, ONE substrate via MCP, signals, and rules",
5
5
  "files": [
6
6
  ".claude-plugin",