ruvnet-brain 3.9.134-dev → 4.0.2

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 (218) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/README.md +5 -5
  3. package/bin/install.mjs +382 -36
  4. package/console/CONTRACT.md +172 -0
  5. package/console/activity.js +753 -0
  6. package/console/app.js +4189 -0
  7. package/console/architecture.html +1221 -0
  8. package/console/assets/depth-1.webp +0 -0
  9. package/console/assets/depth-2.webp +0 -0
  10. package/console/assets/depth-3.webp +0 -0
  11. package/console/assets/harness-vs-plain.svg +259 -0
  12. package/console/assets/hero.webp +0 -0
  13. package/console/assets/memory.webp +0 -0
  14. package/console/assets/metaharness.svg +247 -0
  15. package/console/index.html +777 -0
  16. package/console/install-architecture.html +162 -0
  17. package/console/install-mockup.html +543 -0
  18. package/console/style.css +2144 -0
  19. package/console/tips.css +926 -0
  20. package/console/tips.html +858 -0
  21. package/console/tips.js +128 -0
  22. package/docs/RELEASE-NOTES-4.0.md +88 -0
  23. package/kb/model-requirements.mjs +37 -6
  24. package/kb/zip-extract.mjs +53 -14
  25. package/keys/ruvnet-brain-signing.pub.pem +3 -0
  26. package/package.json +14 -22
  27. package/plugin/.claude-plugin/marketplace.json +14 -0
  28. package/plugin/.claude-plugin/plugin.json +22 -0
  29. package/plugin/.codex-plugin/plugin.json +21 -0
  30. package/plugin/.mcp.json +8 -0
  31. package/plugin/commands/brain-console.md +16 -0
  32. package/plugin/commands/configure.md +33 -0
  33. package/plugin/commands/rvbc.md +79 -0
  34. package/plugin/commands/rvcb.md +16 -0
  35. package/plugin/commands/whats-new.md +57 -0
  36. package/plugin/hooks/codex-hooks.json +160 -0
  37. package/plugin/hooks/hook-contracts.json +77 -0
  38. package/plugin/hooks/hooks.json +202 -0
  39. package/plugin/mcp/managed-cli-interface.mjs +47 -4
  40. package/plugin/mcp/server.mjs +56 -6
  41. package/plugin/scripts/anticipate.sh +534 -0
  42. package/plugin/scripts/codex-hook-adapter.mjs +96 -0
  43. package/plugin/scripts/continuation-gate.mjs +267 -0
  44. package/plugin/scripts/design-wall.sh +137 -0
  45. package/plugin/scripts/detach.mjs +182 -0
  46. package/plugin/scripts/first-session-worker.mjs +38 -0
  47. package/plugin/scripts/gate-receipt.sh +35 -0
  48. package/plugin/scripts/ground-before-write.sh +199 -0
  49. package/plugin/scripts/ground-ruvnet.sh +517 -0
  50. package/plugin/scripts/grounding-stamp.sh +113 -0
  51. package/plugin/scripts/grounding-substance.mjs +595 -0
  52. package/plugin/scripts/hijack-ruvnet.sh +81 -0
  53. package/plugin/scripts/hook-input.mjs +558 -0
  54. package/plugin/scripts/hook-shim-bash.mjs +55 -0
  55. package/plugin/scripts/hook-shim.mjs +303 -0
  56. package/plugin/scripts/host-update.mjs +58 -0
  57. package/plugin/scripts/kling-preflight.sh +146 -0
  58. package/plugin/scripts/learn-capture.sh +173 -0
  59. package/plugin/scripts/learn-flush.mjs +155 -0
  60. package/plugin/scripts/lesson-hooks.sh +213 -0
  61. package/plugin/scripts/md-stamp.mjs +219 -0
  62. package/plugin/scripts/protect-brain-state.sh +84 -0
  63. package/plugin/scripts/route-dispatch.sh +147 -0
  64. package/plugin/scripts/routing-outcome-capture.mjs +89 -0
  65. package/plugin/scripts/runtime-preferences.mjs +269 -0
  66. package/plugin/scripts/session-start-core.mjs +477 -0
  67. package/plugin/scripts/session-start.sh +13 -0
  68. package/plugin/scripts/signal-watch.mjs +193 -0
  69. package/plugin/scripts/unprompted-runtime.mjs +377 -0
  70. package/plugin/scripts/update-apply.mjs +419 -0
  71. package/plugin/scripts/verify-interface.sh +53 -0
  72. package/plugin/scripts/version-bump-gate.sh +112 -0
  73. package/plugin/skills/brain-build/SKILL.md +123 -0
  74. package/plugin/skills/brain-console/SKILL.md +22 -0
  75. package/plugin/skills/brain-prompt/SKILL.md +83 -0
  76. package/plugin/skills/brain-score/SKILL.md +101 -0
  77. package/plugin/skills/release-proof/SKILL.md +81 -0
  78. package/plugin/skills/release-proof/agents/openai.yaml +4 -0
  79. package/plugin/skills/release-proof/references/receipt-contract.md +38 -0
  80. package/plugin/skills/release-proof/scripts/release-proof.mjs +210 -0
  81. package/plugin/skills/ruvnet-brain/PLAYBOOK.md +121 -0
  82. package/plugin/skills/ruvnet-brain/SKILL.md +234 -0
  83. package/plugin/skills/rvbc/SKILL.md +23 -0
  84. package/plugin/skills/savings/SKILL.md +46 -0
  85. package/plugin/skills/whats-new/SKILL.md +22 -0
  86. package/scripts/adr-backfill.mjs +107 -0
  87. package/scripts/advocacy-outcomes.mjs +808 -0
  88. package/scripts/agentdb-context.mjs +216 -0
  89. package/scripts/agentdb-fleet-doctor.mjs +101 -0
  90. package/scripts/ascii-drift.mjs +236 -0
  91. package/scripts/behavioral-l1-l4.mjs +210 -0
  92. package/scripts/brain-capability-check.mjs +72 -0
  93. package/scripts/brain-grade-groundtruth.mjs +100 -0
  94. package/scripts/brain-latency-50.mjs +227 -0
  95. package/scripts/brain-novice-50.mjs +189 -0
  96. package/scripts/brain-stamp.mjs +94 -0
  97. package/scripts/brain-state.mjs +212 -0
  98. package/scripts/build-bundle.mjs +522 -0
  99. package/scripts/build-concepts.mjs +132 -0
  100. package/scripts/build-l2.mjs +71 -0
  101. package/scripts/build-primer.mjs +73 -0
  102. package/scripts/build-symbols.mjs +68 -0
  103. package/scripts/calibrate-router.mjs +97 -0
  104. package/scripts/capability-audit.mjs +321 -0
  105. package/scripts/capability-registry.mjs +876 -0
  106. package/scripts/check-indexation.mjs +108 -0
  107. package/scripts/check-legibility.mjs +189 -0
  108. package/scripts/ci/build-fixture-kb.mjs +67 -0
  109. package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
  110. package/scripts/ci/learning-replay-recorder.mjs +59 -0
  111. package/scripts/ci/mutate-hook-timeout.mjs +70 -0
  112. package/scripts/ci/stranger-fixture-stage.mjs +17 -0
  113. package/scripts/ci/stranger-scenario.mjs +228 -0
  114. package/scripts/ci/stranger-timeout.mjs +25 -0
  115. package/scripts/ci-verdict.mjs +29 -0
  116. package/scripts/claims-verify.mjs +710 -0
  117. package/scripts/clear-claude-tmp.sh +31 -0
  118. package/scripts/console-engine.mjs +434 -0
  119. package/scripts/console-engine.test.mjs +125 -0
  120. package/scripts/corpus-qa.mjs +250 -0
  121. package/scripts/correction-detect-embed.mjs +346 -0
  122. package/scripts/correction-detect-measure.mjs +270 -0
  123. package/scripts/correction-detect.mjs +686 -0
  124. package/scripts/count-chunks.mjs +54 -0
  125. package/scripts/described-questions.json +30 -0
  126. package/scripts/design-grade.mjs +58 -0
  127. package/scripts/dev-plugin-link.sh +105 -0
  128. package/scripts/distill-project.mjs +200 -0
  129. package/scripts/doc-currency.mjs +801 -0
  130. package/scripts/eval-brain.mjs +244 -0
  131. package/scripts/fix-metaharness-memretrieve.mjs +121 -0
  132. package/scripts/full-hints.mjs +87 -0
  133. package/scripts/gate.sh +39 -0
  134. package/scripts/gates.mjs +146 -0
  135. package/scripts/gen-console-images.mjs +54 -0
  136. package/scripts/gen-images.mjs +47 -0
  137. package/scripts/git-clone-refresh.mjs +52 -0
  138. package/scripts/git-hooks/pre-push +126 -0
  139. package/scripts/goal-match.mjs +398 -0
  140. package/scripts/goldie-research.mjs +223 -0
  141. package/scripts/goldie-weekly.sh +67 -0
  142. package/scripts/health-repair.mjs +250 -0
  143. package/scripts/helix-scenario-questions.json +10 -0
  144. package/scripts/ingest-gists.mjs +230 -0
  145. package/scripts/ingest-meeting.mjs +115 -0
  146. package/scripts/ingest-repo.mjs +79 -0
  147. package/scripts/install-npx-witness.sh +49 -0
  148. package/scripts/issue-fix.mjs +639 -0
  149. package/scripts/issue-watch.mjs +276 -0
  150. package/scripts/issue4-close-note.md +31 -0
  151. package/scripts/key-canary.mjs +91 -0
  152. package/scripts/latency-to-surface.mjs +233 -0
  153. package/scripts/learning-enable.mjs +380 -0
  154. package/scripts/learning-replay.mjs +1570 -0
  155. package/scripts/learnings.mjs +62 -0
  156. package/scripts/lesson-gate.mjs +680 -0
  157. package/scripts/lesson-lifecycle.mjs +449 -0
  158. package/scripts/lesson-promote.mjs +262 -0
  159. package/scripts/lesson-ratify.mjs +98 -0
  160. package/scripts/lesson-seed.mjs +252 -0
  161. package/scripts/lesson-store.mjs +447 -0
  162. package/scripts/loop-checkpoint.mjs +86 -0
  163. package/scripts/memdb-health.sh +14 -0
  164. package/scripts/memory-doctor.mjs +271 -0
  165. package/scripts/model-catalog.mjs +79 -0
  166. package/scripts/nightly-controller.mjs +66 -0
  167. package/scripts/nightly-gists.sh +72 -0
  168. package/scripts/nightly-wrapper.sh +180 -0
  169. package/scripts/notify.sh +12 -0
  170. package/scripts/npx-witness.sh +56 -0
  171. package/scripts/onboarding-console.mjs +2749 -0
  172. package/scripts/private-fence.mjs +69 -0
  173. package/scripts/proactivity-metrics.mjs +118 -0
  174. package/scripts/proof-questions.json +56 -0
  175. package/scripts/prove.mjs +95 -0
  176. package/scripts/proxy/claude-proxied.sh +57 -0
  177. package/scripts/proxy/proxy-revert.sh +59 -0
  178. package/scripts/proxy/proxy-up.sh +60 -0
  179. package/scripts/proxy/proxy-verify.mjs +142 -0
  180. package/scripts/published-surface-probe.mjs +241 -0
  181. package/scripts/qe/card-lane-gate.mjs +162 -0
  182. package/scripts/qe/session-start-gate.mjs +229 -0
  183. package/scripts/qe/ux-suite.mjs +323 -0
  184. package/scripts/reconcile-project.mjs +0 -0
  185. package/scripts/record-lesson.mjs +113 -0
  186. package/scripts/refresh-model-catalog.mjs +99 -0
  187. package/scripts/release-proof.mjs +9 -0
  188. package/scripts/release-vector.mjs +281 -0
  189. package/scripts/release.mjs +395 -0
  190. package/scripts/remedy-registry.mjs +247 -0
  191. package/scripts/rerank-cap-eval.mjs +265 -0
  192. package/scripts/rerank-cap-warm-ab.mjs +129 -0
  193. package/scripts/route-cheap.mjs +20 -15
  194. package/scripts/router-utilization.mjs +182 -0
  195. package/scripts/routing-flywheel.mjs +596 -0
  196. package/scripts/rvf-generation.mjs +104 -0
  197. package/scripts/rvf-index-audit.mjs +138 -0
  198. package/scripts/self-update.mjs +508 -0
  199. package/scripts/selfcheck.mjs +7 -1
  200. package/scripts/sign-bundle.mjs +69 -0
  201. package/scripts/signal-watch.mjs +171 -0
  202. package/scripts/stack-sync.mjs +469 -0
  203. package/scripts/stamp-existing-rvf-generations.mjs +53 -0
  204. package/scripts/stamp-sweep.mjs +144 -0
  205. package/scripts/status-honesty.mjs +102 -0
  206. package/scripts/sync-version.mjs +217 -0
  207. package/scripts/token-report.mjs +102 -0
  208. package/scripts/top100-benchmark.mjs +479 -0
  209. package/scripts/top100-corpus.mjs +112 -0
  210. package/scripts/top100-semantic-assertions.mjs +449 -0
  211. package/scripts/update-apply.mjs +9 -0
  212. package/scripts/upgrade-notice.mjs +14 -0
  213. package/scripts/verify-bundle.mjs +51 -0
  214. package/scripts/verify-channels.mjs +184 -0
  215. package/scripts/verify-model-catalog.mjs +104 -0
  216. package/scripts/verify-nightly-close-issue4.sh +31 -0
  217. package/scripts/version.mjs +40 -0
  218. package/scripts/wired-check.mjs +864 -0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # gate-receipt.sh — record what a gate CAUGHT.
3
+ #
4
+ # ─────────────────────────────────────────────────────────────────────────────────────────────────
5
+ # WHY (2026-07-17). The walls logged only their successes. design-grades.jsonl held 13 receipts,
6
+ # 13 of them passing — yet the design wall had just blocked a commit minutes earlier. The block left
7
+ # no trace: the ledger showed the 96 that came after the fix, and nothing about the refusal that
8
+ # forced it. So the system could prove Claude complied and could not prove it had ever CAUGHT Claude,
9
+ # which is the only part anyone would want to see. Stuart, looking at the console: "make it worth
10
+ # something, because right now it seems to be facts without purpose."
11
+ #
12
+ # A gate that stops something and says nothing is unfalsifiable. This makes the catch auditable.
13
+ #
14
+ # CONTRACT: called by a blocking gate immediately before it exits non-zero.
15
+ # gate-receipt.sh <gate> <subject> <reason>
16
+ # NEVER fails, never blocks, never writes to stdout/stderr — a receipt that breaks a gate would
17
+ # trade a real protection for a log line. Every failure path here is swallowed on purpose.
18
+ # ─────────────────────────────────────────────────────────────────────────────────────────────────
19
+ set -uo pipefail
20
+
21
+ F="$HOME/.cache/ruvnet-brain/gate-blocks.jsonl"
22
+ mkdir -p "$(dirname "$F")" 2>/dev/null || exit 0
23
+
24
+ # Strip the few characters that would break a JSON line. Reasons are short human strings, not data.
25
+ clean() { printf '%s' "${1:-}" | tr -d '"\\' | tr '\n\r\t' ' ' | cut -c1-160; }
26
+
27
+ printf '{"at":"%s","gate":"%s","subject":"%s","reason":"%s","cwd":"%s"}\n' \
28
+ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
29
+ "$(clean "${1:-unknown}")" \
30
+ "$(clean "${2:-}")" \
31
+ "$(clean "${3:-}")" \
32
+ "$(clean "$(basename "${CLAUDE_PROJECT_DIR:-$PWD}")")" \
33
+ >> "$F" 2>/dev/null || true
34
+
35
+ exit 0
@@ -0,0 +1,199 @@
1
+ #!/bin/bash
2
+ # ground-before-write.sh — PreToolUse gate on Write|Edit.
3
+ # YOU MAY NOT WRITE RUV-DOMAIN CODE THE BRAIN HAS NOT SEEN FIRST.
4
+ #
5
+ # ─────────────────────────────────────────────────────────────────────────────────────────────
6
+ # WHY (2026-07-13). Stuart, after losing a full day: "The 100% entire reason we built RuvNet
7
+ # Brain was to prevent exactly this scenario... come up with a way to keep Claude Code from
8
+ # stepping in and overriding RuvNet-Brain."
9
+ #
10
+ # What happened, twice in one week: Claude hand-wrote agentdb-autocapture.mjs (prompt-echo
11
+ # snapshots) while rUv's ADR-174 `memory distill` pipeline shipped the real design; Claude
12
+ # hand-wrote a 216-line "MetaHarness router" while @metaharness/router sat on npm. Both times
13
+ # the brain HELD the answer and was never asked. The write path had no wall.
14
+ #
15
+ # rUv names the disease and the cure himself (@claude-flow/guidance, ADR-G007):
16
+ # "prompts are advisory. Agents can and do ignore them, especially in long sessions."
17
+ # "The model can forget a rule; the gate does not."
18
+ # And his own hooks doc wires exactly this shape: PreToolUse on ^(Write|Edit|MultiEdit)$
19
+ # (ruflo/.claude/commands/hooks/overview.md).
20
+ #
21
+ # THE RULE: writing/editing a CODE file that touches a RuvNet ecosystem product requires a
22
+ # fresh (<24h) grounding stamp for that product — written only by grounding-stamp.sh when
23
+ # search_ruvnet is genuinely called with that product in the query.
24
+ #
25
+ # Deliberate scope choices (so this never becomes a tax that gets switched off):
26
+ # • CODE files only (.mjs/.js/.ts/.sh/...). Docs/README claims are enforced by the CI gates
27
+ # (claims-verify, no-silent-substitution), not per-keystroke.
28
+ # • Product terms only (agentdb, metaharness, ...). Generic words like "memory"/"hook"
29
+ # would fire on half of all software.
30
+ # • Block only the UNGROUNDED terms — grounding agentdb unlocks agentdb, not metaharness.
31
+ # Granularity matches the mistake, same as verify-interface.sh's per-subcommand stamps.
32
+ #
33
+ # CONTRACT: exit 0 = allow · exit 2 + stderr = BLOCK (stderr returns to the model as reason).
34
+ # FAILS OPEN on anything unparseable. Opt-in (router profile), like every gate here.
35
+ # ─────────────────────────────────────────────────────────────────────────────────────────────
36
+
37
+ set -uo pipefail
38
+
39
+ INPUT=""
40
+ # BOUNDED READ (2026-07-27, ADR-055 F20): an unqualified `read` never returns on a stdin that is
41
+ # opened and never closed — measured across the mesh, 18 of 37 registered commands sat until the
42
+ # harness killed them. Real Claude Code writes and closes, so this costs no normal turn; that is
43
+ # exactly why a hook that CAN hang forever survives unnoticed. -t bounds the wait, and the string
44
+ # is truncated AFTER the loop because a hook payload is one line with no newline, so `read` hands
45
+ # the whole thing back at once and a per-iteration cap never fires.
46
+ while IFS= read -r -t 2 _l; do
47
+ INPUT+="$_l"
48
+ [ ${#INPUT} -ge 65536 ] && break
49
+ done
50
+ [ -n "$_l" ] && INPUT+="$_l"
51
+ INPUT="${INPUT:0:65536}"
52
+ [ -n "$INPUT" ] || exit 0
53
+
54
+ # ── BRAIN OFF — THE FIRST CHECK IN THE FILE (ADR-054 §3). ───────────────────────────────────────
55
+ # This gate demands a fresh grounding stamp, and a stamp can only be minted by a successful
56
+ # search_ruvnet. With the brain switched off there IS no search_ruvnet, so an unmodified gate would
57
+ # block every rUv-domain write forever with an instruction the user has deliberately made
58
+ # impossible to follow. That is the "something bad happened because it can be turned off" failure
59
+ # the owner's build mandate rules out, and it is why this check comes before the opt-in check,
60
+ # before the tool check and before the file-type check: nothing this gate does downstream is
61
+ # meaningful while its evidence source is switched off.
62
+ #
63
+ # It degrades to ONE advisory line on stdout and exit 0 — never a block. PreToolUse stdout on exit 0
64
+ # is transcript-only (it does not enter the model's context), so this is legibility for the person
65
+ # reading their own session, not another channel of unsolicited speech.
66
+ #
67
+ # Both duel reviewers independently caught that this hook is wired in the USER's settings.json,
68
+ # OUTSIDE plugin/hooks/hooks.json and therefore outside the shim's dispatch table — so the shim's
69
+ # per-hook offBehavior cannot reach it and the check has to live here, in the body. It is a pure
70
+ # `[ -f ]` on a path built from bash parameter expansion only: this gate's standing contract
71
+ # (ADR-0021, enforced by ground-before-write.test.mjs) is that it depends on no node, no jq, no
72
+ # python, so it can never fail-open because a tool went missing.
73
+ #
74
+ # The `! -r` clause is the bash half of a polarity bug gate test 5 found in the node half: an
75
+ # unreadable state directory makes `[ -f ]` (like fs.existsSync) answer "no sentinel", which would
76
+ # re-arm this gate for a user who had switched the brain off — the two readers disagreeing about the
77
+ # same machine. "The directory is there and we cannot read it" is not evidence of ON.
78
+ BRAIN_STATE_DIR="${RUVNET_BRAIN_STATE_DIR:-$HOME/.config/ruvnet-brain}"
79
+ BRAIN_OFF_FILE="$BRAIN_STATE_DIR/brain-off"
80
+ if [ -f "$BRAIN_OFF_FILE" ] || { [ -d "$BRAIN_STATE_DIR" ] && [ ! -r "$BRAIN_STATE_DIR" ]; }; then
81
+ echo "[RuvNet Brain is off by your setting — the grounding gate is advisory only, nothing is blocked.]"
82
+ exit 0
83
+ fi
84
+
85
+ PROFILE="${MODEL_ROUTER_PROFILE:-$HOME/.claude/model-router/profile.json}"
86
+ [ -f "$PROFILE" ] || exit 0
87
+
88
+ # The blanket override, NARROWED (ADR-055 §3.5, verbatim: "the blanket RUVNET_SKIP_GROUNDING_CHECK=1
89
+ # regime is retired for the fourth wall"). It still disarms the RECENCY wall below, exactly as
90
+ # documented since ADR-0012 — but it no longer disarms the SUBSTANCE stage, which has its own
91
+ # in-band, reasoned, receipted token. The reason is the whole point of issue #46: recency is a
92
+ # ceremony you can reasonably choose to skip, while "you are writing the opposite of the source you
93
+ # were just shown" is not a ceremony, and a variable exported once at the top of a session must not
94
+ # buy a day of contradicting writes. Note the substance stage never blocks on ABSENCE of evidence,
95
+ # so with the recency wall skipped and nothing stamped, it permits — as it should.
96
+ SKIP_RECENCY=0
97
+ [ "${RUVNET_SKIP_GROUNDING_CHECK:-0}" = "1" ] && SKIP_RECENCY=1
98
+
99
+ # This gate is a BLOCKING wall (the #1-failure preventer), so by deliberate design (ADR-0021, and
100
+ # enforced by ground-before-write.test.mjs) it depends on NOTHING fragile — pure bash builtins, no
101
+ # node/jq/python — and therefore cannot fail-open because a tool went missing. The #13 quote-truncation
102
+ # that justified hook-input.mjs for design-wall does NOT bite here: the product-term scan below runs
103
+ # over the RAW payload (untouched by field()), and the only parsed values are tool_name (Write/Edit —
104
+ # no quotes) and file_path (a truncated path merely fails the extension check → exit 0, harmless).
105
+ field() { local re="\"$1\"[[:space:]]*:[[:space:]]*\"([^\"]*)\""; [[ $INPUT =~ $re ]] && printf '%s' "${BASH_REMATCH[1]}"; }
106
+
107
+ case "$(field tool_name)" in Write|Edit|MultiEdit) ;; *) exit 0 ;; esac
108
+
109
+ FILE_PATH=$(field file_path)
110
+ [ -n "$FILE_PATH" ] || exit 0
111
+ case "$FILE_PATH" in
112
+ *.mjs|*.cjs|*.js|*.ts|*.tsx|*.jsx|*.sh|*.bash|*.zsh|*.rs|*.py|*.go) ;;
113
+ *) exit 0 ;;
114
+ esac
115
+ # The grounding-guidance hooks ENUMERATE every rUv product by design — that IS their job: telling the
116
+ # model which rUv primitive replaces which classical default. They don't hand-roll or call any rUv
117
+ # tool, so demanding a fresh stamp per listed term just to EDIT the guidance is a false positive — the
118
+ # gate firing on its own source material (it blocked a trim of ground-ruvnet.sh on 2026-07-18). Exempt
119
+ # these two by basename; the repo-wide CI gates (claims-verify, no-silent-substitution) still cover them.
120
+ case "$FILE_PATH" in
121
+ */ground-ruvnet.sh|*/session-start.sh) exit 0 ;;
122
+ esac
123
+
124
+ shopt -s nocasematch 2>/dev/null || true
125
+
126
+ STAMP_DIR="$HOME/.cache/ruvnet-brain/grounded"
127
+ NOW=$(date +%s 2>/dev/null) || exit 0
128
+
129
+ # Scan the WHOLE tool input (path + content + new_string) — where the code mentions the
130
+ # product is where the hand-roll hides.
131
+ MISSING=""
132
+ SEEN=""
133
+ for t in agentdb metaharness ruvector aidefence agentic-flow agentic-qe ruv-swarm rvf ruflo; do
134
+ [[ $INPUT == *"$t"* ]] || continue
135
+ SEEN="$SEEN$t "
136
+ STAMP="$STAMP_DIR/$t"
137
+ if [ -f "$STAMP" ]; then
138
+ # GNU date reads a file's mtime with -r; BSD/macOS needs stat -f %m. Either failing → allow
139
+ # (fail open — a gate that bricks a session is worse than the bug it prevents).
140
+ THEN=$(date -r "$STAMP" +%s 2>/dev/null) || THEN=$(stat -f %m "$STAMP" 2>/dev/null) || exit 0
141
+ [ $((NOW - THEN)) -lt 86400 ] && continue
142
+ fi
143
+ MISSING="$MISSING$t "
144
+ done
145
+
146
+ # ═══════════════════════════════════════════════════════════════════════════════════════════════
147
+ # STAGE 2 — THE SUBSTANCE WALL (ADR-055 §3, issue #46). Runs only once the recency wall above has
148
+ # PASSED: a term the model has never grounded is stopped by stage 1 and never reaches here.
149
+ #
150
+ # ── THE DEPENDENCY CONTRACT, AND WHY THIS DOES NOT BREAK IT ────────────────────────────────────
151
+ # Everything above this line is pure bash builtins, by standing contract (ADR-0021, enforced by
152
+ # ground-before-write.test.mjs): the recency wall is THE blocking wall, so it can never fail open
153
+ # because a tool went missing. That contract is unchanged and still tested — the assertion now
154
+ # reads the file UP TO THIS MARKER.
155
+ #
156
+ # The substance stage is different in kind and is allowed one dependency, node, because deciding
157
+ # "does this write contradict the source the brain returned?" requires a real JSON parse of the
158
+ # payload (this repo has paid twice for pretending otherwise — issue #13's quote truncation) plus a
159
+ # read of the evidence ledger. It is therefore built to FAIL OPEN, loudly and by construction: no
160
+ # node, no checker file, no ledger, any non-2 exit → the write proceeds. The only thing that can
161
+ # produce a refusal here is a detector that found a contradiction, which is ADR-055 §1.2's
162
+ # "malfunction ≠ decision" applied to the one stage that has something to malfunction.
163
+ # ═══════════════════════════════════════════════════════════════════════════════════════════════
164
+ if [ -z "$MISSING" ] || [ "$SKIP_RECENCY" = "1" ]; then
165
+ [ -n "$SEEN" ] || exit 0
166
+ SUBSTANCE="$(dirname "${BASH_SOURCE[0]}")/grounding-substance.mjs"
167
+ [ -f "$SUBSTANCE" ] || exit 0
168
+ command -v node >/dev/null 2>&1 || exit 0
169
+ SUB_OUT=$(printf '%s' "$INPUT" | node "$SUBSTANCE" 2>&1)
170
+ SUB_RC=$?
171
+ if [ "$SUB_RC" = "2" ]; then
172
+ printf '%s\n' "$SUB_OUT" >&2
173
+ exit 2
174
+ fi
175
+ # Anything else — clean pass, a crash, a timeout, an unknown code — permits the write. An override
176
+ # acceptance line (exit 0 with stderr) is surfaced so the record is visible in the transcript.
177
+ [ -n "$SUB_OUT" ] && printf '%s\n' "$SUB_OUT" >&2
178
+ exit 0
179
+ fi
180
+
181
+ read -r -d '' MSG <<EOF || true
182
+ ⛔ BLOCKED — you are writing rUv-domain code the brain has not seen: ${MISSING% }
183
+
184
+ This exact move has already cost a full day TWICE: a hand-rolled agentdb capture hook while
185
+ rUv's ADR-174 distill pipeline shipped the real design, and a fake "MetaHarness router" while
186
+ @metaharness/router sat on npm. Both times the brain held the answer and was never asked.
187
+
188
+ Before writing this file, ground each blocked term in the RuvNet Brain — call the
189
+ search_ruvnet MCP tool with the product in the query, e.g.:
190
+
191
+ search_ruvnet({ query: "${MISSING%% *}: how does rUv implement / recommend this?" })
192
+
193
+ Reading real source stamps the term for 24h and this gate opens. rUv's own rule (ADR-G007):
194
+ prompts are advisory; the gate is not. EFFECTIVE BEATS EFFICIENT.
195
+ (Deliberate override, say why out loud: RUVNET_SKIP_GROUNDING_CHECK=1)
196
+ EOF
197
+ bash "$(dirname "${BASH_SOURCE[0]}")/gate-receipt.sh" ground-before-write "${MISSING%% *}" "rUv-product code without a fresh search_ruvnet stamp" 2>/dev/null || true
198
+ printf '%s\n' "$MSG" >&2
199
+ exit 2