ruvnet-brain 4.5.16 → 4.5.17
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 +1 -1
- package/bin/install.mjs +1 -1
- package/config/practical-rule-catalog.json +111 -0
- package/package.json +2 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/hooks/codex-hooks.json +13 -1
- package/plugin/hooks/hook-contracts.json +20 -4
- package/plugin/scripts/adr-currency-gate.mjs +75 -31
- package/plugin/scripts/advocacy-outcomes.mjs +18 -4
- package/plugin/scripts/agentdb-recall.mjs +161 -26
- package/plugin/scripts/capability-inventory-receipt.mjs +16 -7
- package/plugin/scripts/capability-registry.mjs +55 -114
- package/plugin/scripts/capacity-aware-parallel-work.mjs +1 -1
- package/plugin/scripts/codex-hook-adapter.mjs +33 -6
- package/plugin/scripts/codex-hook-wrapper.mjs +23 -3
- package/plugin/scripts/continuity-brief.mjs +55 -22
- package/plugin/scripts/continuity-events.mjs +93 -17
- package/plugin/scripts/continuity-hook-policy.mjs +1 -1
- package/plugin/scripts/continuity-journal.mjs +77 -29
- package/plugin/scripts/decision-gate.mjs +63 -22
- package/plugin/scripts/decision-outcomes.mjs +192 -199
- package/plugin/scripts/detach.mjs +53 -13
- package/plugin/scripts/doc-currency.mjs +993 -0
- package/plugin/scripts/ground-ruvnet.sh +16 -3
- package/plugin/scripts/grounding-answer.mjs +18 -16
- package/plugin/scripts/grounding-stamp.sh +3 -0
- package/plugin/scripts/grounding-turn-evidence.mjs +60 -65
- package/plugin/scripts/grounding-turn-gate.mjs +34 -29
- package/plugin/scripts/grounding-turn-mark.mjs +101 -17
- package/plugin/scripts/hook-context-budget.mjs +90 -0
- package/plugin/scripts/hook-registry.mjs +129 -1
- package/plugin/scripts/hook-shim.mjs +20 -2
- package/plugin/scripts/lesson-gate.mjs +1 -1
- package/plugin/scripts/project-capture-queue.mjs +22 -15
- package/plugin/scripts/project-progression-producer.mjs +15 -8
- package/plugin/scripts/project-progression-reader.mjs +14 -4
- package/plugin/scripts/project-progression-session-start.mjs +29 -17
- package/plugin/scripts/project-progression-sources.mjs +24 -10
- package/plugin/scripts/project-progression-store.mjs +7 -2
- package/plugin/scripts/project-store-resolver.mjs +10 -7
- package/plugin/scripts/project-transition-hook.mjs +20 -12
- package/plugin/scripts/session-snapshot-hook.mjs +20 -9
- package/plugin/scripts/session-start-budget.mjs +18 -0
- package/plugin/scripts/session-start-core.mjs +32 -6
- package/plugin/scripts/session-start-trace.mjs +2 -1
- package/plugin/scripts/turn-outcome-capture.mjs +7 -5
- package/plugin/scripts/unprompted-runtime.mjs +36 -10
- package/plugin/scripts/user-settings.mjs +7 -1
- package/scripts/doc-currency.mjs +6 -990
- package/scripts/hook-qualify-core.mjs +1 -1
- package/scripts/model-managed-prompt.mjs +1 -1
- package/scripts/model-managed-workflow-service.mjs +113 -14
- package/scripts/model-routing-controller.mjs +112 -21
- package/scripts/practical-rule-selector.mjs +51 -0
- package/scripts/product-integrity-contract.mjs +11 -4
- package/scripts/source-scope-receipt.mjs +9 -4
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
import fs from 'node:fs';
|
|
42
42
|
import os from 'node:os';
|
|
43
43
|
import path from 'node:path';
|
|
44
|
-
import { spawn } from 'node:child_process';
|
|
44
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
45
45
|
import { fileURLToPath } from 'node:url';
|
|
46
46
|
|
|
47
47
|
const SELF = fileURLToPath(import.meta.url);
|
|
@@ -73,7 +73,8 @@ function receipt(row) {
|
|
|
73
73
|
const p = receiptPath();
|
|
74
74
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
75
75
|
fs.appendFileSync(p, `${JSON.stringify({ ts: new Date().toISOString(), ...row })}\n`);
|
|
76
|
-
|
|
76
|
+
return true;
|
|
77
|
+
} catch { return false; }
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/** Open the job's log, or /dev/null. Never throws — an unwritable log is not a reason to skip work. */
|
|
@@ -149,6 +150,8 @@ if (!SUPERVISOR) {
|
|
|
149
150
|
// ── SUPERVISOR HALF (already outside the session's process group). Runs the real job, holds the
|
|
150
151
|
// clock, and is the only thing that can end it early.
|
|
151
152
|
const out = openLog();
|
|
153
|
+
// Keep an unresolved retirement requirement if any later receipt cannot be written.
|
|
154
|
+
if (!receipt({ state: 'launching', supervisorPid: process.pid, ttlSec, cmd, retirementRequired: true })) process.exit(0);
|
|
152
155
|
const job = spawn(cmd[0], cmd.slice(1), {
|
|
153
156
|
detached: true, // its own group again, so the TTL kill reaches ITS children too (npm, git, node)
|
|
154
157
|
stdio: ['ignore', out, out],
|
|
@@ -157,26 +160,63 @@ const job = spawn(cmd[0], cmd.slice(1), {
|
|
|
157
160
|
});
|
|
158
161
|
|
|
159
162
|
let killedAtTtl = false;
|
|
160
|
-
|
|
163
|
+
let finished = false;
|
|
164
|
+
let grace;
|
|
165
|
+
const probe = (group = process.platform !== 'win32') => {
|
|
166
|
+
if (!job.pid) return { state: 'unknown', error: 'missing-job-pid' };
|
|
167
|
+
try { process.kill(group ? -job.pid : job.pid, 0); return { state: 'alive' }; }
|
|
168
|
+
catch (error) { return error.code === 'ESRCH' ? { state: 'gone' } : { state: 'unknown', error: error.code || 'probe-failed' }; }
|
|
169
|
+
};
|
|
170
|
+
const finish = (state, detail = {}) => {
|
|
171
|
+
if (finished) return;
|
|
172
|
+
finished = true; clearTimeout(deadline); clearTimeout(grace);
|
|
173
|
+
// Group absence cannot certify descendants that escaped into another group. Keep that fence.
|
|
174
|
+
receipt({ state, pid: job.pid ?? null, supervisorPid: process.pid, ttlSec, cmd,
|
|
175
|
+
retirementRequired: true, retirementConfirmed: false, retirementState: 'UNKNOWN',
|
|
176
|
+
scope: process.platform === 'win32' ? 'windows-taskkill-tree-attempt' : 'posix-process-group', ...detail });
|
|
177
|
+
process.exit(0);
|
|
178
|
+
};
|
|
179
|
+
receipt({ state: 'started', pid: job.pid ?? null, supervisorPid: process.pid, ttlSec, cmd, retirementRequired: true });
|
|
161
180
|
|
|
162
181
|
const deadline = setTimeout(() => {
|
|
163
182
|
killedAtTtl = true;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
if (process.platform === 'win32') {
|
|
184
|
+
if (job.exitCode !== null || job.signalCode !== null) return finish('retirement-unknown', { reason: 'parent-exited; descendant identity unavailable', rootObservation: probe(false) });
|
|
185
|
+
const killed = spawnSync('taskkill', ['/PID', String(job.pid), '/T', '/F'], {
|
|
186
|
+
timeout: 1000, windowsHide: true, encoding: 'utf8', maxBuffer: 16 * 1024 });
|
|
187
|
+
return finish('retirement-unknown', { reason: 'complete Windows descendant retirement not observed',
|
|
188
|
+
taskkill: { status: killed.status, error: killed.error?.code || null }, rootObservation: probe(false) });
|
|
189
|
+
}
|
|
190
|
+
const errors = [];
|
|
191
|
+
const signal = name => {
|
|
192
|
+
try { process.kill(-job.pid, name); }
|
|
193
|
+
catch (error) { if (error.code !== 'ESRCH') errors.push({ signal: name, error: error.code || 'kill-failed' }); }
|
|
194
|
+
};
|
|
195
|
+
signal('SIGTERM');
|
|
196
|
+
grace = setTimeout(() => {
|
|
197
|
+
if (probe().state !== 'gone') signal('SIGKILL');
|
|
198
|
+
// Ref'd timers retain supervision after the direct parent exits; observe native reaping too.
|
|
199
|
+
grace = setTimeout(() => {
|
|
200
|
+
const groupObservation = probe();
|
|
201
|
+
finish(groupObservation.state === 'gone' && !errors.length ? 'group-retired-at-ttl' : 'retirement-unknown',
|
|
202
|
+
{ groupObservation, groupRetirementConfirmed: groupObservation.state === 'gone', killErrors: errors,
|
|
203
|
+
reason: 'escaped or unobserved descendants are not certified' });
|
|
204
|
+
}, 100);
|
|
205
|
+
}, GRACE_MS);
|
|
170
206
|
}, ttlSec * 1000);
|
|
171
207
|
|
|
172
208
|
job.on('error', (e) => {
|
|
173
209
|
clearTimeout(deadline);
|
|
174
|
-
receipt({ state: 'spawn-failed', pid: null, ttlSec, cmd, detail: e.message });
|
|
210
|
+
receipt({ state: 'spawn-failed', pid: null, ttlSec, cmd, detail: e.message, retirementRequired: false, launched: false });
|
|
175
211
|
process.exit(0);
|
|
176
212
|
});
|
|
177
213
|
job.on('exit', (code, signal) => {
|
|
178
|
-
clearTimeout(deadline);
|
|
179
214
|
if (killedAtTtl) return; // the TTL path writes its own, more specific receipt
|
|
180
|
-
receipt({ state: 'exited', pid: job.pid ?? null, code, signal, cmd });
|
|
181
|
-
process.exit(
|
|
215
|
+
receipt({ state: 'parent-exited', pid: job.pid ?? null, code, signal, cmd, retirementRequired: true, retirementConfirmed: false });
|
|
216
|
+
if (process.platform === 'win32') return finish('retirement-unknown', { reason: 'parent exit does not establish descendant retirement', rootObservation: probe(false) });
|
|
217
|
+
const groupObservation = probe();
|
|
218
|
+
if (groupObservation.state === 'gone') finish('group-retired', { code, signal, groupObservation, groupRetirementConfirmed: true,
|
|
219
|
+
reason: 'escaped or unobserved descendants are not certified' });
|
|
220
|
+
else if (groupObservation.state === 'unknown') finish('retirement-unknown', { code, signal, groupObservation });
|
|
221
|
+
// A surviving owned group stays supervised until the original TTL.
|
|
182
222
|
});
|