ruvnet-brain 4.5.8 โ†’ 4.5.9

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 CHANGED
@@ -7,7 +7,7 @@ Created: 2026-06-29 22:36:38 EDT
7
7
 
8
8
  # ๐Ÿง  RuvNet Brain
9
9
 
10
- ### ๐Ÿง  RuvNet Brain โ€” [![RuvNet Brain version 4.5.8 โ€” updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.5.8-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
10
+ ### ๐Ÿง  RuvNet Brain โ€” [![RuvNet Brain version 4.5.9 โ€” updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.5.9-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
11
11
 
12
12
  **A portable, source-grounded brain over Reuven Cohen's (rUv's) RuvNet stack โ€” delivered as a Claude Code plugin that makes Claude _use_ the stack instead of fighting it.**
13
13
 
package/bin/install.mjs CHANGED
@@ -461,7 +461,9 @@ function resolveCacheDir() {
461
461
  );
462
462
  info(`brain dir: ${c.bold(cacheDir)}`);
463
463
  if (custom) info(`(from your RUVNET_BRAIN_KB override)`);
464
- fs.mkdirSync(cacheDir, { recursive: true });
464
+ // Activation creates the KB from a validated stage. A placeholder here becomes a false prior
465
+ // generation, which the cleanup proof correctly retains because it has no SOURCE.json.
466
+ fs.mkdirSync(path.dirname(cacheDir), { recursive: true });
465
467
  return { cacheDir, isCustom: Boolean(custom) };
466
468
  }
467
469
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
- "version": "4.5.8",
3
+ "version": "4.5.9",
4
4
  "description": "One-command installer for RuvNet Brain \u2014 a portable, source-grounded brain over rUv's RuvNet building blocks, delivered as a Claude Code plugin so Claude uses the stack instead of fighting it.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
3
  "description": "RuvNet brain transplant for Claude Code โ€” grounds every RuvNet decision in real source across 77 rUv repositories, prefers Ruflo / RuVector-RVF / AgentDB over training-prior defaults (pgvector, Pinecone, hand-rolled cosine), and can pull in any RuvNet repo on demand. Ships a UserPromptSubmit retrieve-and-inject grounding hook and a PreToolUse write gate that refuses ungrounded rUv-product code until search_ruvnet has been consulted (ADR-0012 / ADR-067).",
4
- "version": "4.5.8",
4
+ "version": "4.5.9",
5
5
  "author": {
6
6
  "name": "Stuart Kerr"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
- "version": "4.5.8",
3
+ "version": "4.5.9",
4
4
  "description": "Source-grounded RuvNet knowledge, lifecycle enforcement, and learning for Codex.",
5
5
  "author": {
6
6
  "name": "Stuart Kerr"
@@ -145,6 +145,7 @@ export const INTENTS = Object.freeze([
145
145
  fit: 'the request is to retrieve by meaning rather than by literal match',
146
146
  cues: Object.freeze([
147
147
  /\bby meaning\b/,
148
+ /^(?:please\s+)?(?:search|retrieve|find)\s+(?:these|those|my|our|the)\s+(?:docs?|documents?|passages?|chunks?|notes?)\s+by meaning\b/,
148
149
  /\bsemantic(ally)?\s+(search|retriev|match|similar)/,
149
150
  /\b(instead of|rather than|not just|beyond)\s+(an?\s+)?(exact\s+)?(keywords?|string|substring|literal|grep)/,
150
151
  /\bvector\s+(search|database|db|store|index|embedding)/,
@@ -98,11 +98,10 @@ SKIP_RECENCY=0
98
98
  [ "${RUVNET_SKIP_GROUNDING_CHECK:-0}" = "1" ] && SKIP_RECENCY=1
99
99
 
100
100
  # This gate is a BLOCKING wall (the #1-failure preventer), so by deliberate design (ADR-0021, and
101
- # enforced by ground-before-write.test.mjs) it depends on NOTHING fragile โ€” pure bash builtins, no
102
- # node/jq/python โ€” and therefore cannot fail-open because a tool went missing. The #13 quote-truncation
103
- # that justified hook-input.mjs for design-wall does NOT bite here: the product-term scan below runs
104
- # over the RAW payload (untouched by field()), and the only parsed values are tool_name (Write/Edit โ€”
105
- # no quotes) and file_path (a truncated path merely fails the extension check โ†’ exit 0, harmless).
101
+ # enforced by ground-before-write.test.mjs) its default refusal depends on no JSON parser. The optional
102
+ # inert-code exemption below can narrow a valid input; a missing helper or any uncertainty leaves the
103
+ # raw product scan intact. The legacy field parser below selects only tool name and code-file path;
104
+ # it never supplies the code text used by the optional projection.
106
105
  field() { local re="\"$1\"[[:space:]]*:[[:space:]]*\"([^\"]*)\""; [[ $INPUT =~ $re ]] && printf '%s' "${BASH_REMATCH[1]}"; }
107
106
 
108
107
  # Case-insensitive (4.5): Grok sends `write` / `search_replace`; a case-sensitive match allowed a Grok
@@ -134,8 +133,8 @@ shopt -s nocasematch 2>/dev/null || true
134
133
  STAMP_DIR="$HOME/.cache/ruvnet-brain/grounded"
135
134
  NOW=$(date +%s 2>/dev/null) || exit 0
136
135
 
137
- # Scan the WHOLE tool input (path + content + new_string) โ€” where the code mentions the
138
- # product is where the hand-roll hides.
136
+ # Scan raw product terms without a parser; the optional inert-text exemption below may narrow
137
+ # this result, but missing dependencies cannot erase the raw refusal.
139
138
  MISSING=""
140
139
  SEEN=""
141
140
  for t in agentdb metaharness ruvector aidefence agentic-flow agentic-qe ruv-swarm rvf ruflo; do
@@ -156,10 +155,10 @@ done
156
155
  # PASSED: a term the model has never grounded is stopped by stage 1 and never reaches here.
157
156
  #
158
157
  # โ”€โ”€ THE DEPENDENCY CONTRACT, AND WHY THIS DOES NOT BREAK IT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
159
- # Everything above this line is pure bash builtins, by standing contract (ADR-0021, enforced by
160
- # ground-before-write.test.mjs): the recency wall is THE blocking wall, so it can never fail open
161
- # because a tool went missing. That contract is unchanged and still tested โ€” the assertion now
162
- # reads the file UP TO THIS MARKER.
158
+ # Everything above this marker remains pure bash: the recency wall computes a refusal without
159
+ # node/jq/python (ADR-0021). The optional exemption below can only narrow that raw result after
160
+ # proving an inert mention; absence or uncertainty retains it. The substance stage keeps its
161
+ # separate, explicitly fail-open contradiction contract.
163
162
  #
164
163
  # The substance stage is different in kind and is allowed one dependency, node, because deciding
165
164
  # "does this write contradict the source the brain returned?" requires a real JSON parse of the
@@ -169,6 +168,23 @@ done
169
168
  # produce a refusal here is a detector that found a contradiction, which is ADR-055 ยง1.2's
170
169
  # "malfunction โ‰  decision" applied to the one stage that has something to malfunction.
171
170
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
171
+ # Optional positive exemption (#373), only when a raw product mention exists. A missing helper,
172
+ # node, empty output or lexical uncertainty leaves the pure-bash result unchanged. This stage
173
+ # cannot manufacture a refusal and never changes the raw payload used by the substance wall.
174
+ PROJECTION="$(dirname "${BASH_SOURCE[0]}")/grounding-code-projection.mjs"
175
+ if [ -n "$SEEN" ] && [ -f "$PROJECTION" ] && command -v node >/dev/null 2>&1; then
176
+ PROJECTED=$(printf '%s' "$INPUT" | node "$PROJECTION" 2>/dev/null)
177
+ if [ "$?" = "0" ] && [ -n "$PROJECTED" ]; then
178
+ PROJECTED_MISSING=""; PROJECTED_SEEN=""
179
+ for t in $SEEN; do
180
+ [[ $PROJECTED == *"$t"* ]] || continue
181
+ PROJECTED_SEEN="$PROJECTED_SEEN$t "
182
+ [[ " $MISSING" == *" $t "* ]] && PROJECTED_MISSING="$PROJECTED_MISSING$t "
183
+ done
184
+ MISSING="$PROJECTED_MISSING"; SEEN="$PROJECTED_SEEN"
185
+ fi
186
+ fi
187
+
172
188
  if [ -z "$MISSING" ] || [ "$SKIP_RECENCY" = "1" ]; then
173
189
  [ -n "$SEEN" ] || exit 0
174
190
  SUBSTANCE="$(dirname "${BASH_SOURCE[0]}")/grounding-substance.mjs"
@@ -0,0 +1,85 @@
1
+ // Optional conservative #373 projection. Uncertainty keeps the original raw bash gate.
2
+ // Keep executable strings and paths: mentioning a product in code still requires grounding.
3
+ import fs from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ export function codeWithoutInertText(source, python = false) {
7
+ if (/\r(?!\n)|[\u2028\u2029]/.test(source)) throw new Error('Unsupported line boundary');
8
+ // Template interpolation and Python f-string expressions need a parser, not a guessed lexer.
9
+ if ((!python && source.includes('`')) || (python && /(?:^|[^\w])(?:fr|rf|f)["']/i.test(source))) throw new Error('Interpolation requires original scan');
10
+ if (python) {
11
+ // A different source decoder can turn apparent comments into executable statements.
12
+ for (const line of source.split(/\r?\n/).slice(0, 2)) {
13
+ const cookie = line.match(/^[ \t\f]*#.*?coding[=:][ \t]*([^ \t\r\n]+)/i);
14
+ if (cookie && !/^utf[-_]?8$/i.test(cookie[1])) throw new Error('Unsupported source encoding');
15
+ }
16
+ if (/\\\r?\n/.test(source)) throw new Error('Continuation requires original scan');
17
+ }
18
+ let out = ''; let i = 0;
19
+ while (i < source.length) {
20
+ const c = source[i]; const pair = source.slice(i, i + 2);
21
+ if ((python && c === '#') || (!python && pair === '//')) {
22
+ const end = source.indexOf('\n', i); i = end < 0 ? source.length : end; continue;
23
+ }
24
+ if (!python && pair === '/*') {
25
+ const end = source.indexOf('*/', i + 2); if (end < 0) throw new Error('Incomplete comment');
26
+ out += ' '; i = end + 2; continue;
27
+ }
28
+ if (c === '"' || c === "'" || (!python && c === '`')) {
29
+ const start = i; const triple = python && source.slice(i, i + 3) === c.repeat(3);
30
+ const delimiter = triple ? c.repeat(3) : c;
31
+ i += delimiter.length;
32
+ let closed = false;
33
+ while (i < source.length) {
34
+ if (source[i] === '\\') { i += 2; continue; }
35
+ if (source.slice(i, i + delimiter.length) === delimiter) { i += delimiter.length; closed = true; break; }
36
+ if (!triple && c !== '`' && /[\r\n]/.test(source[i])) throw new Error('Incomplete string');
37
+ i++;
38
+ }
39
+ if (!closed) throw new Error('Incomplete string');
40
+ // Exempt only a leading module docstring before any executable text. Standalone-looking
41
+ // literals inside functions, open expressions or control flow retain the original scan.
42
+ const before = source.slice(source.lastIndexOf('\n', start - 1) + 1, start);
43
+ const endOfLine = source.indexOf('\n', i); const after = source.slice(i, endOfLine < 0 ? source.length : endOfLine);
44
+ const inert = triple && out.trim() === '' && !/\b(?:exec|eval|compile|__doc__)\b/.test(source) && /^[ \t]*$/.test(before) && /^[ \t\r]*(?:#.*)?$/.test(after);
45
+ out += inert ? ' ' : source.slice(start, i); continue;
46
+ }
47
+ // Regex literals, heredocs, language-specific nested comments etc. are not guessed.
48
+ if (!python && c === '/') throw new Error('Ambiguous slash');
49
+ if (!python && pair === '<' + '<') throw new Error('Unsupported heredoc');
50
+ out += c; i++;
51
+ }
52
+ return out;
53
+ }
54
+
55
+ export function projectGroundingInput(raw) {
56
+ const event = JSON.parse(raw); const input = event.tool_input ?? event.toolInput;
57
+ if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('Unknown write input');
58
+ const file = input.file_path; if (typeof file !== 'string') throw new Error('Unknown path');
59
+ // Limited lexical scope; other languages retain their existing strict gate.
60
+ if (!/\.(?:py|[cm]?js|jsx|ts|tsx)$/.test(file)) throw new Error('Unsupported language');
61
+ // Partial edits have no enclosing lexical context. Only complete Write or a verified
62
+ // single Add File patch may receive an exemption; never reconstruct user files here.
63
+ const tool = event.tool_name ?? event.toolName;
64
+ let code;
65
+ if (tool === 'Write' && typeof input.content === 'string' && !Object.hasOwn(input, 'edits')) {
66
+ code = input.content;
67
+ } else if (tool === 'Edit' && typeof input.new_string === 'string' && input.new_string.startsWith('*** Begin Patch')) {
68
+ const lines = input.new_string.trimEnd().split(/\r?\n/);
69
+ if (lines[0] !== '*** Begin Patch' || lines[1] !== `*** Add File: ${file}`
70
+ || lines.at(-1) !== '*** End Patch' || !lines.slice(2, -1).every((line) => line.startsWith('+'))) {
71
+ throw new Error('Ambiguous patch');
72
+ }
73
+ code = lines.slice(2, -1).map((line) => line.slice(1)).join('\n');
74
+ } else throw new Error('Partial or unknown write context');
75
+ const projectedCode = codeWithoutInertText(code, file.endsWith('.py'));
76
+ // Never exempt a product-owned path. Preserve managed-store paths even without a product name.
77
+ const projection = `${file}\n${projectedCode}`;
78
+ if (/\.swarm|(?:agentdb-)?memory\.db|memory_entries/i.test(projection)) throw new Error('Managed memory indicators need original scan');
79
+ return projection;
80
+ }
81
+
82
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
83
+ try { process.stdout.write(projectGroundingInput(fs.readFileSync(0, 'utf8'))); }
84
+ catch { process.exitCode = 1; } // A failed exemption leaves the original guard in force.
85
+ }
@@ -1,6 +1,15 @@
1
1
  // Reviewed stabilization release boundaries. Legacy tests remain developer diagnostics.
2
2
  export const RELEASE_REQUIREMENTS = Object.freeze({
3
3
  "source": [
4
+ {
5
+ "id": "bounded-advocacy-and-write-grounding",
6
+ "reason": "Explicit document meaning requests retain corroboration; literal searches stay silent; POSIX complete-write exemptions preserve executable and managed-store grounding",
7
+ "files": ["tests/unit/advocacy-route.test.mjs", "tests/unit/advocacy-catalog.test.mjs", "tests/unit/card-lane.test.mjs"],
8
+ "platformFiles": {
9
+ "linux": ["tests/unit/advocacy-route-budget.test.mjs", "tests/unit/ground-before-write.test.mjs", "tests/unit/grounding-code-projection.test.mjs"],
10
+ "macos": ["tests/unit/advocacy-route-budget.test.mjs", "tests/unit/ground-before-write.test.mjs", "tests/unit/grounding-code-projection.test.mjs"]
11
+ }
12
+ },
4
13
  {
5
14
  "id": "owned-startup-execution-evidence",
6
15
  "reason": "Opt-in native execution and private stage diagnostics bind released source and preserve unknown convergence and incomplete cleanup boundaries",