@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.
- package/agents/abm-strategist.md +89 -0
- package/agents/ads-meta.md +97 -0
- package/agents/analyst.md +173 -0
- package/agents/architect.md +280 -0
- package/agents/brand-guardian.md +88 -0
- package/agents/brand-strategist.md +92 -0
- package/agents/campaign-content.md +90 -0
- package/agents/campaign-email.md +88 -0
- package/agents/campaign-sms.md +87 -0
- package/agents/campaign-social.md +87 -0
- package/agents/cco.md +99 -0
- package/agents/ceo.md +106 -0
- package/agents/chairman.md +120 -0
- package/agents/cmo.md +101 -0
- package/agents/community-greeter.md +89 -0
- package/agents/community-moderator.md +92 -0
- package/agents/compliance.md +92 -0
- package/agents/copywriter.md +100 -0
- package/agents/creative-strategist.md +95 -0
- package/agents/cro.md +98 -0
- package/agents/cto.md +267 -0
- package/agents/customer-interviewer.md +93 -0
- package/agents/customer-researcher.md +89 -0
- package/agents/customer-success-manager.md +90 -0
- package/agents/customer-trainer.md +90 -0
- package/agents/cxo.md +100 -0
- package/agents/demand-creator.md +89 -0
- package/agents/demo-mover.md +83 -0
- package/agents/demo-specialist.md +89 -0
- package/agents/demo-thai-family-law.md +87 -0
- package/agents/designer.md +112 -0
- package/agents/discovery-caller.md +98 -0
- package/agents/doctor.md +139 -0
- package/agents/educate-coach.md +100 -0
- package/agents/elevate-tutor.md +83 -0
- package/agents/email-lifecycle-marketer.md +91 -0
- package/agents/engage-specialist.md +99 -0
- package/agents/events-coordinator.md +90 -0
- package/agents/foundation-builder.md +94 -0
- package/agents/funnel-architect.md +89 -0
- package/agents/gift-creator.md +100 -0
- package/agents/google-ads.md +103 -0
- package/agents/guide.md +292 -0
- package/agents/helpdesk-dispatcher.md +88 -0
- package/agents/hook-specialist.md +91 -0
- package/agents/identify-optimizer.md +101 -0
- package/agents/implementer.md +305 -0
- package/agents/incident-commander.md +120 -0
- package/agents/insights-lead.md +93 -0
- package/agents/journey-runner.md +113 -0
- package/agents/linkedin-ads.md +123 -0
- package/agents/live-sales-chat.md +90 -0
- package/agents/market-researcher.md +88 -0
- package/agents/media-buyer.md +96 -0
- package/agents/movers-customer-researcher.md +95 -0
- package/agents/movers-foundation-builder.md +96 -0
- package/agents/movers-market-researcher.md +97 -0
- package/agents/movers-pricing-strategist.md +171 -0
- package/agents/nurture-architect.md +99 -0
- package/agents/offer-architect.md +92 -0
- package/agents/onboarder.md +222 -0
- package/agents/onboarding-specialist.md +92 -0
- package/agents/operations-dashboard.md +98 -0
- package/agents/perf-engineer.md +348 -0
- package/agents/playbook-writer.md +71 -0
- package/agents/plg-strategist.md +91 -0
- package/agents/positioning-architect.md +88 -0
- package/agents/press-officer.md +89 -0
- package/agents/pricing-strategist.md +94 -0
- package/agents/privacy-officer.md +92 -0
- package/agents/referral-manager.md +91 -0
- package/agents/refine-analyst.md +102 -0
- package/agents/release-manager.md +261 -0
- package/agents/renewals-upsell-rep.md +90 -0
- package/agents/review-engineer.md +327 -0
- package/agents/rewards-steward.md +82 -0
- package/agents/sales-call-coach.md +94 -0
- package/agents/sales-closer.md +98 -0
- package/agents/security-auditor.md +313 -0
- package/agents/sell-closer.md +101 -0
- package/agents/share-amplifier.md +99 -0
- package/agents/social-media-manager.md +92 -0
- package/agents/storyteller.md +250 -0
- package/agents/strategist.md +83 -0
- package/agents/strategy-aligner.md +94 -0
- package/agents/support-agent.md +90 -0
- package/agents/tagger.md +245 -0
- package/agents/tech-writer.md +216 -0
- package/agents/test-engineer.md +243 -0
- package/agents/tiktok-ads.md +105 -0
- package/agents/tracking-engineer.md +92 -0
- package/agents/upsell-strategist.md +101 -0
- package/agents/voice.md +85 -0
- package/agents/w4-tools.md +153 -0
- package/agents/w4-verify.md +20 -138
- package/agents/workflow-optimiser.md +84 -0
- package/commands/close.md +814 -160
- package/commands/create.md +2 -2
- package/commands/deploy.md +554 -175
- package/commands/do.md +119 -109
- package/commands/fast.md +67 -0
- package/commands/improve.md +2 -2
- package/commands/one.md +418 -0
- package/commands/see.md +1 -1
- package/commands/sync.md +1 -1
- package/commands/tasks.md +222 -0
- package/commands/vespio.md +110 -0
- package/commands/vespio.remote.md +227 -0
- package/hooks/hooks.json +25 -79
- package/hooks/lib/governor-escape-match.sh +88 -0
- package/hooks/lib/hook.sh +4 -13
- package/hooks/lib/signal.sh +9 -2
- package/hooks/scripts/branch-pin.sh +6 -0
- package/hooks/scripts/config-protect.sh +6 -0
- package/hooks/scripts/do-outcome-gate.sh +19 -2
- package/hooks/scripts/git-add-guard.sh +43 -2
- package/hooks/scripts/governor-escape.sh +96 -0
- package/hooks/scripts/load-guard.sh +127 -0
- package/hooks/scripts/post-edit-check.sh +36 -29
- package/hooks/scripts/session-start.sh +34 -80
- package/hooks/scripts/task-complete-verify.sh +46 -40
- package/package.json +1 -1
- package/rules/documentation.md +9 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agentverse-audit.sh +256 -0
- package/scripts/asi-walk.sh +435 -0
- package/scripts/astro-build-cached.sh +143 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/blocks-manifest.mjs +497 -0
- package/scripts/blocks-ratchet.sh +116 -0
- package/scripts/blocks-render-probe.mjs +529 -0
- package/scripts/blocks-usage.mjs +334 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/close-metrics.sh +558 -0
- package/scripts/close-owner.sh +268 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-dev.sh +35 -0
- package/scripts/deploy-emit.sh +298 -0
- package/scripts/deploy-gate-check.sh +128 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy.sh +1273 -0
- package/scripts/do-auto.sh +598 -48
- package/scripts/do-brief.sh +113 -0
- package/scripts/do-close.sh +1137 -0
- package/scripts/do-commit.sh +75 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-cycle-shape-check.sh +160 -0
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-derive-check.sh +436 -0
- package/scripts/do-fleet.sh +106 -28
- package/scripts/do-folder.sh +10 -1
- package/scripts/do-next.sh +106 -0
- package/scripts/do-orchestrate.sh +17 -5
- package/scripts/do-plan-json.mjs +201 -0
- package/scripts/do-plan-json.sh +8 -0
- package/scripts/do-preflight.sh +117 -0
- package/scripts/do-project.sh +157 -0
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +295 -23
- package/scripts/do-rank.py +31 -1
- package/scripts/do-recon-cache.sh +7 -1
- package/scripts/do-recon-pack.sh +196 -0
- package/scripts/do-reconcile.sh +121 -4
- package/scripts/do-signal.sh +280 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-test-gate.sh +80 -0
- package/scripts/do-tick.sh +102 -0
- package/scripts/do-tier.sh +6 -0
- package/scripts/do-triage.sh +182 -0
- package/scripts/do-ui-gate.sh +1 -1
- package/scripts/do-w4-gates.sh +451 -0
- package/scripts/do-walk.sh +12 -1
- package/scripts/env-sync.sh +173 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +68 -14
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +118 -3
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-ship.sh +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/factory-turn.sh +326 -0
- package/scripts/factory-walk.sh +396 -0
- package/scripts/factory-width.sh +57 -0
- package/scripts/fade-toxic.sh +4 -3
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/fixtures/triage-dupe.md +5 -0
- package/scripts/fleet-manifest.mjs +108 -0
- package/scripts/fleet-status.sh +110 -0
- package/scripts/full-suite-paths-check.sh +144 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +125 -0
- package/scripts/gate-run.sh +73 -0
- package/scripts/gc-content-check.sh +140 -0
- package/scripts/gen-dev-config.py +20 -0
- package/scripts/govern-bound-check.sh +60 -0
- package/scripts/govern-claims-check.sh +233 -0
- package/scripts/govern-mem-check.sh +290 -0
- package/scripts/governor-doors-check.sh +362 -0
- package/scripts/governor-escape-check.sh +171 -0
- package/scripts/health.sh +413 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/land.sh +551 -0
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/lib/govern.sh +361 -0
- package/scripts/lib/govern.ts +756 -0
- package/scripts/lighthouse-run.sh +187 -0
- package/scripts/livekit-live-check.sh +61 -0
- package/scripts/livekit-ratchet.sh +46 -0
- package/scripts/load-guard-check.sh +49 -0
- package/scripts/machine-check.sh +102 -0
- package/scripts/machine-watch.sh +177 -0
- package/scripts/one-agents.mjs +415 -0
- package/scripts/one-resume.sh +78 -0
- package/scripts/orphan-baseline.json +182 -0
- package/scripts/orphan-modules.mjs +179 -0
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/promise-manifest.mjs +24 -1
- package/scripts/release.sh +322 -0
- package/scripts/roles-check.sh +946 -0
- package/scripts/sdk-build-cached.sh +64 -0
- package/scripts/signal-watch.sh +241 -0
- package/scripts/skills-publish.sh +94 -0
- package/scripts/speed-cache-check.sh +173 -0
- package/scripts/speed-check.mjs +907 -0
- package/scripts/speed-parity-check.sh +648 -0
- package/scripts/speed-waterfall-check.sh +355 -0
- package/scripts/substrate-env-parity.mjs +156 -0
- package/scripts/tasks-claim-race.mjs +108 -0
- package/scripts/tasks-loop.sh +185 -0
- package/scripts/test-cached.sh +255 -0
- package/scripts/test-full.sh +87 -0
- package/scripts/test-honesty.mjs +137 -0
- package/scripts/test-lanes.sh +166 -0
- package/scripts/test-speed.sh +94 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +179 -0
- package/scripts/typedb-flake-check.sh +197 -0
- package/scripts/urls-lint.sh +15 -0
- package/scripts/verify-fast.sh +445 -0
- package/scripts/vespio-sync.sh +149 -0
- package/scripts/wf-check.mjs +104 -1
- package/scripts/worktree-preview.sh +879 -0
- package/scripts/worktree-up.sh +208 -0
- package/skills/livekit-agents/SKILL.md +285 -0
- package/skills/livekit-agents/references/freshness-rules.md +168 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/signal/SKILL.md +0 -1
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/hooks/scripts/compact-hint.sh +0 -35
- package/hooks/scripts/gate-guard.sh +0 -83
- package/hooks/scripts/read-tracker.sh +0 -26
- package/hooks/scripts/session-end-verify.sh +0 -51
- package/hooks/scripts/stop-reflect.sh +0 -140
- package/hooks/scripts/sync-priority-todo.sh +0 -57
- package/hooks/scripts/sync-todo-docs.sh +0 -46
- package/hooks/scripts/tool-signal.sh +0 -48
- 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
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
|
2
|
+
# POST-EDIT-CHECK โ PostToolUse(Write|Edit): two cheap reads, never a block.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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
|
-
#
|
|
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=$
|
|
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=$
|
|
31
|
+
emit_signal "hook:post-edit:ok" 1 "file=$BASE wf-check=ok"
|
|
32
|
+
exit 0
|
|
33
33
|
fi
|
|
34
34
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
#!/
|
|
2
|
-
# SESSION-START โ
|
|
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
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
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
|
-
#
|
|
9
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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 "
|
|
93
|
-
emit_world "session:start" "fyi" "session,start" "session started" "
|
|
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 โ
|
|
2
|
+
# TASK-COMPLETE VERIFY โ a READER, not a runner. Fires on TaskCompleted.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
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
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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