ruvnet-brain 4.0.1 → 4.0.4
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/.claude-plugin/marketplace.json +1 -0
- package/README.md +4 -4
- package/bin/install.mjs +303 -24
- package/console/CONTRACT.md +172 -0
- package/console/activity.js +753 -0
- package/console/app.js +4189 -0
- package/console/architecture.html +1221 -0
- package/console/assets/depth-1.webp +0 -0
- package/console/assets/depth-2.webp +0 -0
- package/console/assets/depth-3.webp +0 -0
- package/console/assets/harness-vs-plain.svg +259 -0
- package/console/assets/hero.webp +0 -0
- package/console/assets/memory.webp +0 -0
- package/console/assets/metaharness.svg +247 -0
- package/console/index.html +777 -0
- package/console/install-architecture.html +162 -0
- package/console/install-mockup.html +543 -0
- package/console/style.css +2144 -0
- package/console/tips.css +926 -0
- package/console/tips.html +858 -0
- package/console/tips.js +128 -0
- package/docs/RELEASE-NOTES-4.0.md +88 -0
- package/kb/model-requirements.mjs +37 -6
- package/keys/ruvnet-brain-signing.pub.pem +3 -0
- package/package.json +8 -22
- package/plugin/.claude-plugin/marketplace.json +1 -0
- package/plugin/.claude-plugin/plugin.json +2 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/commands/brain-console.md +2 -2
- package/plugin/commands/configure.md +3 -2
- package/plugin/commands/rvbc.md +4 -3
- package/plugin/commands/rvcb.md +2 -2
- package/plugin/commands/whats-new.md +6 -6
- package/plugin/docs/RELEASE-NOTES-4.0.md +88 -0
- package/plugin/hooks/hooks.json +1 -2
- package/plugin/mcp/managed-cli-interface.mjs +47 -4
- package/plugin/mcp/server.mjs +90 -32
- package/plugin/scripts/detach.mjs +14 -0
- package/plugin/scripts/first-session-worker.mjs +38 -0
- package/plugin/scripts/ground-ruvnet.sh +16 -6
- package/plugin/scripts/hook-shim.mjs +34 -29
- package/plugin/scripts/learn-capture.sh +22 -3
- package/plugin/scripts/learn-flush.mjs +21 -4
- package/plugin/scripts/runtime-preferences.mjs +269 -0
- package/plugin/scripts/session-start-core.mjs +503 -0
- package/plugin/scripts/session-start.sh +3 -858
- package/plugin/scripts/whats-new.mjs +42 -0
- package/plugin/skills/brain-console/SKILL.md +4 -2
- package/plugin/skills/release-proof/SKILL.md +98 -0
- package/plugin/skills/release-proof/agents/openai.yaml +4 -0
- package/plugin/skills/release-proof/references/receipt-contract.md +44 -0
- package/plugin/skills/release-proof/scripts/release-proof.mjs +286 -0
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +5 -1
- package/plugin/skills/ruvnet-brain/SKILL.md +22 -7
- package/plugin/skills/rvbc/SKILL.md +9 -6
- package/plugin/skills/whats-new/SKILL.md +4 -4
- package/scripts/adr-backfill.mjs +107 -0
- package/scripts/advocacy-outcomes.mjs +808 -0
- package/scripts/agentdb-context.mjs +216 -0
- package/scripts/agentdb-fleet-doctor.mjs +101 -0
- package/scripts/ascii-drift.mjs +236 -0
- package/scripts/behavioral-l1-l4.mjs +210 -0
- package/scripts/brain-capability-check.mjs +72 -0
- package/scripts/brain-grade-groundtruth.mjs +100 -0
- package/scripts/brain-latency-50.mjs +227 -0
- package/scripts/brain-novice-50.mjs +189 -0
- package/scripts/brain-stamp.mjs +94 -0
- package/scripts/brain-state.mjs +212 -0
- package/scripts/build-bundle.mjs +531 -0
- package/scripts/build-concepts.mjs +132 -0
- package/scripts/build-l2.mjs +71 -0
- package/scripts/build-primer.mjs +73 -0
- package/scripts/build-symbols.mjs +68 -0
- package/scripts/calibrate-router.mjs +97 -0
- package/scripts/capability-audit.mjs +321 -0
- package/scripts/capability-registry.mjs +876 -0
- package/scripts/check-indexation.mjs +108 -0
- package/scripts/check-legibility.mjs +189 -0
- package/scripts/ci/build-fixture-kb.mjs +67 -0
- package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
- package/scripts/ci/learning-replay-recorder.mjs +59 -0
- package/scripts/ci/mutate-hook-timeout.mjs +70 -0
- package/scripts/ci/stranger-fixture-stage.mjs +17 -0
- package/scripts/ci/stranger-scenario.mjs +228 -0
- package/scripts/ci/stranger-timeout.mjs +25 -0
- package/scripts/ci-verdict.mjs +29 -0
- package/scripts/claims-verify.mjs +710 -0
- package/scripts/clear-claude-tmp.sh +31 -0
- package/scripts/console-engine.mjs +434 -0
- package/scripts/console-engine.test.mjs +125 -0
- package/scripts/corpus-qa.mjs +250 -0
- package/scripts/correction-detect-embed.mjs +346 -0
- package/scripts/correction-detect-measure.mjs +270 -0
- package/scripts/correction-detect.mjs +686 -0
- package/scripts/count-chunks.mjs +54 -0
- package/scripts/described-questions.json +30 -0
- package/scripts/design-grade.mjs +58 -0
- package/scripts/dev-plugin-link.sh +105 -0
- package/scripts/distill-project.mjs +200 -0
- package/scripts/doc-currency.mjs +801 -0
- package/scripts/eval-brain.mjs +244 -0
- package/scripts/fix-metaharness-memretrieve.mjs +121 -0
- package/scripts/fix-workstream.mjs +291 -0
- package/scripts/full-hints.mjs +87 -0
- package/scripts/gate.sh +39 -0
- package/scripts/gates.mjs +146 -0
- package/scripts/gen-console-images.mjs +54 -0
- package/scripts/gen-images.mjs +47 -0
- package/scripts/git-clone-refresh.mjs +52 -0
- package/scripts/git-hooks/pre-push +126 -0
- package/scripts/goal-match.mjs +398 -0
- package/scripts/goldie-research.mjs +223 -0
- package/scripts/goldie-weekly.sh +67 -0
- package/scripts/health-repair.mjs +237 -0
- package/scripts/helix-scenario-questions.json +10 -0
- package/scripts/ingest-gists.mjs +230 -0
- package/scripts/ingest-meeting.mjs +115 -0
- package/scripts/ingest-repo.mjs +79 -0
- package/scripts/install-npx-witness.sh +49 -0
- package/scripts/issue-fix.mjs +558 -0
- package/scripts/issue-watch.mjs +276 -0
- package/scripts/issue4-close-note.md +31 -0
- package/scripts/key-canary.mjs +91 -0
- package/scripts/latency-to-surface.mjs +233 -0
- package/scripts/learning-enable.mjs +380 -0
- package/scripts/learning-replay.mjs +1570 -0
- package/scripts/learnings.mjs +62 -0
- package/scripts/lesson-gate.mjs +680 -0
- package/scripts/lesson-lifecycle.mjs +449 -0
- package/scripts/lesson-promote.mjs +262 -0
- package/scripts/lesson-ratify.mjs +98 -0
- package/scripts/lesson-seed.mjs +252 -0
- package/scripts/lesson-store.mjs +447 -0
- package/scripts/loop-checkpoint.mjs +86 -0
- package/scripts/memdb-health.sh +14 -0
- package/scripts/memory-doctor.mjs +326 -0
- package/scripts/model-catalog.mjs +79 -0
- package/scripts/nightly-controller.mjs +66 -0
- package/scripts/nightly-gists.sh +72 -0
- package/scripts/nightly-wrapper.sh +172 -0
- package/scripts/notify.sh +12 -0
- package/scripts/npx-witness.sh +56 -0
- package/scripts/onboarding-console.mjs +2922 -0
- package/scripts/private-fence.mjs +69 -0
- package/scripts/proactivity-metrics.mjs +118 -0
- package/scripts/proof-questions.json +56 -0
- package/scripts/protected-release-invocation.mjs +76 -0
- package/scripts/prove.mjs +95 -0
- package/scripts/proxy/claude-proxied.sh +57 -0
- package/scripts/proxy/proxy-revert.sh +59 -0
- package/scripts/proxy/proxy-up.sh +60 -0
- package/scripts/proxy/proxy-verify.mjs +142 -0
- package/scripts/publication-receipt.mjs +307 -0
- package/scripts/published-surface-probe.mjs +241 -0
- package/scripts/qe/card-lane-gate.mjs +162 -0
- package/scripts/qe/session-start-gate.mjs +229 -0
- package/scripts/qe/ux-suite.mjs +323 -0
- package/scripts/reconcile-project.mjs +0 -0
- package/scripts/record-lesson.mjs +113 -0
- package/scripts/refresh-model-catalog.mjs +99 -0
- package/scripts/release-authority.mjs +93 -0
- package/scripts/release-proof.mjs +9 -0
- package/scripts/release-vector.mjs +281 -0
- package/scripts/release.mjs +439 -0
- package/scripts/remedy-registry.mjs +247 -0
- package/scripts/rerank-cap-eval.mjs +265 -0
- package/scripts/rerank-cap-warm-ab.mjs +129 -0
- package/scripts/route-cheap.mjs +20 -15
- package/scripts/router-utilization.mjs +182 -0
- package/scripts/routing-flywheel.mjs +596 -0
- package/scripts/rvf-generation.mjs +104 -0
- package/scripts/rvf-index-audit.mjs +138 -0
- package/scripts/self-update.mjs +296 -0
- package/scripts/selfcheck.mjs +7 -1
- package/scripts/sign-bundle.mjs +69 -0
- package/scripts/signal-watch.mjs +171 -0
- package/scripts/stabilization-receipt.mjs +108 -0
- package/scripts/stack-sync.mjs +469 -0
- package/scripts/stamp-existing-rvf-generations.mjs +53 -0
- package/scripts/stamp-sweep.mjs +144 -0
- package/scripts/status-honesty.mjs +102 -0
- package/scripts/sync-version.mjs +217 -0
- package/scripts/token-report.mjs +102 -0
- package/scripts/top100-benchmark.mjs +479 -0
- package/scripts/top100-corpus.mjs +112 -0
- package/scripts/top100-semantic-assertions.mjs +449 -0
- package/scripts/update-apply.mjs +9 -0
- package/scripts/upgrade-notice.mjs +14 -0
- package/scripts/verify-bundle.mjs +51 -0
- package/scripts/verify-channels.mjs +184 -0
- package/scripts/verify-model-catalog.mjs +104 -0
- package/scripts/verify-nightly-close-issue4.sh +31 -0
- package/scripts/version.mjs +40 -0
- package/scripts/wired-check.mjs +867 -0
- package/plugin/scripts/finalize-token-meter.mjs +0 -25
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Installed What's New boundary. The executable, manifest and curated notes live in the same
|
|
3
|
+
// immutable plugin payload, so a Stable Spine generation can never report another version's notes.
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
+
const notesPath = path.join(pluginRoot, 'docs', 'RELEASE-NOTES-4.0.md');
|
|
11
|
+
const manifestCandidates = [
|
|
12
|
+
path.join(pluginRoot, '.codex-plugin', 'plugin.json'),
|
|
13
|
+
path.join(pluginRoot, '.claude-plugin', 'plugin.json'),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function fail(message) {
|
|
17
|
+
process.stderr.write(`RuvNet Brain What's New failed: ${message}\n`);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let manifest = null;
|
|
22
|
+
for (const candidate of manifestCandidates) {
|
|
23
|
+
try {
|
|
24
|
+
manifest = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
25
|
+
if (manifest?.version) break;
|
|
26
|
+
} catch { /* try the other installed host manifest */ }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!manifest?.version) {
|
|
30
|
+
fail(`installed version metadata is missing from ${pluginRoot}`);
|
|
31
|
+
} else if (!fs.existsSync(notesPath)) {
|
|
32
|
+
fail(`installed release notes are missing at ${notesPath}`);
|
|
33
|
+
} else {
|
|
34
|
+
let notes = '';
|
|
35
|
+
try { notes = fs.readFileSync(notesPath, 'utf8'); }
|
|
36
|
+
catch (error) { fail(`installed release notes could not be read at ${notesPath}: ${error.message}`); }
|
|
37
|
+
if (notes) {
|
|
38
|
+
process.stdout.write(`RuvNet Brain ${manifest.version}\n\n`);
|
|
39
|
+
process.stdout.write(notes);
|
|
40
|
+
if (!notes.endsWith('\n')) process.stdout.write('\n');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -10,8 +10,10 @@ Treat `/rvbc`, `/rvcb`, `/brain-console`, and `/ruvnet-brain:configure` as equal
|
|
|
10
10
|
Never correct the user's spelling.
|
|
11
11
|
|
|
12
12
|
1. Say one short sentence: "Opening it now; it scans live while you watch."
|
|
13
|
-
2.
|
|
14
|
-
|
|
13
|
+
2. Resolve the installed runtime at
|
|
14
|
+
`${RUVNET_BRAIN_KB:-$HOME/.cache/ruvnet-brain/kb}/.console-runtime/scripts/onboarding-console.mjs`.
|
|
15
|
+
A current-repository `scripts/onboarding-console.mjs` is allowed only for an explicit developer
|
|
16
|
+
checkout. Never fall back to a guessed `~/Code` path.
|
|
15
17
|
3. Run `node <resolved-script> --serve --open` in the background.
|
|
16
18
|
4. Give the URL immediately. Do not promise a duration; the page reports its own scan progress.
|
|
17
19
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-proof
|
|
3
|
+
description: Fail-closed exact-artifact release and deployment authority. Use before saying a release is ready, pushing a release commit, publishing npm packages, creating GitHub releases, deploying production, closing release-blocking issues, or claiming all gates are green. Requires clean immutable lineage, zero open issues, exact-SHA GitHub success, nonzero no-skip QE, packed-artifact host tests, installed Brain/RVF proof, independent graders, and post-publication byte verification.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release Proof
|
|
7
|
+
|
|
8
|
+
Treat release as a two-seal transaction. Never publish from a source checkout merely because its
|
|
9
|
+
tests pass. Never turn `UNKNOWN`, `SKIP`, `todo`, `0 tests`, dirty state, or an agent report into
|
|
10
|
+
green.
|
|
11
|
+
|
|
12
|
+
## Non-bypassable rules
|
|
13
|
+
|
|
14
|
+
1. Use the exact source SHA and one packed-artifact SHA-256 everywhere.
|
|
15
|
+
2. Require zero open GitHub issues for RuvNet Brain. A local fix is not a closed issue.
|
|
16
|
+
3. Require every named GitHub workflow to complete successfully on the exact candidate SHA.
|
|
17
|
+
4. Reject any test/QE result with zero tests, skips, todos, unknowns, pending jobs, or failures.
|
|
18
|
+
5. Require two distinct independent graders scoring at least 95, each bound to the SHA and digest.
|
|
19
|
+
6. Install the sealed artifact into virgin Claude Code and Codex homes; test their real entrypoints.
|
|
20
|
+
7. Require the source package, Claude manifest, Codex manifest, packed npm version, bundle
|
|
21
|
+
`brainVersion`/`releaseTag`, and both installed host versions to identify one exact generation.
|
|
22
|
+
8. Require the active Brain registry to contain the `ruvnet-brain` RVF store and require narrow,
|
|
23
|
+
broad, and concurrent cited searches to complete within 80% of their deadline.
|
|
24
|
+
9. Publish only through the protected release workflow. Never run `npm publish` or `gh release
|
|
25
|
+
create` locally.
|
|
26
|
+
10. After publication, download npm and GitHub artifacts, compare their bytes with the seal, install
|
|
27
|
+
both hosts again, query the active MCP again, and require `published-surface-probe` green.
|
|
28
|
+
11. Close an issue only after posting its acceptance evidence. Never close from source inspection.
|
|
29
|
+
|
|
30
|
+
## Candidate seal
|
|
31
|
+
|
|
32
|
+
Generate the receipt from commands in the protected candidate workflow. Do not hand-author it.
|
|
33
|
+
For generation 4.0.4, dispatch `.github/workflows/protected-release.yml` only with the full candidate
|
|
34
|
+
SHA, sealed artifact SHA-256, exact version `4.0.4`, and the successful exact-SHA CI run ID whose
|
|
35
|
+
named `release-qe` job produced `release-evidence-<sha>`. The workflow checks every binding before
|
|
36
|
+
creating its sealed handoff and again after the production reviewer approves. Missing artifacts,
|
|
37
|
+
pending/red jobs, malformed inputs, version splits, and byte mismatches stop before the publisher.
|
|
38
|
+
Validate it from the repository with:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node scripts/release-proof.mjs --candidate release-evidence/candidate-receipt.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
From an installed Claude plugin, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node "$CLAUDE_PLUGIN_ROOT/skills/release-proof/scripts/release-proof.mjs" \
|
|
48
|
+
--candidate release-evidence/candidate-receipt.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Exit 0 is the only candidate seal. Read every failure code; repair the system, regenerate evidence,
|
|
52
|
+
and rerun. Do not edit the receipt to remove a failure.
|
|
53
|
+
|
|
54
|
+
## Publication seal
|
|
55
|
+
|
|
56
|
+
After the protected publisher completes, validate both receipts:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node scripts/release-proof.mjs \
|
|
60
|
+
--candidate release-evidence/candidate-receipt.json \
|
|
61
|
+
--publication release-evidence/publication-receipt.json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Only exit 0 permits “shipped,” “deployed,” “green,” or “ready.” If publication occurred but this
|
|
65
|
+
seal fails, say `PUBLICATION DEGRADED`, preserve the previous known-good release, and repair or
|
|
66
|
+
roll back through the release workflow.
|
|
67
|
+
|
|
68
|
+
`scripts/release.mjs --publish` is intentionally unusable from a local shell or another workflow.
|
|
69
|
+
Its invocation guard requires GitHub Actions workflow `protected-release`, the candidate receipt,
|
|
70
|
+
and matching SHA/digest/version bindings before any push, tag, release, or npm action. The workflow
|
|
71
|
+
then runs `scripts/publication-receipt.mjs` after channel verification. That producer independently
|
|
72
|
+
downloads the sealed package from npm and the GitHub Release, requires both copies to match the
|
|
73
|
+
candidate bytes and identity, installs the npm copy into virgin Claude and Codex homes, proves the
|
|
74
|
+
installed self-RVF/readiness/search deadline, and runs `published-surface-probe` on the candidate
|
|
75
|
+
SHA. It refuses to overwrite an existing receipt. The workflow uploads both append-only receipts
|
|
76
|
+
only after the two-receipt validator exits 0; missing evidence is red, never inferred.
|
|
77
|
+
|
|
78
|
+
## Evidence and issue handling
|
|
79
|
+
|
|
80
|
+
For each issue:
|
|
81
|
+
|
|
82
|
+
1. Reproduce the original symptom against the old/public artifact.
|
|
83
|
+
2. Run its acceptance criteria against the sealed candidate.
|
|
84
|
+
3. Disable or mutate the fix; the regression must fail.
|
|
85
|
+
4. Post SHA, artifact digest, commands, results, and untested limits to the issue.
|
|
86
|
+
5. Close only after the GitHub evidence is visible and exact-SHA required checks are green.
|
|
87
|
+
|
|
88
|
+
Read [references/receipt-contract.md](references/receipt-contract.md) for receipt fields and failure
|
|
89
|
+
semantics. Store the protocol and final release receipt in Ruflo/AgentDB only after the publication
|
|
90
|
+
seal passes.
|
|
91
|
+
|
|
92
|
+
## Status language
|
|
93
|
+
|
|
94
|
+
- Candidate seal absent or failed: `NOT READY`.
|
|
95
|
+
- Candidate sealed, not published: `SEALED, NOT SHIPPED`.
|
|
96
|
+
- Published, publication seal pending: `PUBLISHED, NOT VERIFIED`.
|
|
97
|
+
- Publication seal failed: `PUBLICATION DEGRADED`.
|
|
98
|
+
- Both seals exit 0: `SHIPPED AND VERIFIED`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Receipt contract
|
|
2
|
+
|
|
3
|
+
The authority accepts schema version 1 JSON. Receipts are append-only evidence artifacts generated
|
|
4
|
+
by protected workflows, never editable status documents.
|
|
5
|
+
|
|
6
|
+
## Candidate receipt
|
|
7
|
+
|
|
8
|
+
Required bindings:
|
|
9
|
+
|
|
10
|
+
- `sha`, `tree`, `dirty:false`
|
|
11
|
+
- `version`, `tag`, and exact-equal `sourceVersions.package`, `sourceVersions.claudePlugin`, and
|
|
12
|
+
`sourceVersions.codexPlugin`
|
|
13
|
+
- `artifact.path`, `artifact.sha256`, `artifact.sourceSha`
|
|
14
|
+
- exact-equal `artifact.version`, `artifact.bundle.brainVersion`, and
|
|
15
|
+
`artifact.bundle.releaseTag`
|
|
16
|
+
- exact-SHA release-vector verdict with zero unknown/skipped
|
|
17
|
+
- aggregate tests with nonzero total, all passed, zero failed/skipped/todo
|
|
18
|
+
- fresh coverage floor and zero critical/high security findings
|
|
19
|
+
- zero open GitHub issues
|
|
20
|
+
- required GitHub workflow results on the same SHA
|
|
21
|
+
- virgin-home Claude and Codex results on the same artifact digest and exact candidate version
|
|
22
|
+
- installed Brain self-RVF plus narrow, broad, and concurrent cited search timings
|
|
23
|
+
- nonzero Agentic QE totals with zero failed/skipped
|
|
24
|
+
- two distinct independent grader receipts at 95 or higher, bound to SHA and digest
|
|
25
|
+
|
|
26
|
+
## Publication receipt
|
|
27
|
+
|
|
28
|
+
Required bindings:
|
|
29
|
+
|
|
30
|
+
- candidate SHA and artifact digest
|
|
31
|
+
- candidate `version` plus exact-equal npm version, GitHub tag, bundle `brainVersion`/`releaseTag`,
|
|
32
|
+
and installed Claude/Codex versions
|
|
33
|
+
- npm and GitHub release bytes matching the candidate digest
|
|
34
|
+
- clean installed Claude and Codex results from the public package
|
|
35
|
+
- installed Brain self-RVF and broad search within 80 percent of deadline
|
|
36
|
+
- successful exact-SHA `published-surface-probe`
|
|
37
|
+
|
|
38
|
+
## Failure semantics
|
|
39
|
+
|
|
40
|
+
Any missing field, split version identity, malformed digest, mismatched SHA, dirty tree, open issue, absent/pending/red
|
|
41
|
+
workflow, skipped/todo/zero-test result, missing RVF store, uncited search, deadline-margin breach,
|
|
42
|
+
low/missing grader, or public byte mismatch is `FAIL`. There is no warning state and no score
|
|
43
|
+
average. The authority never publishes; publication belongs to the protected workflow after the
|
|
44
|
+
candidate seal.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Fail-closed release authority. It validates evidence bound to one clean source SHA and one
|
|
3
|
+
// packed-artifact digest. It never publishes and never converts UNKNOWN/SKIP/zero work into PASS.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
|
|
9
|
+
export const REQUIRED_CHECKS = ['ci', 'integration-linux', 'stranger-matrix', 'ux-qe', 'release-qe'];
|
|
10
|
+
|
|
11
|
+
const fail = (code, detail) => ({ code, detail });
|
|
12
|
+
const cleanHex = (value, length) => new RegExp(`^[a-f0-9]{${length}}$`, 'i').test(String(value || ''));
|
|
13
|
+
const digestOf = (receipt) => String(receipt?.artifact?.sha256 || '').replace(/^sha256:/, '');
|
|
14
|
+
const versionField = (value) => typeof value === 'string' && value.length > 0 ? value : null;
|
|
15
|
+
|
|
16
|
+
function versionIdentityFailures(expectedVersion, expectedTag, fields) {
|
|
17
|
+
const mismatches = [];
|
|
18
|
+
if (!versionField(expectedVersion)) mismatches.push('version is missing');
|
|
19
|
+
if (expectedTag !== `v${expectedVersion}`) mismatches.push(`tag=${expectedTag ?? 'missing'}`);
|
|
20
|
+
for (const [name, value, expected = expectedVersion] of fields) {
|
|
21
|
+
if (value !== expected) mismatches.push(`${name}=${value ?? 'missing'}`);
|
|
22
|
+
}
|
|
23
|
+
return mismatches;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function evaluateStabilizationCandidateReceipt(receipt) {
|
|
27
|
+
const failures = [];
|
|
28
|
+
const sha = String(receipt?.sha || '');
|
|
29
|
+
const digest = digestOf(receipt);
|
|
30
|
+
if (receipt?.schemaVersion !== 1 || receipt?.phase !== 'stabilization-candidate' || receipt?.mode !== 'stabilization') {
|
|
31
|
+
failures.push(fail('INVALID_STABILIZATION_RECEIPT', 'schemaVersion=1, phase=stabilization-candidate, and mode=stabilization are required'));
|
|
32
|
+
}
|
|
33
|
+
if (receipt?.targetScore !== 95 || receipt?.scoreClaimed !== false) {
|
|
34
|
+
failures.push(fail('STABILIZATION_SCORE_MISREPRESENTED', 'stabilization must preserve the 95 target and explicitly make no 95 claim'));
|
|
35
|
+
}
|
|
36
|
+
if (!cleanHex(sha, 40) || !cleanHex(receipt?.tree, 40) || receipt?.dirty !== false) {
|
|
37
|
+
failures.push(fail('INVALID_LINEAGE', 'clean candidate SHA and tree are required'));
|
|
38
|
+
}
|
|
39
|
+
if (!cleanHex(digest, 64) || receipt?.artifact?.sourceSha !== sha) {
|
|
40
|
+
failures.push(fail('INVALID_ARTIFACT_DIGEST', 'sealed package digest must be bound to the candidate SHA'));
|
|
41
|
+
}
|
|
42
|
+
const version = versionField(receipt?.version);
|
|
43
|
+
const identityMismatches = versionIdentityFailures(version, receipt?.tag, [
|
|
44
|
+
['source package', receipt?.sourceVersions?.package],
|
|
45
|
+
['source Claude plugin', receipt?.sourceVersions?.claudePlugin],
|
|
46
|
+
['source Codex plugin', receipt?.sourceVersions?.codexPlugin],
|
|
47
|
+
['packed npm artifact', receipt?.artifact?.version],
|
|
48
|
+
]);
|
|
49
|
+
if (identityMismatches.length) failures.push(fail('VERSION_IDENTITY_MISMATCH', identityMismatches.join(', ')));
|
|
50
|
+
if (receipt?.security?.status !== 'PASS' || receipt?.security?.critical !== 0 || receipt?.security?.high !== 0) {
|
|
51
|
+
failures.push(fail('SECURITY_NOT_PASS', 'stabilization requires zero critical/high dependency findings'));
|
|
52
|
+
}
|
|
53
|
+
if (!(receipt?.qe?.total > 0) || receipt?.qe?.status !== 'PASS' || receipt?.qe?.failed !== 0
|
|
54
|
+
|| receipt?.qe?.skipped !== 0 || receipt?.qe?.passed !== receipt?.qe?.total) {
|
|
55
|
+
failures.push(fail('QE_NOT_PASS', 'release QE must execute nonzero work with zero failed/skipped tests'));
|
|
56
|
+
}
|
|
57
|
+
return { verdict: failures.length === 0 ? 'PASS' : 'FAIL', sha, artifactSha256: digest, failures };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function evaluateCandidateReceipt(receipt) {
|
|
61
|
+
if (receipt?.phase === 'stabilization-candidate') return evaluateStabilizationCandidateReceipt(receipt);
|
|
62
|
+
const failures = [];
|
|
63
|
+
const sha = String(receipt?.sha || '');
|
|
64
|
+
const digest = digestOf(receipt);
|
|
65
|
+
|
|
66
|
+
if (receipt?.schemaVersion !== 1 || receipt?.phase !== 'candidate') failures.push(fail('INVALID_RECEIPT', 'schemaVersion=1 and phase=candidate are required'));
|
|
67
|
+
if (!cleanHex(sha, 40) || !cleanHex(receipt?.tree, 40)) failures.push(fail('INVALID_LINEAGE', 'candidate SHA and tree must be full git object ids'));
|
|
68
|
+
if (receipt?.dirty !== false) failures.push(fail('DIRTY_WORKTREE', 'release candidates must come from a clean worktree'));
|
|
69
|
+
if (!cleanHex(digest, 64)) failures.push(fail('INVALID_ARTIFACT_DIGEST', 'artifact SHA-256 is missing or malformed'));
|
|
70
|
+
if (receipt?.artifact?.sourceSha !== sha) failures.push(fail('ARTIFACT_SOURCE_MISMATCH', 'packed artifact is not bound to the candidate SHA'));
|
|
71
|
+
|
|
72
|
+
const version = versionField(receipt?.version);
|
|
73
|
+
const tag = version ? `v${version}` : null;
|
|
74
|
+
const identityMismatches = versionIdentityFailures(version, receipt?.tag, [
|
|
75
|
+
['source package', receipt?.sourceVersions?.package],
|
|
76
|
+
['source Claude plugin', receipt?.sourceVersions?.claudePlugin],
|
|
77
|
+
['source Codex plugin', receipt?.sourceVersions?.codexPlugin],
|
|
78
|
+
['packed npm artifact', receipt?.artifact?.version],
|
|
79
|
+
['bundle brainVersion', receipt?.artifact?.bundle?.brainVersion],
|
|
80
|
+
['bundle releaseTag', receipt?.artifact?.bundle?.releaseTag, tag],
|
|
81
|
+
['installed Claude host', receipt?.hosts?.claude?.version],
|
|
82
|
+
['installed Codex host', receipt?.hosts?.codex?.version],
|
|
83
|
+
]);
|
|
84
|
+
if (identityMismatches.length > 0) {
|
|
85
|
+
failures.push(fail('VERSION_IDENTITY_MISMATCH', `candidate surfaces must identify one generation: ${identityMismatches.join(', ')}`));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const vector = receipt?.releaseVector || {};
|
|
89
|
+
if (vector.verdict !== 'PASS' || vector.sha !== sha || vector.unknown !== 0 || vector.skipped !== 0) {
|
|
90
|
+
failures.push(fail('RELEASE_VECTOR_NOT_PASS', 'release vector must PASS on the exact SHA with zero UNKNOWN/SKIP'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const tests = receipt?.tests || {};
|
|
94
|
+
if (!(tests.total > 0)) failures.push(fail('ZERO_TESTS', 'a zero-test run is never evidence'));
|
|
95
|
+
if (tests.failed !== 0 || tests.skipped !== 0 || tests.todo !== 0 || tests.passed !== tests.total) {
|
|
96
|
+
failures.push(fail('TESTS_NOT_ALL_EXECUTED', 'all discovered tests must execute and pass; skipped/todo are release failures'));
|
|
97
|
+
}
|
|
98
|
+
const coverage = receipt?.coverage || {};
|
|
99
|
+
if (coverage.status !== 'PASS' || !(coverage.lines >= coverage.requiredLines)) failures.push(fail('COVERAGE_NOT_PASS', 'fresh exact-SHA coverage must meet the floor'));
|
|
100
|
+
const security = receipt?.security || {};
|
|
101
|
+
if (security.status !== 'PASS' || security.critical !== 0 || security.high !== 0) failures.push(fail('SECURITY_NOT_PASS', 'security must pass with zero critical/high findings'));
|
|
102
|
+
|
|
103
|
+
const openIssues = Array.isArray(receipt?.issues?.open) ? receipt.issues.open : null;
|
|
104
|
+
if (openIssues === null || openIssues.length > 0) failures.push(fail('OPEN_ISSUES', openIssues === null ? 'open-issue evidence missing' : `${openIssues.length} issue(s) remain open`));
|
|
105
|
+
|
|
106
|
+
if (receipt?.github?.sha !== sha) failures.push(fail('GITHUB_SHA_MISMATCH', 'GitHub evidence is not bound to candidate SHA'));
|
|
107
|
+
const checks = Array.isArray(receipt?.github?.checks) ? receipt.github.checks : [];
|
|
108
|
+
for (const name of REQUIRED_CHECKS) {
|
|
109
|
+
const matches = checks.filter((check) => check?.name === name);
|
|
110
|
+
if (matches.length === 0) failures.push(fail('REQUIRED_CHECK_MISSING', `${name} has no exact-SHA result`));
|
|
111
|
+
else if (!matches.some((check) => check.status === 'completed' && check.conclusion === 'success')) failures.push(fail('REQUIRED_CHECK_NOT_GREEN', `${name} is not completed/success`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const hostName of ['claude', 'codex']) {
|
|
115
|
+
const host = receipt?.hosts?.[hostName];
|
|
116
|
+
if (host?.status !== 'PASS') failures.push(fail('HOST_NOT_PASS', `${hostName} clean-install acceptance did not pass`));
|
|
117
|
+
if (host?.artifactSha256 !== digest) failures.push(fail('HOST_ARTIFACT_MISMATCH', `${hostName} did not install the sealed artifact`));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const brain = receipt?.brain || {};
|
|
121
|
+
if (brain.status !== 'PASS') failures.push(fail('BRAIN_NOT_PASS', 'installed MCP search acceptance did not pass'));
|
|
122
|
+
if (brain.selfStore !== true) failures.push(fail('BRAIN_SELF_STORE_MISSING', 'active RVF registry does not contain ruvnet-brain'));
|
|
123
|
+
if (brain.citedSelfSource !== true) failures.push(fail('BRAIN_SELF_CITATION_MISSING', 'installed Brain did not cite its own source'));
|
|
124
|
+
const deadline = Number(brain.deadlineMs);
|
|
125
|
+
const observed = Math.max(Number(brain.narrowMs), Number(brain.broadMs), Number(brain.concurrentMs));
|
|
126
|
+
if (!(deadline > 0) || !Number.isFinite(observed) || observed > deadline * 0.8) failures.push(fail('BRAIN_DEADLINE_MARGIN', 'searches must finish within 80% of deadline'));
|
|
127
|
+
|
|
128
|
+
const qe = receipt?.qe || {};
|
|
129
|
+
if (!(qe.total > 0)) failures.push(fail('QE_ZERO_TESTS', 'Agentic QE executed zero tests'));
|
|
130
|
+
if (qe.status !== 'PASS' || qe.failed !== 0 || qe.skipped !== 0 || qe.passed !== qe.total) failures.push(fail('QE_NOT_PASS', 'Agentic QE must run a nonzero fleet with zero failed/skipped tests'));
|
|
131
|
+
|
|
132
|
+
const graders = Array.isArray(receipt?.graders) ? receipt.graders : [];
|
|
133
|
+
const independent = graders.filter((grader) => grader?.independent === true);
|
|
134
|
+
if (independent.length < 2 || new Set(independent.map((grader) => grader.id)).size < 2) failures.push(fail('INDEPENDENT_GRADERS', 'two distinct independent graders are required'));
|
|
135
|
+
for (const grader of independent) {
|
|
136
|
+
if (!(grader.score >= 95)) failures.push(fail('GRADER_BELOW_95', `${grader.id || 'grader'} scored ${grader.score ?? 'UNKNOWN'}`));
|
|
137
|
+
if (grader.sha !== sha || grader.artifactSha256 !== digest) failures.push(fail('GRADER_BINDING_MISMATCH', `${grader.id || 'grader'} is not bound to the seal`));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { verdict: failures.length === 0 ? 'PASS' : 'FAIL', sha, artifactSha256: digest, failures };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function evaluatePublicationReceipt(candidate, publication) {
|
|
144
|
+
const candidateResult = evaluateCandidateReceipt(candidate);
|
|
145
|
+
const failures = [...candidateResult.failures];
|
|
146
|
+
const sha = candidateResult.sha;
|
|
147
|
+
const digest = candidateResult.artifactSha256;
|
|
148
|
+
if (publication?.schemaVersion !== 1 || publication?.phase !== 'publication') failures.push(fail('INVALID_PUBLICATION_RECEIPT', 'schemaVersion=1 and phase=publication are required'));
|
|
149
|
+
if (publication?.sha !== sha || publication?.artifactSha256 !== digest) failures.push(fail('PUBLICATION_SEAL_MISMATCH', 'publication does not reference candidate seal'));
|
|
150
|
+
const version = versionField(candidate?.version);
|
|
151
|
+
const tag = version ? `v${version}` : null;
|
|
152
|
+
const identityMismatches = versionIdentityFailures(version, candidate?.tag, [
|
|
153
|
+
['publication version', publication?.version],
|
|
154
|
+
['npm version', publication?.npm?.version],
|
|
155
|
+
['GitHub release tag', publication?.githubRelease?.tag, tag],
|
|
156
|
+
['bundle brainVersion', publication?.bundle?.brainVersion],
|
|
157
|
+
['bundle releaseTag', publication?.bundle?.releaseTag, tag],
|
|
158
|
+
['installed Claude host', publication?.installed?.claude?.version],
|
|
159
|
+
['installed Codex host', publication?.installed?.codex?.version],
|
|
160
|
+
]);
|
|
161
|
+
if (identityMismatches.length > 0) {
|
|
162
|
+
failures.push(fail('PUBLIC_VERSION_IDENTITY_MISMATCH', `public surfaces must identify candidate ${version ?? 'UNKNOWN'}: ${identityMismatches.join(', ')}`));
|
|
163
|
+
}
|
|
164
|
+
for (const surface of ['npm', 'githubRelease']) {
|
|
165
|
+
const item = publication?.[surface];
|
|
166
|
+
if (item?.sha !== sha || item?.artifactSha256 !== digest) failures.push(fail('PUBLIC_ARTIFACT_MISMATCH', `${surface} differs from candidate seal`));
|
|
167
|
+
}
|
|
168
|
+
for (const hostName of ['claude', 'codex']) {
|
|
169
|
+
const host = publication?.installed?.[hostName];
|
|
170
|
+
if (host?.status !== 'PASS' || host?.artifactSha256 !== digest) failures.push(fail('PUBLIC_HOST_NOT_PASS', `${hostName} is not running sealed public artifact`));
|
|
171
|
+
}
|
|
172
|
+
const brain = publication?.brain || {};
|
|
173
|
+
if (brain.status !== 'PASS' || brain.selfStore !== true || !(brain.broadMs <= Number(brain.deadlineMs) * 0.8)) failures.push(fail('PUBLIC_BRAIN_NOT_PASS', 'public installed Brain acceptance failed'));
|
|
174
|
+
const probes = Array.isArray(publication?.postPublicationChecks) ? publication.postPublicationChecks : [];
|
|
175
|
+
const probe = probes.find((check) => check?.name === 'published-surface-probe' && check?.sha === sha);
|
|
176
|
+
if (probe?.status !== 'completed' || probe?.conclusion !== 'success') failures.push(fail('POST_PUBLICATION_CHECK_NOT_GREEN', 'published-surface-probe is not green'));
|
|
177
|
+
return { verdict: failures.length === 0 ? 'PASS' : 'FAIL', sha, artifactSha256: digest, failures };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function evaluateLivePreflight(observed) {
|
|
181
|
+
const failures = [];
|
|
182
|
+
if (observed?.dirty !== false) failures.push(fail('DIRTY_WORKTREE', 'local worktree is dirty'));
|
|
183
|
+
if (observed?.localSha !== observed?.remoteSha) failures.push(fail('REMOTE_SHA_MISMATCH', 'local HEAD is not origin/main'));
|
|
184
|
+
if (!Array.isArray(observed?.openIssues) || observed.openIssues.length > 0) failures.push(fail('OPEN_ISSUES', `${observed?.openIssues?.length ?? 'unknown'} issue(s) open`));
|
|
185
|
+
if (!Array.isArray(observed?.failedRuns) || observed.failedRuns.length > 0) failures.push(fail('GITHUB_FAILURES', `${observed?.failedRuns?.length ?? 'unknown'} recent failed run(s)`));
|
|
186
|
+
if (observed?.activeSelfStore !== true) failures.push(fail('BRAIN_SELF_STORE_MISSING', 'installed active registry lacks ruvnet-brain'));
|
|
187
|
+
if (observed?.branchEnforceAdmins !== true) failures.push(fail('ADMIN_BYPASS_ENABLED', 'main protection does not enforce required checks for admins'));
|
|
188
|
+
if (observed?.productionProtected !== true) failures.push(fail('PRODUCTION_ENV_UNPROTECTED', 'production environment has no required reviewer protection'));
|
|
189
|
+
if (observed?.releaseVector !== 'PASS') failures.push(fail('RELEASE_VECTOR_NOT_PASS', `release vector is ${observed?.releaseVector ?? 'UNKNOWN'}`));
|
|
190
|
+
return { verdict: failures.length === 0 ? 'PASS' : 'FAIL', observed, failures };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function latestRunsByWorkflow(runs) {
|
|
194
|
+
const latest = new Map();
|
|
195
|
+
for (const run of Array.isArray(runs) ? runs : []) {
|
|
196
|
+
const prior = latest.get(run.workflowName);
|
|
197
|
+
if (!prior || Number(run.databaseId || 0) > Number(prior.databaseId || 0)) latest.set(run.workflowName, run);
|
|
198
|
+
}
|
|
199
|
+
return [...latest.values()];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function command(cmd, args, options = {}) {
|
|
203
|
+
const result = spawnSync(cmd, args, { encoding: 'utf8', ...options });
|
|
204
|
+
return {
|
|
205
|
+
status: result.status,
|
|
206
|
+
stdout: result.stdout || '',
|
|
207
|
+
stderr: result.stderr || '',
|
|
208
|
+
error: result.error,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function jsonCommand(cmd, args, options = {}) {
|
|
213
|
+
const result = command(cmd, args, options);
|
|
214
|
+
if (result.status !== 0) return null;
|
|
215
|
+
try { return JSON.parse(result.stdout); } catch { return null; }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function collectLivePreflight({ root = process.cwd(), repo = 'stuinfla/ruvnet-brain', includeVector = true } = {}) {
|
|
219
|
+
const git = (args) => command('git', args, { cwd: root });
|
|
220
|
+
const localSha = git(['rev-parse', 'HEAD']).stdout.trim();
|
|
221
|
+
const dirty = Boolean(git(['status', '--porcelain']).stdout.trim());
|
|
222
|
+
git(['fetch', '--quiet', 'origin', 'main']);
|
|
223
|
+
const remoteSha = git(['rev-parse', 'origin/main']).stdout.trim();
|
|
224
|
+
const openIssues = jsonCommand('gh', ['issue', 'list', '--repo', repo, '--state', 'open', '--limit', '100', '--json', 'number,title,url']) ?? null;
|
|
225
|
+
const runs = jsonCommand('gh', ['run', 'list', '--repo', repo, '--limit', '50', '--json', 'databaseId,workflowName,headSha,status,conclusion,url']) ?? null;
|
|
226
|
+
const failedRuns = Array.isArray(runs)
|
|
227
|
+
? latestRunsByWorkflow(runs.filter((run) => run.headSha === remoteSha))
|
|
228
|
+
.filter((run) => run.status !== 'completed' || run.conclusion !== 'success')
|
|
229
|
+
: null;
|
|
230
|
+
const protection = jsonCommand('gh', ['api', `repos/${repo}/branches/main/protection`]);
|
|
231
|
+
const environments = jsonCommand('gh', ['api', `repos/${repo}/environments`]);
|
|
232
|
+
const production = environments?.environments?.find((environment) => environment.name === 'Production – ruvnet-brain');
|
|
233
|
+
let activeSelfStore = false;
|
|
234
|
+
try {
|
|
235
|
+
const source = JSON.parse(fs.readFileSync(path.join(process.env.HOME || '', '.cache/ruvnet-brain/kb/SOURCE.json'), 'utf8'));
|
|
236
|
+
const stores = source.sources || source.repos || {};
|
|
237
|
+
activeSelfStore = Boolean(stores['ruvnet-brain']);
|
|
238
|
+
} catch {}
|
|
239
|
+
let releaseVector = 'UNKNOWN';
|
|
240
|
+
if (includeVector) {
|
|
241
|
+
const vector = jsonCommand(process.execPath, [path.join(root, 'scripts/release-vector.mjs'), '--json'], { cwd: root });
|
|
242
|
+
releaseVector = vector?.verdict || 'UNKNOWN';
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
localSha,
|
|
246
|
+
remoteSha,
|
|
247
|
+
dirty,
|
|
248
|
+
openIssues,
|
|
249
|
+
failedRuns,
|
|
250
|
+
activeSelfStore,
|
|
251
|
+
branchEnforceAdmins: protection?.enforce_admins?.enabled === true,
|
|
252
|
+
productionProtected: Array.isArray(production?.protection_rules) && production.protection_rules.length > 0 && production.can_admins_bypass === false,
|
|
253
|
+
releaseVector,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const readJson = (file) => JSON.parse(fs.readFileSync(path.resolve(file), 'utf8'));
|
|
258
|
+
const argument = (args, flag) => {
|
|
259
|
+
const index = args.indexOf(flag);
|
|
260
|
+
return index >= 0 ? args[index + 1] : null;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
export function main(args = process.argv.slice(2)) {
|
|
264
|
+
if (args.includes('--status')) {
|
|
265
|
+
const result = evaluateLivePreflight(collectLivePreflight({ includeVector: !args.includes('--quick') }));
|
|
266
|
+
console.log(JSON.stringify(result, null, 2));
|
|
267
|
+
return result.verdict === 'PASS' ? 0 : 1;
|
|
268
|
+
}
|
|
269
|
+
const candidatePath = argument(args, '--candidate');
|
|
270
|
+
const publicationPath = argument(args, '--publication');
|
|
271
|
+
if (!candidatePath) {
|
|
272
|
+
console.error('Usage: release-proof.mjs --candidate <candidate-receipt.json> [--publication <publication-receipt.json>]');
|
|
273
|
+
return 2;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
const candidate = readJson(candidatePath);
|
|
277
|
+
const result = publicationPath ? evaluatePublicationReceipt(candidate, readJson(publicationPath)) : evaluateCandidateReceipt(candidate);
|
|
278
|
+
console.log(JSON.stringify(result, null, 2));
|
|
279
|
+
return result.verdict === 'PASS' ? 0 : 1;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error(`release-proof: ${error.message}`);
|
|
282
|
+
return 2;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url) process.exitCode = main();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# THE PLAYBOOK — the standing build playbook, in full
|
|
2
2
|
|
|
3
|
-
Updated: 2026-07-
|
|
3
|
+
Updated: 2026-07-30 | Version 1.0.1
|
|
4
4
|
Created: 2026-07-27
|
|
5
5
|
|
|
6
6
|
**Read this before your first build response in a session.** `plugin/scripts/session-start.sh`
|
|
@@ -41,6 +41,10 @@ the exact lie that makes people distrust rUv's code.
|
|
|
41
41
|
token exchange", not "does RuvNet apply") — the useful hit can be in ANY of the 32 repos, never
|
|
42
42
|
trust memory about what the corpus does or doesn't have.
|
|
43
43
|
- Check project memory (ruflo memory search / AgentDB) for prior decisions on this area.
|
|
44
|
+
- Diagnose memory only through one canonical absolute path: store a unique key with
|
|
45
|
+
`ruflo memory store --path <project>/.swarm/memory.db`, retrieve that exact key with the same
|
|
46
|
+
`--path`, then confirm the exact row through SQLite. A semantic-search miss, DB/WAL mtime,
|
|
47
|
+
daemon startup, or `[OK] Data stored successfully` alone proves neither failure nor success.
|
|
44
48
|
- Invoke Ruflo MCP tools first for capabilities they already expose. For a CLI-only interface,
|
|
45
49
|
use the brain's `ruvnet_cli_help` then `ruvnet_cli_run` tools with literal argv; never guess flags
|
|
46
50
|
by reconstructing a raw shell command.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ruvnet-brain
|
|
3
3
|
description: Use whenever a task involves the RuvNet / rUv ecosystem (Ruflo, RuVector/RVF, AgentDB, RuLake, RuView, agentic-flow, agenticow, SAFLA, QuDAG, DAA, ruv-fann, FACT, SynthLang, SPARC, or any of rUv's 20+ repos) — OR whenever you are asked to build, implement, add, refactor, enhance, or fix ANYTHING, in any repo, on any stack. Grounds every RuvNet capability claim in real source via search_ruvnet before asserting, actively considers the FULL toolkit (not just the 2-3 most-cited tools) for whichever one or two would genuinely help THIS project, and TAKES THE LEAD the Ruv way on every build regardless of stack — proposes the right architecture + why, gets one go/no-go, then orchestrates end-to-end (SPARC, parallel swarms, persistent memory, QA gates, proof) instead of acting like a passive answer-bot.
|
|
4
|
-
updated: 2026-
|
|
4
|
+
updated: 2026-08-01
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# RuvNet Brain
|
|
@@ -127,7 +127,10 @@ When asked to build, implement, add, refactor, enhance, or fix anything, do NOT
|
|
|
127
127
|
|
|
128
128
|
**2. On a yes (or when clearly authorized / low-risk), orchestrate end-to-end:**
|
|
129
129
|
- **SPARC** the non-trivial features: Specification → Pseudocode → Architecture → Refinement → Completion, with a QA gate between phases.
|
|
130
|
-
- **Parallelize** with Ruflo: `swarm_init` + `agent_spawn` to register tracked agents
|
|
130
|
+
- **Parallelize** with Ruflo: use `swarm_init` + `agent_spawn` to coordinate and register tracked agents; those calls do not select the execution billing path. Execute every normal swarm task through the active host's subscription-backed native agents — Claude Code's native Task tool or Codex's native collaboration agents — for research, reasoning, and hands-on file work. Run independent streams concurrently; don't serialize what can be parallel.
|
|
131
|
+
- **Isolate every non-trivial fix:** give each writing lane one dedicated git worktree and a scoped branch rooted at the current integration base. Never let two writers share a worktree. A read-only lane may share a checkout. Preserve any dirty or failed worktree as recovery evidence; never force-remove it to make the status look clean.
|
|
132
|
+
- **Promote through one rail:** require the lane's focused tests and failure-path mutant first, then hand its exact diff and evidence to one clean integration owner. The integration owner reconciles dependency order, runs the impacted regression gate, and creates one reviewable commit per fix. A passing lane is not shipped, and a dirty shared checkout is never a release candidate.
|
|
133
|
+
- **Seal before publication:** run the installed `release-proof` skill against an immutable packed artifact from the clean integration SHA. Only the protected release workflow may publish; only the post-publication seal permits `shipped` or `verified`. If any source, test, issue, host, grader, digest, or workflow evidence is absent, report the exact status (`NOT READY`, `SEALED, NOT SHIPPED`, or `PUBLISHED, NOT VERIFIED`) and keep working from the preserved workstream.
|
|
131
134
|
- **Persist** decisions + state to AgentDB (`memory_store` / `memory_search`) so nothing is lost across sessions or compaction. Recall before deciding — and write memory AT THE MOMENT a decision lands (an ADR accepted, a correction from the user, an architecture call), not reactively when someone asks why memory is empty. "I hadn't been writing memory, only checking it" is a real field failure; proactive capture is the fix.
|
|
132
135
|
- **Ground** every RuvNet capability claim via `search_ruvnet` before asserting, and prefer RuvNet building blocks over generic defaults — but only when the build actually touches the RuvNet stack. For a build with no RuvNet angle, ground in the user's own codebase and official docs instead, the normal way, without mentioning search_ruvnet or RuvNet at all.
|
|
133
136
|
- **Capture** key decisions as ADRs; QA each gate.
|
|
@@ -137,9 +140,13 @@ When asked to build, implement, add, refactor, enhance, or fix anything, do NOT
|
|
|
137
140
|
|
|
138
141
|
**4. Keep the user confident.** Say what you're doing and why as you go, signal progress, and explain any esoteric concept in one plain line before you lean on it. Narrate *decisions and progress* — never your own compliance with an internal rule (e.g. don't announce that a rule "doesn't apply here"; just proceed as if it were never mentioned). The user should always feel a sharp engineer is in charge and moving — never stalled, never guessing, and never explaining its own instructions to itself out loud.
|
|
139
142
|
|
|
140
|
-
## Cost-optimal model routing —
|
|
143
|
+
## Cost-optimal model routing — subscription execution is the default
|
|
141
144
|
|
|
142
|
-
Two separate questions, don't conflate them: (1) which
|
|
145
|
+
Two separate questions, don't conflate them: (1) which tier to use inside the active Claude Code or
|
|
146
|
+
Codex subscription, and (2) whether the user has explicitly chosen a separately billed provider.
|
|
147
|
+
The first is the default. Provider-backed execution requires explicit user opt-in; it is never an
|
|
148
|
+
implicit fallback. Never ask for an API key for normal swarm work. Ruflo coordinates the swarm;
|
|
149
|
+
the active host's native subscription agents execute it.
|
|
143
150
|
|
|
144
151
|
**0. HARD RULE — mechanical work does not run in the main loop (2026-07-13, replaces the advisory version).**
|
|
145
152
|
|
|
@@ -154,7 +161,7 @@ The first version of this rule said "consult the engine before dispatching." Adv
|
|
|
154
161
|
| Mechanical | grep/glob sweeps, reading CI logs, mechanical edits (rename, path fix, import swap), test-fixture rewrites, file inventories | Subagent, `model: haiku` |
|
|
155
162
|
| Analytical | tracing a bug across files, summarizing a subsystem, drafting tests from a spec | Subagent, `model: sonnet` |
|
|
156
163
|
| Judgment | architecture, root-cause reasoning, security/correctness calls, anything user-facing | Main loop (whatever the user chose) |
|
|
157
|
-
| Pure text, no repo access | research, summarize, classify, transform |
|
|
164
|
+
| Pure text, no repo access | research, summarize, classify, transform | Native subscription subagent on the active host |
|
|
158
165
|
|
|
159
166
|
**Every dispatch leaves a receipt — no exceptions, or this is decorative again.** Subagent dispatches were invisible until `dispatch-receipt.mjs` existed (route-cheap only logged OpenRouter calls), which is why the log looked dead even when routing happened. After the Agent returns, with the REAL sizes:
|
|
160
167
|
|
|
@@ -192,13 +199,21 @@ If `profile.json` is missing, ask the two subscription questions (Claude Pro/Max
|
|
|
192
199
|
|
|
193
200
|
Don't expect this specific tool to ever reach GLM/DeepSeek/anything non-Anthropic: its handler never computes an embedding, the one thing that unlocks Ruflo's separate neural router (`neural-router.ts`) — that router is real, well-built code (verified by installing its native FastGRNN backend and watching real training run), with real 2026-06-15 measured benchmark data, but its candidate pool is Ling-2.6-Flash / Gemini-2.5-Flash-Lite / GPT-4.1 / Llama-3.3-70B (not GLM/DeepSeek), it's reachable only via `agent_spawn`, gated behind an off-by-default env var, and has a live NaN bug on sparse per-candidate scoring. Don't wire around those gates — the payoff on rUv's own dataset is thin (n=20, near-tied margins), and a separate rUv benchmark (`ROUTER-PILOT.md`, 2026-06-28) found this whole class of embedding-based difficulty routing scores at chance (ROC-AUC 0.38) on real data. Not the lever to reach for.
|
|
194
201
|
|
|
195
|
-
**2.
|
|
202
|
+
**2. Explicit provider-backed delegation (opt-in only).** This section is inactive unless the user
|
|
203
|
+
explicitly asks to spend through an external provider for the current task. It is never selected
|
|
204
|
+
because a subscription is missing, logged out, quota-limited, or temporarily unavailable; those
|
|
205
|
+
conditions degrade or stop the native run without requesting a provider key. `mcp__ruflo__agent_execute`
|
|
206
|
+
is also provider-API execution, not a subscription-backed Ruflo worker, and must follow the same
|
|
207
|
+
explicit-consent boundary. For a user-authorized OpenRouter run, agentic-flow's CLI is the working
|
|
208
|
+
provider path:
|
|
196
209
|
|
|
197
210
|
```bash
|
|
198
211
|
npx agentic-flow@latest --agent researcher --model "deepseek/deepseek-chat" --task "<task>"
|
|
199
212
|
npx agentic-flow@latest --agent researcher --model "z-ai/glm-4.6" --task "<task>"
|
|
200
213
|
```
|
|
201
|
-
Any `--model` containing `/`
|
|
214
|
+
Any `--model` containing `/` routes through OpenRouter. Invoke it only after the explicit opt-in
|
|
215
|
+
above and only when the required provider credential is already configured; do not request one as
|
|
216
|
+
part of routine swarm setup.
|
|
202
217
|
|
|
203
218
|
Use this for read-only work with no file mutation — research, summarization, classification, simple text transforms — where a cheap model is good enough. Real, current pricing (pulled live from the OpenRouter API):
|
|
204
219
|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rvbc
|
|
3
|
-
description: Open the RuvNet Brain Console in Codex when the user mentions "rvbc", asks for the Brain Console, or wants to configure or inspect RuvNet Brain.
|
|
4
|
-
updated: 2026-07-
|
|
3
|
+
description: Open the RuvNet Brain Console in Claude Code and Codex when the user says "Configure RuvNet Brain", mentions "rvbc", asks for the Brain Console, or wants to configure or inspect RuvNet Brain. Claude Code supports /rvbc; Codex invokes the native $ruvnet-brain:rvbc skill.
|
|
4
|
+
updated: 2026-07-30
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# RuvNet Brain Console
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
Codex
|
|
9
|
+
“Configure RuvNet Brain” opens this Console in Claude Code and Codex. Claude Code also supports
|
|
10
|
+
`/rvbc`; Codex invokes this skill as `$ruvnet-brain:rvbc` because custom plugin slash commands are
|
|
11
|
+
not part of the Codex CLI slash-command surface.
|
|
11
12
|
|
|
12
13
|
1. Say one short sentence: "Opening it now; it scans live while you watch."
|
|
13
|
-
2.
|
|
14
|
-
|
|
14
|
+
2. Resolve the installed runtime at
|
|
15
|
+
`${RUVNET_BRAIN_KB:-$HOME/.cache/ruvnet-brain/kb}/.console-runtime/scripts/onboarding-console.mjs`.
|
|
16
|
+
A current-repository `scripts/onboarding-console.mjs` is allowed only for an explicit developer
|
|
17
|
+
checkout. Never fall back to a guessed `~/Code` path.
|
|
15
18
|
3. Run `node <resolved-script> --serve --open` in the background.
|
|
16
19
|
4. Give the URL immediately. Do not promise a duration; the page reports its own scan progress.
|
|
17
20
|
|
|
@@ -8,10 +8,10 @@ updated: 2026-07-28
|
|
|
8
8
|
|
|
9
9
|
Ground the answer in files that exist now; do not recite release claims from memory.
|
|
10
10
|
|
|
11
|
-
1.
|
|
12
|
-
plugin
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
1. Run the installed plugin's `scripts/whats-new.mjs` executable. It reads the version manifest and
|
|
12
|
+
curated notes from the same immutable plugin payload and exits nonzero if either asset is missing.
|
|
13
|
+
Do not substitute a checkout, download, or another installed version when it fails.
|
|
14
|
+
2. Read that command's output before summarizing it.
|
|
15
15
|
3. State the installed version exactly. Do not say the user is on 4.0 unless it starts with `4.`.
|
|
16
16
|
For a 3.9 development version, describe verified items as "4.0-line enhancements landing in
|
|
17
17
|
3.9.x", using the release note's own status language.
|