ruvnet-brain 4.3.9 → 4.3.10
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/README.md +26 -9
- package/bin/install.mjs +646 -366
- package/bin/nightly-refresh.mjs +115 -0
- package/kb/brain-profile.mjs +118 -32
- package/kb/lifecycle-evidence-retention.mjs +237 -0
- package/kb/refresh-run.mjs +367 -0
- package/kb/retrieval-result.mjs +39 -0
- package/kb/update-storage-transaction.mjs +439 -0
- package/package.json +11 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/hooks/codex-hooks.json +2 -165
- package/plugin/hooks/hook-contracts.json +4 -65
- package/plugin/hooks/hooks.json +2 -208
- package/plugin/scripts/adr-currency-gate.mjs +29 -19
- package/plugin/scripts/capability-registry.mjs +10 -83
- package/plugin/scripts/codex-hook-adapter.mjs +62 -9
- package/plugin/scripts/codex-hook-wrapper.mjs +19 -1
- package/plugin/scripts/continuation-gate.mjs +30 -28
- package/plugin/scripts/continuation-objective.mjs +34 -0
- package/plugin/scripts/development-maintenance.mjs +49 -0
- package/plugin/scripts/hook-shim.mjs +3 -0
- package/plugin/scripts/learn-flush.mjs +6 -1
- package/plugin/scripts/lesson-gate.mjs +5 -2
- package/plugin/scripts/lesson-presentation.mjs +5 -0
- package/plugin/scripts/md-stamp.mjs +109 -6
- package/plugin/scripts/memory-doctor.mjs +3 -7
- package/plugin/scripts/nightly-controller.mjs +9 -27
- package/plugin/scripts/nightly-scheduler.mjs +472 -0
- package/plugin/scripts/project-progression-contract.mjs +4 -4
- package/plugin/scripts/project-progression-store.mjs +35 -6
- package/plugin/scripts/ruflo-bin.mjs +20 -0
- package/plugin/scripts/session-snapshot-contract.mjs +6 -1
- package/plugin/scripts/version-bump-gate.sh +14 -2
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +15 -23
- package/scripts/build-bundle.mjs +8 -0
- package/scripts/build-primer.mjs +2 -3
- package/scripts/candidate-host-evidence.mjs +59 -41
- package/scripts/ci/build-fixture-kb.mjs +59 -2
- package/scripts/ci/mutate-hook-timeout.mjs +24 -47
- package/scripts/ci/stranger-scenario.mjs +13 -28
- package/scripts/claims-verify.mjs +219 -21
- package/scripts/console-engine.mjs +4 -18
- package/scripts/console-runtime-identity.mjs +6 -0
- package/scripts/development-maintenance.mjs +47 -0
- package/scripts/development-push-check.mjs +35 -0
- package/scripts/distill-project.mjs +24 -47
- package/scripts/doc-currency.mjs +91 -19
- package/scripts/git-hooks/pre-push +4 -162
- package/scripts/health-repair.mjs +22 -4
- package/scripts/hook-retirement-check.mjs +22 -0
- package/scripts/host-install-matrix.mjs +125 -61
- package/scripts/integration-evidence.mjs +11 -6
- package/scripts/learning-replay-fixture.mjs +23 -2
- package/scripts/nightly-two-run-proof.mjs +415 -0
- package/scripts/nightly-watchdog.mjs +17 -2
- package/scripts/npm-invocation.mjs +20 -0
- package/scripts/prepublication-evidence.mjs +42 -6
- package/scripts/primer-grounding.mjs +20 -0
- package/scripts/product-integrity-contract.mjs +22 -2
- package/scripts/public-verification-abandon.mjs +101 -0
- package/scripts/public-verification-aggregate.mjs +53 -19
- package/scripts/public-verification-finalizer.mjs +8 -2
- package/scripts/public-verification-lane.mjs +63 -8
- package/scripts/publication-receipt.mjs +109 -45
- package/scripts/published-surface-probe.mjs +10 -2
- package/scripts/qa-contract.mjs +57 -0
- package/scripts/qa-lanes.mjs +40 -0
- package/scripts/qa-runner.mjs +54 -64
- package/scripts/qe/ux-suite.mjs +10 -34
- package/scripts/qualified-candidate-check.mjs +124 -0
- package/scripts/release-abort-stale.mjs +2 -2
- package/scripts/release-projection.mjs +20 -1
- package/scripts/release-qualification-contract.mjs +87 -0
- package/scripts/release-qualification.mjs +135 -0
- package/scripts/release-transaction.mjs +81 -1
- package/scripts/remedy-registry.mjs +6 -13
- package/scripts/retrieval-canary.mjs +91 -18
- package/scripts/selfcheck.mjs +12 -4
- package/scripts/snapshot-freshness.mjs +58 -0
- package/scripts/stack-sync.mjs +41 -43
- package/scripts/staged-host-verifier.mjs +63 -14
- package/scripts/wired-check.mjs +31 -2
|
@@ -1,167 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "RuvNet Brain
|
|
3
|
-
"hooks": {
|
|
4
|
-
"SessionStart": [
|
|
5
|
-
{
|
|
6
|
-
"matcher": "startup|resume|clear|compact|fork",
|
|
7
|
-
"hooks": [
|
|
8
|
-
{
|
|
9
|
-
"type": "command",
|
|
10
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 session-start",
|
|
11
|
-
"timeout": 5
|
|
12
|
-
}
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
],
|
|
16
|
-
"PreCompact": [
|
|
17
|
-
{
|
|
18
|
-
"matcher": ".*",
|
|
19
|
-
"hooks": [
|
|
20
|
-
{
|
|
21
|
-
"type": "command",
|
|
22
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 session-snapshot PreCompact",
|
|
23
|
-
"timeout": 5
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"UserPromptSubmit": [
|
|
29
|
-
{
|
|
30
|
-
"hooks": [
|
|
31
|
-
{
|
|
32
|
-
"type": "command",
|
|
33
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 9000 ground-ruvnet",
|
|
34
|
-
"timeout": 10
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"type": "command",
|
|
38
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 9000 unprompted-speech UserPromptSubmit",
|
|
39
|
-
"timeout": 10
|
|
40
|
-
}
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"PreToolUse": [
|
|
45
|
-
{
|
|
46
|
-
"matcher": "^(Edit|Write|MultiEdit|apply_patch)$",
|
|
47
|
-
"hooks": [
|
|
48
|
-
{
|
|
49
|
-
"type": "command",
|
|
50
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 6500 decision-gate write",
|
|
51
|
-
"timeout": 8
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"matcher": "^(Bash|exec_command|functions\\.exec_command|functions__exec_command)$",
|
|
57
|
-
"hooks": [
|
|
58
|
-
{
|
|
59
|
-
"type": "command",
|
|
60
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 6500 decision-gate bash",
|
|
61
|
-
"timeout": 8
|
|
62
|
-
}
|
|
63
|
-
]
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"matcher": "^(Agent|spawn_agent)$",
|
|
67
|
-
"hooks": [
|
|
68
|
-
{
|
|
69
|
-
"type": "command",
|
|
70
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 route-dispatch",
|
|
71
|
-
"timeout": 5
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"matcher": "^(Bash|exec_command|functions\\.exec_command|functions__exec_command)$",
|
|
77
|
-
"hooks": [
|
|
78
|
-
{
|
|
79
|
-
"type": "command",
|
|
80
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 verify-interface",
|
|
81
|
-
"timeout": 5
|
|
82
|
-
}
|
|
83
|
-
]
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
|
-
"PostToolUse": [
|
|
87
|
-
{
|
|
88
|
-
"matcher": "^(?:.*__)?search_ruvnet$",
|
|
89
|
-
"hooks": [
|
|
90
|
-
{
|
|
91
|
-
"type": "command",
|
|
92
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 grounding-stamp",
|
|
93
|
-
"timeout": 5
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"matcher": "^(Bash|Edit|Write|MultiEdit|exec_command|functions\\.exec_command|functions__exec_command|apply_patch)$",
|
|
99
|
-
"hooks": [
|
|
100
|
-
{
|
|
101
|
-
"type": "command",
|
|
102
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 learn-capture",
|
|
103
|
-
"timeout": 5
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"matcher": "^(Edit|Write|MultiEdit|apply_patch)$",
|
|
109
|
-
"hooks": [
|
|
110
|
-
{
|
|
111
|
-
"type": "command",
|
|
112
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 md-stamp",
|
|
113
|
-
"timeout": 5
|
|
114
|
-
}
|
|
115
|
-
]
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"matcher": "^(Bash|exec_command|functions\\.exec_command|functions__exec_command)$",
|
|
119
|
-
"hooks": [
|
|
120
|
-
{
|
|
121
|
-
"type": "command",
|
|
122
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 signal-watch",
|
|
123
|
-
"timeout": 5
|
|
124
|
-
}
|
|
125
|
-
]
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
"matcher": "^(Agent|spawn_agent)$",
|
|
129
|
-
"hooks": [
|
|
130
|
-
{
|
|
131
|
-
"type": "command",
|
|
132
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 4500 routing-outcome",
|
|
133
|
-
"timeout": 5
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
}
|
|
137
|
-
],
|
|
138
|
-
"SessionEnd": [
|
|
139
|
-
{
|
|
140
|
-
"matcher": ".*",
|
|
141
|
-
"hooks": [
|
|
142
|
-
{
|
|
143
|
-
"type": "command",
|
|
144
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 2800 session-snapshot SessionEnd",
|
|
145
|
-
"timeout": 3
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"type": "command",
|
|
149
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 2800 learn-flush",
|
|
150
|
-
"timeout": 3
|
|
151
|
-
}
|
|
152
|
-
]
|
|
153
|
-
}
|
|
154
|
-
],
|
|
155
|
-
"Stop": [
|
|
156
|
-
{
|
|
157
|
-
"hooks": [
|
|
158
|
-
{
|
|
159
|
-
"type": "command",
|
|
160
|
-
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 9000 continuation-gate",
|
|
161
|
-
"timeout": 10
|
|
162
|
-
}
|
|
163
|
-
]
|
|
164
|
-
}
|
|
165
|
-
]
|
|
166
|
-
}
|
|
2
|
+
"description": "RuvNet Brain automatic host hooks are intentionally retired. This schema-valid empty registry is shipped so install and update converge old hook-bearing generations to zero implicit lifecycle handlers.",
|
|
3
|
+
"hooks": {}
|
|
167
4
|
}
|
|
@@ -1,67 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_note": "
|
|
3
|
-
"_version":
|
|
4
|
-
"contracts": [
|
|
5
|
-
|
|
6
|
-
"id": "project:version-bump-gate",
|
|
7
|
-
"layer": "project",
|
|
8
|
-
"event": "PreToolUse",
|
|
9
|
-
"matcher": "Bash",
|
|
10
|
-
"commandIncludes": "version-bump-gate.sh",
|
|
11
|
-
"mode": "blocking",
|
|
12
|
-
"offBehavior": "run",
|
|
13
|
-
"offReason": "ADR-054's discriminator, applied literally: this is an HONESTY wall, not a retrieval feature. It refuses a commit that changes behaviour without bumping the version \u2014 the signal every user's updater reads. Nothing about that becomes acceptable because the owner switched the retrieval brain off, and the gate reads git state only: no corpus, no network, no brain.",
|
|
14
|
-
"failureBehavior": "fail-open \u2014 a crash or unparseable payload permits the command and records degraded health; malfunction is never a decision (ADR-055 \u00a71.2)",
|
|
15
|
-
"timeoutSeconds": 5,
|
|
16
|
-
"warmP95BudgetMs": 50,
|
|
17
|
-
"coldBudgetMs": 500,
|
|
18
|
-
"stdoutCapBytes": 4096,
|
|
19
|
-
"stateRead": [
|
|
20
|
-
"git HEAD",
|
|
21
|
-
"plugin/.claude-plugin/plugin.json"
|
|
22
|
-
],
|
|
23
|
-
"stateWritten": [],
|
|
24
|
-
"dependencies": [
|
|
25
|
-
"bash",
|
|
26
|
-
"git"
|
|
27
|
-
],
|
|
28
|
-
"reachesStrangers": false,
|
|
29
|
-
"owner": "ruvnet-brain (this repo only \u2014 ADR-053; it enforces a rule that is true here and nowhere else)",
|
|
30
|
-
"note": "Project-scoped on purpose: it lived in the GLOBAL settings until 2026-07-14, taxing every Bash call in 34+ projects with a rule true in one."
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
"matcherAllowlist": [
|
|
34
|
-
{
|
|
35
|
-
"layer": "plugin",
|
|
36
|
-
"event": "PreToolUse",
|
|
37
|
-
"matcher": "Bash",
|
|
38
|
-
"handler": "verify-interface.sh",
|
|
39
|
-
"reason": "UNANCHORED, known (ADR-055 F4/F12): also selects BashOutput. One of three blocking walls on the same event that each re-parse the same payload \u2014 the parse-once execution-guard is the real fix, not six anchor characters.",
|
|
40
|
-
"retiredBy": "ADR-055 build item 3 (execution-guard.mjs \u2014 ONE registration, ONE parse)"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"layer": "plugin",
|
|
44
|
-
"event": "PostToolUse",
|
|
45
|
-
"matcher": "Write|Edit|MultiEdit|Bash",
|
|
46
|
-
"handler": "learn-capture.sh",
|
|
47
|
-
"reason": "UNANCHORED, known (ADR-055 F4): NotebookEdit reaches the learning pipeline by substring accident, never by decision. Advisory.",
|
|
48
|
-
"retiredBy": "ADR-055 build item 3"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"layer": "plugin",
|
|
52
|
-
"event": "PostToolUse",
|
|
53
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
54
|
-
"handler": "md-stamp.mjs",
|
|
55
|
-
"reason": "UNANCHORED, known (ADR-055 F4): same NotebookEdit substring accident. Advisory.",
|
|
56
|
-
"retiredBy": "ADR-055 build item 3"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"layer": "project",
|
|
60
|
-
"event": "PreToolUse",
|
|
61
|
-
"matcher": "Bash",
|
|
62
|
-
"handler": "version-bump-gate.sh",
|
|
63
|
-
"reason": "UNANCHORED, known: also selects BashOutput, which carries no command to gate, so the gate reads an empty payload and permits. Wasted spawn, not a wrong refusal.",
|
|
64
|
-
"retiredBy": "ADR-055 build item 3, together with the plugin-layer Bash walls it sits beside"
|
|
65
|
-
}
|
|
66
|
-
]
|
|
2
|
+
"_note": "No Brain-owned automatic hook contract remains. Dormant hook bodies are retained as historical implementation material and may only be exposed through explicit, separately tested commands.",
|
|
3
|
+
"_version": 2,
|
|
4
|
+
"contracts": [],
|
|
5
|
+
"matcherAllowlist": []
|
|
67
6
|
}
|
package/plugin/hooks/hooks.json
CHANGED
|
@@ -1,210 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "
|
|
3
|
-
"hooks": {
|
|
4
|
-
"SessionStart": [
|
|
5
|
-
{
|
|
6
|
-
"matcher": "startup",
|
|
7
|
-
"hooks": [
|
|
8
|
-
{
|
|
9
|
-
"type": "command",
|
|
10
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" session-start || true",
|
|
11
|
-
"timeout": 5
|
|
12
|
-
}
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"matcher": "resume",
|
|
17
|
-
"hooks": [
|
|
18
|
-
{
|
|
19
|
-
"type": "command",
|
|
20
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" session-start || true",
|
|
21
|
-
"timeout": 5
|
|
22
|
-
}
|
|
23
|
-
]
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"matcher": "clear|compact|fork",
|
|
27
|
-
"hooks": [
|
|
28
|
-
{
|
|
29
|
-
"type": "command",
|
|
30
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" session-start || true",
|
|
31
|
-
"timeout": 5
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
"PreCompact": [
|
|
37
|
-
{
|
|
38
|
-
"matcher": ".*",
|
|
39
|
-
"hooks": [
|
|
40
|
-
{
|
|
41
|
-
"type": "command",
|
|
42
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" session-snapshot PreCompact || true",
|
|
43
|
-
"timeout": 5
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
"UserPromptSubmit": [
|
|
49
|
-
{
|
|
50
|
-
"matcher": ".*",
|
|
51
|
-
"hooks": [
|
|
52
|
-
{
|
|
53
|
-
"type": "command",
|
|
54
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" ground-ruvnet || true",
|
|
55
|
-
"timeout": 10
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"matcher": "*",
|
|
61
|
-
"hooks": [
|
|
62
|
-
{
|
|
63
|
-
"type": "command",
|
|
64
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" unprompted-speech UserPromptSubmit",
|
|
65
|
-
"timeout": 10
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
],
|
|
70
|
-
"PreToolUse": [
|
|
71
|
-
{
|
|
72
|
-
"matcher": "^(Write|Edit|MultiEdit|NotebookEdit)$",
|
|
73
|
-
"hooks": [
|
|
74
|
-
{
|
|
75
|
-
"type": "command",
|
|
76
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" decision-gate write",
|
|
77
|
-
"timeout": 5
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"matcher": "^Bash$",
|
|
83
|
-
"hooks": [
|
|
84
|
-
{
|
|
85
|
-
"type": "command",
|
|
86
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" decision-gate bash",
|
|
87
|
-
"timeout": 5
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"matcher": "^(Task|Agent)$",
|
|
93
|
-
"hooks": [
|
|
94
|
-
{
|
|
95
|
-
"type": "command",
|
|
96
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" route-dispatch || true",
|
|
97
|
-
"timeout": 5
|
|
98
|
-
}
|
|
99
|
-
]
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"matcher": "Bash",
|
|
103
|
-
"hooks": [
|
|
104
|
-
{
|
|
105
|
-
"type": "command",
|
|
106
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" verify-interface || true",
|
|
107
|
-
"timeout": 5
|
|
108
|
-
}
|
|
109
|
-
]
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
"PostToolUse": [
|
|
113
|
-
{
|
|
114
|
-
"matcher": "^(Task|Agent)$",
|
|
115
|
-
"hooks": [
|
|
116
|
-
{
|
|
117
|
-
"type": "command",
|
|
118
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" routing-outcome || true",
|
|
119
|
-
"timeout": 5
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
"matcher": "^(?:.*__)?search_ruvnet$",
|
|
125
|
-
"hooks": [
|
|
126
|
-
{
|
|
127
|
-
"type": "command",
|
|
128
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" grounding-stamp || true",
|
|
129
|
-
"timeout": 5
|
|
130
|
-
}
|
|
131
|
-
]
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
"matcher": "Write|Edit|MultiEdit|Bash",
|
|
135
|
-
"hooks": [
|
|
136
|
-
{
|
|
137
|
-
"type": "command",
|
|
138
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" learn-capture || true",
|
|
139
|
-
"timeout": 5
|
|
140
|
-
}
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
"matcher": "Write|Edit|MultiEdit",
|
|
145
|
-
"hooks": [
|
|
146
|
-
{
|
|
147
|
-
"type": "command",
|
|
148
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" md-stamp || true",
|
|
149
|
-
"timeout": 5
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
"matcher": "^Bash$",
|
|
155
|
-
"hooks": [
|
|
156
|
-
{
|
|
157
|
-
"type": "command",
|
|
158
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" signal-watch || true",
|
|
159
|
-
"timeout": 5
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
}
|
|
163
|
-
],
|
|
164
|
-
"SessionEnd": [
|
|
165
|
-
{
|
|
166
|
-
"matcher": ".*",
|
|
167
|
-
"hooks": [
|
|
168
|
-
{
|
|
169
|
-
"type": "command",
|
|
170
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" session-snapshot SessionEnd || true",
|
|
171
|
-
"timeout": 5
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
"matcher": ".*",
|
|
177
|
-
"hooks": [
|
|
178
|
-
{
|
|
179
|
-
"type": "command",
|
|
180
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" learn-flush || true",
|
|
181
|
-
"timeout": 30
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
],
|
|
186
|
-
"TeammateIdle": [
|
|
187
|
-
{
|
|
188
|
-
"hooks": [
|
|
189
|
-
{
|
|
190
|
-
"type": "command",
|
|
191
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" swarm-slot-recycler",
|
|
192
|
-
"timeout": 5
|
|
193
|
-
}
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
],
|
|
197
|
-
"Stop": [
|
|
198
|
-
{
|
|
199
|
-
"matcher": "*",
|
|
200
|
-
"hooks": [
|
|
201
|
-
{
|
|
202
|
-
"type": "command",
|
|
203
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hook-shim.mjs\" continuation-gate || true",
|
|
204
|
-
"timeout": 10
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
}
|
|
208
|
-
]
|
|
209
|
-
}
|
|
2
|
+
"description": "RuvNet Brain automatic host hooks are intentionally retired. Knowledge access remains available through MCP, skills, explicit CLI commands, CI, and release proof; no lifecycle event runs Brain code implicitly.",
|
|
3
|
+
"hooks": {}
|
|
210
4
|
}
|
|
@@ -36,7 +36,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
36
36
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
37
37
|
const REPO = path.resolve(HERE, '..', '..');
|
|
38
38
|
|
|
39
|
-
/**
|
|
39
|
+
/** Finding codes within this edit gate's narrow stale-debt policy. */
|
|
40
40
|
const STALE = new Set(['presumed-stale']);
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -66,7 +66,7 @@ async function loadDocCurrency(root) {
|
|
|
66
66
|
export async function staleGovernorsOf(relPath, { root = REPO, docCurrency = null, readFile = null } = {}) {
|
|
67
67
|
const mod = docCurrency ?? await loadDocCurrency(root);
|
|
68
68
|
if (!mod) return [];
|
|
69
|
-
const { listDocs, evaluateDoc, parseFrontmatter, DEFAULT_DIRS, isGitRepo } = mod;
|
|
69
|
+
const { listDocs, evaluateDoc, parseFrontmatter, resolveGoverned, blockingFindings, DEFAULT_DIRS, isGitRepo } = mod;
|
|
70
70
|
// `readFile` is injectable for one reason, and it is not tidiness: with `fs.readFileSync` hardcoded
|
|
71
71
|
// here, a test that injects `listDocs`/`parseFrontmatter` never reaches them — the read throws on a
|
|
72
72
|
// fixture path that does not exist, the loop `continue`s, and NO CANDIDATE IS EVER FOUND. The first
|
|
@@ -74,38 +74,48 @@ export async function staleGovernorsOf(relPath, { root = REPO, docCurrency = nul
|
|
|
74
74
|
// passed. A suite of only allow-cases would have shipped this green and unfireable, which is the
|
|
75
75
|
// exact defect class this repo has now hit four times in one day.
|
|
76
76
|
const read = readFile ?? ((p) => fs.readFileSync(p, 'utf8'));
|
|
77
|
-
|
|
77
|
+
let docs;
|
|
78
|
+
try {
|
|
79
|
+
if (!isGitRepo(root)) return [];
|
|
80
|
+
docs = listDocs(root, DEFAULT_DIRS);
|
|
81
|
+
} catch { return []; }
|
|
78
82
|
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
// naive loop is ~19 SECONDS on the Write path, which would make this the gate everybody disables.
|
|
82
|
-
// Reading frontmatter for all 83 costs 40ms total. So: cheap pass to find WHICH documents govern
|
|
83
|
-
// this file (usually one), expensive pass on only those. ~250ms typical, and exactly 40ms for the
|
|
84
|
-
// overwhelmingly common case of a file no ADR governs.
|
|
83
|
+
// Resolve declarations together, then evaluate only the documents governing this exact file.
|
|
84
|
+
// The shared resolver owns scalar/list/glob semantics; directories are not recursive governors.
|
|
85
85
|
const candidates = [];
|
|
86
|
-
|
|
86
|
+
const entries = new Set();
|
|
87
|
+
for (const docRel of docs) {
|
|
87
88
|
let fm;
|
|
88
89
|
try { fm = parseFrontmatter(read(path.join(root, docRel))); } catch { continue; }
|
|
89
90
|
const governs = fm?.keys?.governs;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
const declared = (Array.isArray(governs) ? governs : (governs ? [String(governs)] : []))
|
|
92
|
+
.map((entry) => String(entry).trim()).filter(Boolean);
|
|
93
|
+
if (!declared.length) continue;
|
|
94
|
+
declared.forEach((entry) => entries.add(entry));
|
|
95
|
+
candidates.push({ docRel, id: fm?.keys?.id ?? path.basename(docRel), declared });
|
|
95
96
|
}
|
|
96
97
|
if (!candidates.length) return [];
|
|
98
|
+
let matching;
|
|
99
|
+
try {
|
|
100
|
+
const target = relPath.split(path.sep).join('/');
|
|
101
|
+
matching = new Set(resolveGoverned(root, [...entries])
|
|
102
|
+
.filter((g) => g.resolved && g.path === target).map((g) => g.from));
|
|
103
|
+
} catch { return []; }
|
|
97
104
|
|
|
98
105
|
const out = [];
|
|
99
106
|
for (const c of candidates) {
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
|
|
107
|
+
if (!c.declared.some((entry) => matching.has(entry))) continue;
|
|
108
|
+
try {
|
|
109
|
+
const doc = evaluateDoc(root, c.docRel);
|
|
110
|
+
const finding = blockingFindings([doc]).find((f) => STALE.has(f.code));
|
|
111
|
+
if (finding) out.push({ doc: c.docRel, id: c.id, why: finding.message });
|
|
112
|
+
} catch { continue; }
|
|
103
113
|
}
|
|
104
114
|
return out;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
export function refusalText(relPath, stale) {
|
|
108
|
-
const names = stale.map((s) => `${s.id} (${s.doc})`).join('\n ');
|
|
118
|
+
const names = stale.map((s) => `${s.id} (${s.doc})${s.why ? ` — ${s.why}` : ''}`).join('\n ');
|
|
109
119
|
return `⛔ BLOCKED — ${relPath} is governed by a document that is ALREADY stale.
|
|
110
120
|
|
|
111
121
|
stale: ${names}
|