From dedb3a8cc27377940c2636ef7b0a39c22e7e3418 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sat, 3 Oct 2026 12:46:26 +0000 Subject: [PATCH 1/3] build: bake ruflo-console (mods) behind toolchains.ruflo_console Bakes ruflo-console, ruflo-mods and ruflo-swarm, ruflo's Claude Code function-hook mods, from the ruflo v3.51.1 tag (flake input rufloConsole, 09a1cb0244a677f54c2b3d691a7009927add527d) into the `agentbox` directory marketplace beside factrail. Gate: [toolchains].ruflo_console, default false, rebuild-class. - scripts/bake-ruflo-console.sh copies only the three plugin directories (1.6 MB of a ~100 MB repo), drops node_modules, defaults userConfig.cli to "ruflo" and fails the build if a version differs from marketplace.json. - scripts/ruflo-console-project.mjs runs at boot. It registers the three at their stable /opt paths (the codex-plugin-cc pattern, so there is no stale cache copy), enables them, sets pluginConfigs cli=ruflo and disables shadowing @ruflo copies. It self-heals and fails open. With the gate off it removes the three ids and leaves everything else byte-identical. - The entrypoint keeps CLAUDE_CODE_ENABLE_FUNCTION_HOOKS and the shared `agentbox` marketplace while either factrail or the console is on. - The ruflo closure is baked when the console gate is on, because cli=ruflo needs the bin on PATH. npx-offline fails ENOTCACHED on a fresh npm cache. - flake.lock narHash computed offline (NAR serialiser validated against the existing codexPlugin entry); the image is unverified until the host rebuild. Co-Authored-By: jjohare --- .github/workflows/invariants.yml | 6 + agentbox.toml | 1 + .../.claude-plugin/marketplace.json | 21 ++ config/entrypoint-unified.sh | 38 ++- flake.lock | 18 ++ flake.nix | 39 +++- management-api/lib/system-manifest.js | 3 + schema/agentbox.toml.schema.json | 5 + scripts/bake-ruflo-console.sh | 62 +++++ scripts/ruflo-console-project.mjs | 195 ++++++++++++++++ tests/config/ruflo-console.test.mjs | 219 ++++++++++++++++++ 11 files changed, 601 insertions(+), 6 deletions(-) create mode 100644 scripts/bake-ruflo-console.sh create mode 100644 scripts/ruflo-console-project.mjs create mode 100644 tests/config/ruflo-console.test.mjs diff --git a/.github/workflows/invariants.yml b/.github/workflows/invariants.yml index 29c5cb852..1d8102512 100644 --- a/.github/workflows/invariants.yml +++ b/.github/workflows/invariants.yml @@ -24,6 +24,9 @@ on: - 'tests/config/protocol-registry-lint.test.mjs' - 'tests/config/declared-vs-running.test.js' - 'tests/config/fixtures/runtime-state.json' + - 'tests/config/ruflo-console.test.mjs' + - 'config/claude-plugins/**' + - 'flake.lock' # Custody X-1 W0/W2 runtime contracts (RC-X1-01..06) and what they read # beyond config/entrypoint-unified.sh and flake.nix (both listed here). - 'tests/runtime-contract/RC-X1-*' @@ -136,6 +139,9 @@ jobs: - name: check-manifest-catalogue (ADR-039 gate-path parity) run: node scripts/ci/check-manifest-catalogue.js + - name: ruflo-console bake + boot projection ([toolchains].ruflo_console) + run: node --test tests/config/ruflo-console.test.mjs + # CY-A2: the manifest must not lie about what runs. CI has no runtime, so # it checks agentbox.toml against the committed, dated snapshot captured # on the box (`--capture`); on the box the same script reads the live diff --git a/agentbox.toml b/agentbox.toml index f45d6ba1b..a04d23030 100644 --- a/agentbox.toml +++ b/agentbox.toml @@ -1876,6 +1876,7 @@ codex = true opencode = true code_server = true cuda = true +ruflo_console = false # ruflo's Claude Code mods (function hooks), baked from the pinned ruflo v3.51.1 tag (flake input rufloConsole): ruflo-console (the /ruflo cockpit) + ruflo-mods (/ruflo mods) + ruflo-swarm (/ruflo swarm …) in the `agentbox` marketplace, userConfig cli=ruflo (the baked bin; also pulls in the ruflo closure). Rebuild-class; needs Claude Code >= 2.1.287. Off = the three ids are unregistered at boot # ── Plugins (ruflo/claude-flow plugin system) ──────────────────────────────── # Plugins are installed into $HOME/.claude-flow/plugins/ at container boot diff --git a/config/claude-plugins/.claude-plugin/marketplace.json b/config/claude-plugins/.claude-plugin/marketplace.json index 5c8aa5a45..98d11e30a 100644 --- a/config/claude-plugins/.claude-plugin/marketplace.json +++ b/config/claude-plugins/.claude-plugin/marketplace.json @@ -12,6 +12,27 @@ "description": "Jev compaction with fact rails: reduces what Jev lets go instead of deleting it, keeps the email taint fence, /factrail switch (ADR-2121). Baked from DreamLab-AI/factrail by lib/factrail.nix.", "version": "0.1.0", "license": "MIT OR Apache-2.0" + }, + { + "name": "ruflo-console", + "source": "./ruflo-console", + "description": "ruflo's cockpit inside Claude Code and the one /ruflo command for every ruflo mod (function hooks): overview, swarms, claims, federation, plugins, learning, MetaHarness, memory and cost views, a confirm-gated command palette and a band above the prompt. Baked from ruvnet/ruflo v3.51.1 (09a1cb0) behind [toolchains].ruflo_console; cli defaults to the baked ruflo bin.", + "version": "0.1.0", + "license": "MIT" + }, + { + "name": "ruflo-mods", + "source": "./ruflo-mods", + "description": "ruflo as a Claude Code mod (function hooks): in-process prompt routing, edit learning signals, tighten-only tool checks, the cost-tracker budget ladder, a mod trust gate and the $.ruflo noun; answers /ruflo mods. Baked from ruvnet/ruflo v3.51.1 (09a1cb0) behind [toolchains].ruflo_console; cli defaults to the baked ruflo bin.", + "version": "0.1.0", + "license": "MIT" + }, + { + "name": "ruflo-swarm", + "source": "./ruflo-swarm", + "description": "Agent teams and swarm coordination with a live swarm pane (function hooks); answers /ruflo swarm pane|status|topology|claims|consensus. Baked from ruvnet/ruflo v3.51.1 (09a1cb0) behind [toolchains].ruflo_console; cli defaults to the baked ruflo bin.", + "version": "0.3.0", + "license": "MIT" } ] } diff --git a/config/entrypoint-unified.sh b/config/entrypoint-unified.sh index 4e10d65cf..f0e47f783 100644 --- a/config/entrypoint-unified.sh +++ b/config/entrypoint-unified.sh @@ -2690,13 +2690,17 @@ unset _INSTR_LAYERS # store (scripts/factrail-store-migrate.mjs), so a resumed email session stays # fenced. _JC_ON="$(_ab_toml_bool features.jev_compaction enabled)" +# [toolchains].ruflo_console shares the `agentbox` marketplace and the +# function-hook switch with factrail, so the factrail block below keeps both +# while either gate is on (see the ruflo-console block after it). +_RC_ON="$(_ab_toml_bool toolchains ruflo_console)" _JC_MARKET="/opt/agentbox/config/claude-plugins" _JC_PLUGIN="$_JC_MARKET/factrail" _JC_BIN="/opt/agentbox/bin/factrail" -if command -v claude >/dev/null 2>&1 && [ -f "$_CLAUDE_SETTINGS" ] || [ "$_JC_ON" = "1" ]; then - SETTINGS="$_CLAUDE_SETTINGS" JC_ON="$_JC_ON" node <<'JCENVJS' || true +if command -v claude >/dev/null 2>&1 && [ -f "$_CLAUDE_SETTINGS" ] || [ "$_JC_ON" = "1" ] || [ "$_RC_ON" = "1" ]; then + SETTINGS="$_CLAUDE_SETTINGS" JC_ON="$_JC_ON" RC_ON="$_RC_ON" node <<'JCENVJS' || true const fs = require('fs'); -const f = process.env.SETTINGS, on = process.env.JC_ON === '1'; +const f = process.env.SETTINGS, on = process.env.JC_ON === '1' || process.env.RC_ON === '1'; let s = {}, orig = ''; try { orig = fs.readFileSync(f, 'utf8'); s = JSON.parse(orig); } catch {} if (on) { s.env = s.env || {}; s.env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS = '1'; } else if (s.env) { delete s.env.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS; if (!Object.keys(s.env).length) delete s.env; } @@ -2806,12 +2810,38 @@ elif command -v claude >/dev/null 2>&1 && [ -d /home/devuser/.claude/plugins ]; run_as_devuser env HOME=/home/devuser timeout 60 claude plugin uninstall factrail@agentbox >/dev/null 2>&1 \ && echo " [factrail] uninstalled plugin (gate off)" fi - if grep -q '"agentbox"' /home/devuser/.claude/plugins/known_marketplaces.json 2>/dev/null; then + if [ "$_RC_ON" != "1" ] && grep -q '"agentbox"' /home/devuser/.claude/plugins/known_marketplaces.json 2>/dev/null; then run_as_devuser env HOME=/home/devuser timeout 60 claude plugin marketplace remove agentbox >/dev/null 2>&1 \ && echo " [factrail] removed agentbox marketplace (gate off)" fi fi +# ── [toolchains].ruflo_console: ruflo-console + ruflo-mods + ruflo-swarm ────── +# Baked from the pinned ruflo v3.51.1 tag (flake input rufloConsole) into the +# `agentbox` marketplace beside factrail. Gate on: the marketplace is +# registered, and scripts/ruflo-console-project.mjs registers the three plugins +# at their stable /opt paths in installed_plugins.json (the codex-plugin-cc +# pattern: no cache copy, so a rebuild is never served a stale plugin), enables +# them in settings.json and sets userConfig cli=ruflo (the baked bin on PATH; +# upstream's npx-offline default fails without a warm npm cache). +# CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is set by the block above. Self-healing: a +# wrong installPath, version or cli is rewritten every boot. Gate off: the three +# ids are removed and nothing else changes. Fail-open throughout. +_RC_MARKET="/opt/agentbox/config/claude-plugins" +if command -v node >/dev/null 2>&1 && [ -d /home/devuser/.claude ]; then + if [ "$_RC_ON" = "1" ] && [ -d "$_RC_MARKET/ruflo-console" ] && command -v claude >/dev/null 2>&1; then + run_as_devuser env HOME=/home/devuser timeout 60 claude plugin marketplace add "$_RC_MARKET" >/dev/null 2>&1 \ + || echo " [ruflo-console] marketplace add failed (continuing; a stale registration may remain)" + command -v ruflo >/dev/null 2>&1 \ + || echo " [ruflo-console] the ruflo bin is not on PATH — console probes will read n/a until the image is rebuilt with the gate on" + fi + run_as_devuser env HOME=/home/devuser node /opt/agentbox/scripts/ruflo-console-project.mjs \ + --on "$_RC_ON" --settings "$_CLAUDE_SETTINGS" \ + --installed /home/devuser/.claude/plugins/installed_plugins.json \ + --market "$_RC_MARKET" || true +fi +unset _RC_MARKET + # ── MCP registry projection (MCP-1 / MCP-2): project .mcp.json FROM skills/mcp.json ── # audit-2026-07-15 MCP-1: skills/mcp.json (the 28-server registry) had NO runtime # consumer — ~19 gated servers registered nowhere the harness reads. This makes the diff --git a/flake.lock b/flake.lock index 5c5ac792b..8109c942e 100644 --- a/flake.lock +++ b/flake.lock @@ -192,11 +192,29 @@ "flake-utils": "flake-utils", "nix2container": "nix2container", "nixpkgs": "nixpkgs_3", + "rufloConsole": "rufloConsole", "rust-overlay": "rust-overlay", "skills": "skills", "vaultSrc": "vaultSrc" } }, + "rufloConsole": { + "flake": false, + "locked": { + "lastModified": 1790957163, + "narHash": "sha256-msekrJ/g8tuHUnc0CMSjGtnv4uMtS5h73bR14KOYsHY=", + "owner": "ruvnet", + "repo": "ruflo", + "rev": "09a1cb0244a677f54c2b3d691a7009927add527d", + "type": "github" + }, + "original": { + "owner": "ruvnet", + "repo": "ruflo", + "rev": "09a1cb0244a677f54c2b3d691a7009927add527d", + "type": "github" + } + }, "rust-overlay": { "inputs": { "nixpkgs": "nixpkgs_4" diff --git a/flake.nix b/flake.nix index 68e1f60ef..8c3442c08 100644 --- a/flake.nix +++ b/flake.nix @@ -53,9 +53,21 @@ url = "github:openai/codex-plugin-cc/db52e28f4d9ded852ab3942cea316258ae4ef346"; flake = false; }; + + # ruflo's Claude Code mods (function hooks): ruflo-console (the /ruflo + # cockpit), plus ruflo-mods and ruflo-swarm, which answer `/ruflo mods` and + # `/ruflo swarm …` on the same command. Pinned to the ruflo v3.51.1 tag, + # files-only like `codexPlugin`; gated by [toolchains].ruflo_console. The + # ruflo repository is ~100 MB, so only the three plugin directories are + # baked (scripts/bake-ruflo-console.sh), never the tree. Bump with: + # nix flake lock --update-input rufloConsole + rufloConsole = { + url = "github:ruvnet/ruflo/09a1cb0244a677f54c2b3d691a7009927add527d"; + flake = false; + }; }; - outputs = { self, nixpkgs, flake-utils, nix2container, rust-overlay, skills, aoe, codexPlugin, vaultSrc }: + outputs = { self, nixpkgs, flake-utils, nix2container, rust-overlay, skills, aoe, codexPlugin, rufloConsole, vaultSrc }: flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" @@ -619,7 +631,10 @@ npmCliGatedPackages = # ruflo ships the claude-flow bins too (consolidated, see rufloPkg) — # either gate pulls in the single closure, never both twice. - lib.optionals ((toolchainCfg.ruflo or false) || (toolchainCfg.claude_flow or false)) [ rufloPkg ] + # ruflo_console reaches ruflo through the `ruflo` bin on PATH (its + # userConfig cli is baked to "ruflo"), so the console gate pulls the + # closure in too. + lib.optionals ((toolchainCfg.ruflo or false) || (toolchainCfg.claude_flow or false) || rufloConsoleOn) [ rufloPkg ] ++ lib.optionals (toolchainCfg.metaharness or false) [ metaharnessPkg metaharnessDarwinPkg ] ++ lib.optionals (toolchainCfg.agentic_qe or false) [ agenticQePkg ] ++ lib.optionals (toolchainCfg.codebase_memory or false) [ codebaseMemoryPkg ] @@ -1644,6 +1659,15 @@ # ADR-2121). One pinned commit gives the binary and the # Claude Code shim that calls it; see lib/factrail.nix. jevCompactionOn = ((agentboxConfig.features or {}).jev_compaction or {}).enabled or false; + # [toolchains].ruflo_console: bake ruflo-console + ruflo-mods + ruflo-swarm + # from the pinned rufloConsole input into the `agentbox` marketplace. + rufloConsoleOn = toolchainCfg.ruflo_console or false; + rufloConsolePlugins = pkgs.runCommand "ruflo-console-plugins-3.51.1" { + nativeBuildInputs = [ pkgs.jq ]; + } '' + bash ${./scripts/bake-ruflo-console.sh} ${rufloConsole} $out \ + ${./config/claude-plugins/.claude-plugin/marketplace.json} + ''; factrailPkg = import ./lib/factrail.nix { inherit lib; pkgs = rustPkgs; }; factrailPackages = lib.optionals jevCompactionOn [ factrailPkg ]; @@ -1933,6 +1957,17 @@ default_days = ${toString (relayCfg.retention_days or 30)} chmod -R u+w $out/opt/agentbox/config/claude-plugins/factrail ''} + ${lib.optionalString rufloConsoleOn '' + # ruflo-console, ruflo-mods and ruflo-swarm join the `agentbox` directory + # marketplace beside factrail. Only the three plugin directories of the + # pinned ruflo tag, with userConfig.cli defaulting to the baked `ruflo` + # bin (npx-offline finds no @claude-flow/cli in a fresh npm cache). The + # entrypoint registers them at /opt paths, never at a store path. + chmod u+w $out/opt/agentbox/config/claude-plugins + cp -r ${rufloConsolePlugins}/. $out/opt/agentbox/config/claude-plugins/ + chmod -R u+w $out/opt/agentbox/config/claude-plugins + ''} + # tmux plugin loader — generated with Nix-interpolated store paths so # run-shell lines resolve correctly without TPM or runtime path search. # Sourced from tmux.conf via: source-file /opt/agentbox/config/tmux-plugins.conf diff --git a/management-api/lib/system-manifest.js b/management-api/lib/system-manifest.js index eb531019f..f3bd2300c 100644 --- a/management-api/lib/system-manifest.js +++ b/management-api/lib/system-manifest.js @@ -103,6 +103,9 @@ const CATALOGUE = [ { id: 'metaharness-plugin', name: 'ruflo-metaharness plugin', layer: 'module', gate: null, apply_class: 'boot', summary: 'ADR-063: boot-symlinked from the ruflo plugin cache via [[plugins.packages]]; 13 harness-intelligence skills, graceful-degrade without the baked CLIs.' }, + { id: 'ruflo-console', name: 'ruflo console + mods (Claude Code function hooks)', layer: 'module', + gate: 'toolchains.ruflo_console', apply_class: 'rebuild', + summary: 'ruflo-console (the /ruflo cockpit: overview, swarms, claims, federation, plugins, learning, MetaHarness, memory and cost), ruflo-mods (/ruflo mods) and ruflo-swarm (/ruflo swarm …), baked from the pinned ruflo v3.51.1 tag (flake input rufloConsole, only the three plugin directories) into the `agentbox` marketplace. REBUILD-class: the plugins and the ruflo closure are gated in flake.nix; the entrypoint then registers them at /opt paths, enables them, sets userConfig cli=ruflo (the baked bin; npx-offline fails without a warm npm cache) and keeps CLAUDE_CODE_ENABLE_FUNCTION_HOOKS on. Off unregisters the three ids. Needs Claude Code >= 2.1.287.' }, { id: 'codex', name: 'OpenAI Codex CLI + MCP', layer: 'module', gate: 'toolchains.codex', apply_class: 'rebuild', summary: 'Rust-native codex binary (musl) + openai-codex MCP server; consultant + QE-court provider.' }, diff --git a/schema/agentbox.toml.schema.json b/schema/agentbox.toml.schema.json index 873083f15..af5259e4a 100644 --- a/schema/agentbox.toml.schema.json +++ b/schema/agentbox.toml.schema.json @@ -847,6 +847,11 @@ }, "gemini_cli": { "type": "boolean" + }, + "ruflo_console": { + "type": "boolean", + "default": false, + "description": "Bake ruflo-console, ruflo-mods and ruflo-swarm (Claude Code function-hook mods) from the pinned ruflo v3.51.1 tag into the agentbox marketplace and enable them at boot with userConfig cli=ruflo. Rebuild-class; needs Claude Code >= 2.1.287." } } }, diff --git a/scripts/bake-ruflo-console.sh b/scripts/bake-ruflo-console.sh new file mode 100644 index 000000000..f2cb8c03d --- /dev/null +++ b/scripts/bake-ruflo-console.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# bake-ruflo-console.sh SRC OUT MARKETPLACE_JSON +# +# Build step for [toolchains].ruflo_console (flake.nix rufloConsolePlugins). +# SRC is the pinned ruflo checkout (the `rufloConsole` flake input). OUT +# receives exactly three directories, ruflo-console, ruflo-mods and ruflo-swarm, +# which the image copies into the `agentbox` directory marketplace +# (/opt/agentbox/config/claude-plugins) beside factrail. +# +# - Only those three plugin directories are copied. The ruflo repository is +# about 100 MB and holds 40-odd other plugins, so nothing else from SRC lands. +# - node_modules is never copied: the plugins are TypeScript function-hook +# modules that Claude Code loads directly and that import only `claude-code` +# types. +# - userConfig.cli defaults to "ruflo" in ruflo-console and ruflo-swarm. +# Upstream's default, npx-offline, runs `npx --offline -y @claude-flow/cli@latest`, +# which fails ENOTCACHED in a container whose npm cache has never fetched the +# package. The image ships the cli as the Nix-baked `ruflo` bin on PATH instead. +# - Every plugin's version must equal the version marketplace.json lists for it, +# so a bumped pin with a stale catalogue fails the build rather than +# registering under the wrong version. +set -euo pipefail + +src="${1:?usage: bake-ruflo-console.sh SRC OUT MARKETPLACE_JSON}" +out="${2:?usage: bake-ruflo-console.sh SRC OUT MARKETPLACE_JSON}" +market="${3:?usage: bake-ruflo-console.sh SRC OUT MARKETPLACE_JSON}" + +plugins=(ruflo-console ruflo-mods ruflo-swarm) +cli_default="ruflo" + +mkdir -p "$out" +for name in "${plugins[@]}"; do + from="$src/plugins/$name" + manifest="$from/.claude-plugin/plugin.json" + [ -f "$manifest" ] || { echo "bake-ruflo-console: $manifest missing at the pinned rev" >&2; exit 1; } + [ -f "$from/hooks/hooks.json" ] || { echo "bake-ruflo-console: $name has no hooks/hooks.json" >&2; exit 1; } + + # cp -r then prune keeps the plugin's own layout, symlinks included. + cp -r "$from" "$out/$name" + chmod -R u+w "$out/$name" + find "$out/$name" -name node_modules -prune -exec rm -rf {} + + + baked="$out/$name/.claude-plugin/plugin.json" + if jq -e '.userConfig.cli' "$baked" >/dev/null; then + jq --arg d "$cli_default" '.userConfig.cli.default = $d' "$baked" > "$baked.tmp" + mv "$baked.tmp" "$baked" + fi + + have="$(jq -r .version "$baked")" + want="$(jq -r --arg n "$name" '.plugins[] | select(.name == $n) | .version' "$market")" + if [ "$have" != "$want" ]; then + echo "bake-ruflo-console: $name is $have at the pinned rev but marketplace.json lists '${want}'" >&2 + exit 1 + fi +done + +# Nothing but the three plugin directories at the top level. +extra="$(find "$out" -mindepth 1 -maxdepth 1 | sed "s#^$out/##" | grep -vxE 'ruflo-console|ruflo-mods|ruflo-swarm' || true)" +if [ -n "$extra" ]; then + echo "bake-ruflo-console: unexpected entries in $out: $extra" >&2 + exit 1 +fi diff --git a/scripts/ruflo-console-project.mjs b/scripts/ruflo-console-project.mjs new file mode 100644 index 000000000..5b5398cca --- /dev/null +++ b/scripts/ruflo-console-project.mjs @@ -0,0 +1,195 @@ +#!/usr/bin/env node +// ruflo-console-project.mjs — boot projection for [toolchains].ruflo_console. +// +// node ruflo-console-project.mjs --on 1|0 --settings +// --installed --market +// +// Gate on (and the three plugins baked under --market): +// * installed_plugins.json holds ruflo-console@agentbox, ruflo-mods@agentbox +// and ruflo-swarm@agentbox, each at its stable /opt/agentbox path (the +// codex-plugin-cc pattern: no copy into ~/.claude/plugins/cache, so a +// rebuilt image is never served a stale cached plugin). An entry whose +// installPath or version differs from the baked one is rewritten, not +// skipped. +// * settings.json enabledPlugins has exactly those three set to true. +// * pluginConfigs[""].options.cli is "ruflo" for the console and the +// swarm, the channel Claude Code hands to the plugin. Upstream's own default, +// npx-offline, fails ENOTCACHED without a warm npm cache; an unset value +// also falls back to it, so the baked manifest default alone is not enough. +// An operator's other valid choice (npx, claude-flow) is kept. +// * The same plugins installed from the network `ruflo` marketplace are +// disabled (not uninstalled), or `/ruflo` would be hooked twice. +// Gate off: the three ids leave installed_plugins.json, enabledPlugins and +// pluginConfigs, and nothing else changes; a file with none of them is not +// rewritten (byte-identical when off, ADR-2020). +// +// Never writes a /nix/store path. Fail-open: an unreadable file is left alone, +// and the script always exits 0 unless called with bad arguments. +import fs from 'node:fs' +import path from 'node:path' + +export const PLUGINS = ['ruflo-console', 'ruflo-mods', 'ruflo-swarm'] +export const MARKETPLACE = 'agentbox' +export const CLI = 'ruflo' +const CLI_CHOICES = new Set(['npx-offline', 'npx', 'ruflo', 'claude-flow']) +const idOf = name => `${name}@${MARKETPLACE}` + +function readJson(file, fallback) { + let text + try { + text = fs.readFileSync(file, 'utf8') + } catch { + return { value: fallback, text: null } + } + try { + return { value: JSON.parse(text), text } + } catch { + return { value: null, text } + } +} + +/** The baked plugins: name → { installPath, version, declaresCli }. Missing ones are left out. */ +export function bakedPlugins(market) { + const out = {} + for (const name of PLUGINS) { + const dir = path.join(market, name) + try { + const manifest = JSON.parse(fs.readFileSync(path.join(dir, '.claude-plugin', 'plugin.json'), 'utf8')) + out[name] = { installPath: dir, version: String(manifest.version ?? '0.0.0'), declaresCli: Boolean(manifest.userConfig?.cli) } + } catch { + // not baked + } + } + return out +} + +/** Pure: the installed_plugins.json document after projection. */ +export function projectInstalled(doc, on, baked, now) { + const next = structuredClone(doc ?? {}) + next.plugins = next.plugins && typeof next.plugins === 'object' ? next.plugins : {} + const log = [] + for (const name of PLUGINS) { + const id = idOf(name) + const want = on ? baked[name] : undefined + if (!want) { + if (id in next.plugins) { + delete next.plugins[id] + log.push(`unregistered ${id}`) + } + continue + } + const held = Array.isArray(next.plugins[id]) ? next.plugins[id][0] : undefined + if (held && held.installPath === want.installPath && held.version === want.version && held.scope === 'user') continue + next.plugins[id] = [{ + scope: 'user', + installPath: want.installPath, + version: want.version, + installedAt: held?.installedAt ?? now, + lastUpdated: now, + }] + log.push(`${held ? 'corrected' : 'registered'} ${id} ${want.version} at ${want.installPath}`) + } + return { doc: next, log } +} + +/** Pure: the settings.json document after projection. */ +export function projectSettings(doc, on, baked) { + const next = structuredClone(doc ?? {}) + const log = [] + const ids = PLUGINS.map(idOf) + if (on) { + next.enabledPlugins = next.enabledPlugins && typeof next.enabledPlugins === 'object' ? next.enabledPlugins : {} + for (const name of PLUGINS) { + if (!baked[name]) continue + const id = idOf(name) + if (next.enabledPlugins[id] !== true) { + next.enabledPlugins[id] = true + log.push(`enabled ${id}`) + } + // The network-marketplace copy of the same mod would hook /ruflo a second time. + const shadow = `${name}@ruflo` + if (next.enabledPlugins[shadow] === true) { + next.enabledPlugins[shadow] = false + log.push(`disabled ${shadow} (the baked ${id} replaces it)`) + } + if (!baked[name].declaresCli) continue + next.pluginConfigs = next.pluginConfigs && typeof next.pluginConfigs === 'object' ? next.pluginConfigs : {} + const entry = next.pluginConfigs[id] && typeof next.pluginConfigs[id] === 'object' ? next.pluginConfigs[id] : {} + const options = entry.options && typeof entry.options === 'object' ? entry.options : {} + const held = options.cli + if (typeof held !== 'string' || !CLI_CHOICES.has(held) || held === 'npx-offline') { + next.pluginConfigs[id] = { ...entry, options: { ...options, cli: CLI } } + log.push(`set ${id} cli=${CLI}${held === undefined ? '' : ` (was ${JSON.stringify(held)})`}`) + } + } + } else { + // Only what this gate wrote is removed; a map it emptied goes with it. + for (const key of ['enabledPlugins', 'pluginConfigs']) { + const map = next[key] + if (!map || typeof map !== 'object') continue + const held = ids.filter(id => id in map) + for (const id of held) { + delete map[id] + log.push(`removed ${id} from ${key}`) + } + if (held.length && Object.keys(map).length === 0) delete next[key] + } + } + return { doc: next, log } +} + +function arg(argv, name) { + const i = argv.indexOf(`--${name}`) + return i >= 0 ? argv[i + 1] : undefined +} + +function main(argv) { + const on = arg(argv, 'on') === '1' + const settingsFile = arg(argv, 'settings') + const installedFile = arg(argv, 'installed') + const market = arg(argv, 'market') + if (!settingsFile || !installedFile || !market) { + console.error('usage: ruflo-console-project.mjs --on 1|0 --settings F --installed F --market DIR') + return 2 + } + const baked = on ? bakedPlugins(market) : {} + if (on && Object.keys(baked).length === 0) { + console.log(` [ruflo-console] enabled but not baked (${market}/ruflo-console missing) — rebuild the image`) + } + const now = arg(argv, 'now') ?? new Date().toISOString() + + const installed = readJson(installedFile, { version: 2, plugins: {} }) + if (installed.value !== null) { + const { doc, log } = projectInstalled(installed.value, on, baked, now) + if (log.length) { + fs.mkdirSync(path.dirname(installedFile), { recursive: true }) + fs.writeFileSync(installedFile, JSON.stringify(doc, null, 2)) + for (const line of log) console.log(` [ruflo-console] ${line}`) + } + } else { + console.log(` [ruflo-console] ${installedFile} is not JSON — left alone`) + } + + const settings = readJson(settingsFile, {}) + if (settings.value !== null) { + const { doc, log } = projectSettings(settings.value, on, baked) + if (log.length) { + fs.mkdirSync(path.dirname(settingsFile), { recursive: true }) + fs.writeFileSync(settingsFile, JSON.stringify(doc, null, 2)) + for (const line of log) console.log(` [ruflo-console] ${line}`) + } + } else { + console.log(` [ruflo-console] ${settingsFile} is not JSON — left alone`) + } + return 0 +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let code = 0 + try { + code = main(process.argv.slice(2)) + } catch (err) { + console.log(` [ruflo-console] projection failed (continuing): ${err?.message ?? err}`) + } + process.exit(code) +} diff --git a/tests/config/ruflo-console.test.mjs b/tests/config/ruflo-console.test.mjs new file mode 100644 index 000000000..572e0fb9b --- /dev/null +++ b/tests/config/ruflo-console.test.mjs @@ -0,0 +1,219 @@ +// [toolchains].ruflo_console — the bake (scripts/bake-ruflo-console.sh), the +// boot projection (scripts/ruflo-console-project.mjs) and the catalogue +// (config/claude-plugins/.claude-plugin/marketplace.json) agree. +// +// node --test tests/config/ruflo-console.test.mjs +// RUFLO_SRC= node --test … also bakes the real tree +import { test } from 'node:test' +import assert from 'node:assert/strict' +import { execFileSync, spawnSync } from 'node:child_process' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import { PLUGINS, projectInstalled, projectSettings, bakedPlugins } from '../../scripts/ruflo-console-project.mjs' + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..') +const bake = path.join(root, 'scripts/bake-ruflo-console.sh') +const project = path.join(root, 'scripts/ruflo-console-project.mjs') +const marketJson = path.join(root, 'config/claude-plugins/.claude-plugin/marketplace.json') +const market = JSON.parse(fs.readFileSync(marketJson, 'utf8')) +const PIN = '09a1cb0244a677f54c2b3d691a7009927add527d' +// The versions each plugin.json carries at the v3.51.1 tag. +const PINNED_VERSIONS = { 'ruflo-console': '0.1.0', 'ruflo-mods': '0.1.0', 'ruflo-swarm': '0.3.0' } +const IDS = PLUGINS.map(n => `${n}@agentbox`) + +const tmp = () => fs.mkdtempSync(path.join(os.tmpdir(), 'ruflo-console-')) +const write = (file, text) => { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, text) } + +/** A miniature ruflo checkout: the three mods, an unrelated plugin, node_modules and repo files. */ +function fixtureRuflo(versions = PINNED_VERSIONS) { + const src = tmp() + for (const name of PLUGINS) { + const dir = path.join(src, 'plugins', name) + const userConfig = name === 'ruflo-mods' ? { modTrust: { type: 'string', default: 'observe' } } : { cli: { type: 'string', default: 'npx-offline' } } + write(path.join(dir, '.claude-plugin/plugin.json'), JSON.stringify({ name, version: versions[name], userConfig })) + write(path.join(dir, 'hooks/hooks.json'), JSON.stringify({ modules: ['./register.ts'] })) + write(path.join(dir, 'hooks/register.ts'), 'export default () => {}\n') + write(path.join(dir, 'node_modules/left-pad/index.js'), 'module.exports = 1\n') + write(path.join(dir, 'hooks/node_modules/x/index.js'), 'module.exports = 2\n') + } + write(path.join(src, 'plugins/ruflo-core/.claude-plugin/plugin.json'), '{"name":"ruflo-core","version":"9.9.9"}') + write(path.join(src, 'v3/package.json'), '{}') + write(path.join(src, 'README.md'), 'ruflo\n') + return src +} + +function runBake(src) { + const out = path.join(tmp(), 'out') + const r = spawnSync('bash', [bake, src, out, marketJson], { encoding: 'utf8' }) + return { out, status: r.status, stderr: r.stderr } +} + +function walk(dir) { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => { + const p = path.join(dir, e.name) + return e.isDirectory() ? [p + '/', ...walk(p)] : [p] + }) +} + +test('marketplace.json lists the three mods at the pinned versions, beside factrail', () => { + const byName = Object.fromEntries(market.plugins.map(p => [p.name, p])) + assert.ok(byName.factrail, 'factrail stays listed') + for (const name of PLUGINS) { + assert.equal(byName[name]?.version, PINNED_VERSIONS[name], `${name} version`) + assert.equal(byName[name].source, `./${name}`) + } +}) + +test('the flake input and lock pin the v3.51.1 commit', () => { + const flake = fs.readFileSync(path.join(root, 'flake.nix'), 'utf8') + assert.match(flake, new RegExp(`url = "github:ruvnet/ruflo/${PIN}";`)) + assert.match(flake, /codexPlugin, rufloConsole, vaultSrc \}:/) + const lock = JSON.parse(fs.readFileSync(path.join(root, 'flake.lock'), 'utf8')) + assert.equal(lock.nodes.rufloConsole.locked.rev, PIN) + assert.equal(lock.nodes.rufloConsole.flake, false) + assert.equal(lock.nodes.root.inputs.rufloConsole, 'rufloConsole') +}) + +test('the bake keeps only the three plugin directories and no node_modules', () => { + const { out, status, stderr } = runBake(fixtureRuflo()) + assert.equal(status, 0, stderr) + assert.deepEqual(fs.readdirSync(out).sort(), [...PLUGINS].sort()) + const files = walk(out) + assert.deepEqual(files.filter(f => f.includes('node_modules')), []) + for (const f of files) assert.ok(PLUGINS.some(n => f.startsWith(path.join(out, n) + '/')), `outside the plugin dirs: ${f}`) +}) + +test('the bake defaults userConfig.cli to ruflo where a plugin declares it', () => { + const { out } = runBake(fixtureRuflo()) + for (const name of PLUGINS) { + const manifest = JSON.parse(fs.readFileSync(path.join(out, name, '.claude-plugin/plugin.json'), 'utf8')) + if (name === 'ruflo-mods') assert.equal(manifest.userConfig.cli, undefined) + else assert.equal(manifest.userConfig.cli.default, 'ruflo') + } +}) + +test('the bake fails when a pinned version differs from marketplace.json', () => { + const { status, stderr } = runBake(fixtureRuflo({ ...PINNED_VERSIONS, 'ruflo-swarm': '0.4.0' })) + assert.notEqual(status, 0) + assert.match(stderr, /ruflo-swarm is 0\.4\.0 at the pinned rev but marketplace\.json lists '0\.3\.0'/) +}) + +test('the real ruflo tree bakes to versions matching marketplace.json', { skip: !process.env.RUFLO_SRC && 'set RUFLO_SRC to a ruflo checkout at the pin' }, () => { + const { out, status, stderr } = runBake(process.env.RUFLO_SRC) + assert.equal(status, 0, stderr) + for (const p of market.plugins.filter(p => PLUGINS.includes(p.name))) { + assert.equal(JSON.parse(fs.readFileSync(path.join(out, p.name, '.claude-plugin/plugin.json'), 'utf8')).version, p.version) + } + assert.deepEqual(walk(out).filter(f => f.includes('node_modules')), []) +}) + +const OTHER = { 'factrail@agentbox': true, 'skill-creator@claude-plugins-official': true } + +test('gate off leaves a settings file without the three ids byte-identical', () => { + const dir = tmp() + const settings = path.join(dir, 'settings.json') + const installed = path.join(dir, 'installed_plugins.json') + const text = JSON.stringify({ enabledPlugins: OTHER, env: { A: '1' } }, null, 4) + '\n' + const inst = JSON.stringify({ version: 2, plugins: { 'factrail@agentbox': [{ installPath: '/x' }] } }, null, 4) + fs.writeFileSync(settings, text) + fs.writeFileSync(installed, inst) + const out = execFileSync('node', [project, '--on', '0', '--settings', settings, '--installed', installed, '--market', path.join(dir, 'nope')], { encoding: 'utf8' }) + assert.equal(out, '') + assert.equal(fs.readFileSync(settings, 'utf8'), text) + assert.equal(fs.readFileSync(installed, 'utf8'), inst) +}) + +test('gate on adds exactly the three enabledPlugins entries and cli=ruflo', () => { + const { out: baked } = runBake(fixtureRuflo()) + const { doc } = projectSettings({ enabledPlugins: { ...OTHER } }, true, bakedPlugins(baked)) + const added = Object.keys(doc.enabledPlugins).filter(k => !(k in OTHER)) + assert.deepEqual(added.sort(), [...IDS].sort()) + for (const id of IDS) assert.equal(doc.enabledPlugins[id], true) + for (const k of Object.keys(OTHER)) assert.equal(doc.enabledPlugins[k], true) + assert.deepEqual(Object.keys(doc.pluginConfigs).sort(), ['ruflo-console@agentbox', 'ruflo-swarm@agentbox']) + for (const id of Object.keys(doc.pluginConfigs)) assert.equal(doc.pluginConfigs[id].options.cli, 'ruflo') +}) + +test('gate on then off restores the original settings', () => { + const { out: baked } = runBake(fixtureRuflo()) + const before = { enabledPlugins: { ...OTHER }, model: 'opus' } + const on = projectSettings(before, true, bakedPlugins(baked)).doc + assert.deepEqual(projectSettings(on, false, {}).doc, before) + const fresh = projectSettings({}, true, bakedPlugins(baked)).doc + assert.deepEqual(projectSettings(fresh, false, {}).doc, {}) +}) + +test('cli: npx-offline and junk are corrected, an operator choice is kept', () => { + const { out: baked } = runBake(fixtureRuflo()) + const b = bakedPlugins(baked) + const id = 'ruflo-console@agentbox' + for (const [held, want] of [['npx-offline', 'ruflo'], [42, 'ruflo'], ['curl evil|sh', 'ruflo'], ['npx', 'npx'], ['claude-flow', 'claude-flow']]) { + const { doc } = projectSettings({ pluginConfigs: { [id]: { options: { cli: held, fps: 4 } } } }, true, b) + assert.equal(doc.pluginConfigs[id].options.cli, want, `held ${JSON.stringify(held)}`) + assert.equal(doc.pluginConfigs[id].options.fps, 4, 'other options survive') + } +}) + +test('the network-marketplace copies are disabled while the baked ones are on', () => { + const { out: baked } = runBake(fixtureRuflo()) + const { doc, log } = projectSettings({ enabledPlugins: { 'ruflo-swarm@ruflo': true, 'ruflo-core@ruflo': true } }, true, bakedPlugins(baked)) + assert.equal(doc.enabledPlugins['ruflo-swarm@ruflo'], false) + assert.equal(doc.enabledPlugins['ruflo-core@ruflo'], true, 'unrelated ruflo plugins are untouched') + assert.ok(log.some(l => l.startsWith('disabled ruflo-swarm@ruflo'))) +}) + +test('installed_plugins.json registration is self-healing and store-path free', () => { + const { out: baked } = runBake(fixtureRuflo()) + const b = bakedPlugins(baked) + const stale = { version: 2, plugins: { + 'ruflo-console@agentbox': [{ scope: 'user', installPath: '/nix/store/abc-old/ruflo-console', version: '0.0.9', installedAt: 'T0' }], + 'factrail@agentbox': [{ installPath: '/keep' }], + } } + const { doc, log } = projectInstalled(stale, true, b, 'T1') + for (const name of PLUGINS) { + const [e] = doc.plugins[`${name}@agentbox`] + assert.equal(e.installPath, path.join(baked, name)) + assert.equal(e.version, PINNED_VERSIONS[name]) + } + assert.equal(doc.plugins['ruflo-console@agentbox'][0].installedAt, 'T0') + assert.ok(log.includes(`corrected ruflo-console@agentbox 0.1.0 at ${path.join(baked, 'ruflo-console')}`)) + assert.deepEqual(doc.plugins['factrail@agentbox'], [{ installPath: '/keep' }]) + assert.equal(projectInstalled(doc, true, b, 'T2').log.length, 0, 'a second boot changes nothing') + assert.doesNotMatch(JSON.stringify(doc), /\/nix\/store/) + const off = projectInstalled(doc, false, {}, 'T3').doc + assert.deepEqual(Object.keys(off.plugins), ['factrail@agentbox']) +}) + +test('the projector fails open on a corrupt settings file', () => { + const dir = tmp() + const settings = path.join(dir, 'settings.json') + fs.writeFileSync(settings, '{not json') + const r = spawnSync('node', [project, '--on', '1', '--settings', settings, '--installed', path.join(dir, 'i.json'), '--market', dir], { encoding: 'utf8' }) + assert.equal(r.status, 0) + assert.equal(fs.readFileSync(settings, 'utf8'), '{not json') +}) + +test('the entrypoint keeps the shared marketplace and function hooks while the console gate is on', () => { + const entry = fs.readFileSync(path.join(root, 'config/entrypoint-unified.sh'), 'utf8') + assert.match(entry, /_RC_ON="\$\(_ab_toml_bool toolchains ruflo_console\)"/) + assert.match(entry, /on = process\.env\.JC_ON === '1' \|\| process\.env\.RC_ON === '1'/) + assert.match(entry, /if \[ "\$_RC_ON" != "1" \] && grep -q '"agentbox"' .*known_marketplaces\.json/) + assert.match(entry, /node \/opt\/agentbox\/scripts\/ruflo-console-project\.mjs \\\n\s+--on "\$_RC_ON"/) + // The RC_ON definition precedes its first use (set -u). + assert.ok(entry.indexOf('_RC_ON="$(') < entry.indexOf('RC_ON="$_RC_ON"')) +}) + +test('manifest, schema and catalogue carry the gate as rebuild-class, default off', async () => { + const toml = fs.readFileSync(path.join(root, 'agentbox.toml'), 'utf8') + const section = toml.slice(toml.indexOf('\n[toolchains]'), toml.indexOf('\n[', toml.indexOf('\n[toolchains]') + 1)) + assert.match(section, /\nruflo_console = false\b/) + const schema = JSON.parse(fs.readFileSync(path.join(root, 'schema/agentbox.toml.schema.json'), 'utf8')) + assert.equal(schema.properties.toolchains.properties.ruflo_console.type, 'boolean') + const { CATALOGUE } = await import(path.join(root, 'management-api/lib/system-manifest.js')) + const entry = CATALOGUE.find(e => e.id === 'ruflo-console') + assert.equal(entry.gate, 'toolchains.ruflo_console') + assert.equal(entry.apply_class, 'rebuild') +}) From 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sat, 3 Oct 2026 12:49:57 +0000 Subject: [PATCH 2/3] fix(custody): classify the ruflo-console boot locals _RC_ON and _RC_MARKET env-secret-inventory requires every name the entrypoint reads to carry a class; both are NON_SECRET locals beside factrail's _JC_* ones. Co-Authored-By: jjohare --- config/custody/env-classes.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/custody/env-classes.json b/config/custody/env-classes.json index 06190e963..5d5c670fc 100644 --- a/config/custody/env-classes.json +++ b/config/custody/env-classes.json @@ -807,6 +807,8 @@ "_RB_MCP_DIR", "_RB_NS", "_RB_STAGING", + "_RC_MARKET", + "_RC_ON", "_RECONCILE_AGENTS", "_RECONCILE_COMMANDS", "_RECONCILE_SKILLS", From 84d8a4d6e6da28e68e38fd84abdc7bbd523c01bc Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sat, 3 Oct 2026 12:55:30 +0000 Subject: [PATCH 3/3] docs(adr): ADR-2121 notes the ruflo mods in the agentbox marketplace; re-verify 21 records (451823ca8) ADR-2121 records the shared marketplace, the shared function-hook switch, and the codex-style /opt registration beside factrail's cache install. It gains the three new files in verified_paths. The other 20 records were tripped by the new gate's lines in flake.nix, agentbox.toml, the schema, the entrypoint, the catalogue, env-classes and the invariants workflow; none changes meaning. Co-Authored-By: jjohare --- docs/adr/ADR-2002-aoe-token-auth-boundary.md | 6 +++++- docs/adr/ADR-2009-nip98-proxy-identity-boundary.md | 6 +++++- docs/adr/ADR-2012-relay-allowlist-only-ingress.md | 6 +++++- docs/adr/ADR-2013-loopback-publish-except-9096.md | 6 +++++- .../adr/ADR-2019-model-lifecycle-384-dim-freeze.md | 6 +++++- docs/adr/ADR-2020-capability-gating.md | 6 +++++- docs/adr/ADR-2023-loom-facade.md | 6 +++++- docs/adr/ADR-2028-vault-manifest-path-authority.md | 6 +++++- .../adr/ADR-2029-rune-markdown-tui-notes-window.md | 6 +++++- ...DR-2031-consultant-model-manifest-projection.md | 6 +++++- docs/adr/ADR-2033-deepsec-security-gate.md | 6 +++++- ...stration-proxy-behind-governed-memory-server.md | 6 +++++- docs/adr/ADR-2091-live-skill-router.md | 6 +++++- ...2092-govern-the-agent-and-command-registries.md | 6 +++++- docs/adr/ADR-2093-jev-verbatim-compaction.md | 6 +++++- ...05-agentbox-kind-bands-and-the-colloquy-move.md | 6 +++++- ...nstruction-tiers-and-claude-home-in-the-repo.md | 6 +++++- ...119-remove-retired-outliner-ontology-runtime.md | 6 +++++- docs/adr/ADR-2120-codex-daemon-package-volume.md | 6 +++++- .../ADR-2121-factrail-implements-jev-compaction.md | 14 ++++++++++++-- ...e-accounts-run-secrets-and-the-identity-port.md | 6 +++++- 21 files changed, 112 insertions(+), 22 deletions(-) diff --git a/docs/adr/ADR-2002-aoe-token-auth-boundary.md b/docs/adr/ADR-2002-aoe-token-auth-boundary.md index 818fd0b75..2491bf389 100644 --- a/docs/adr/ADR-2002-aoe-token-auth-boundary.md +++ b/docs/adr/ADR-2002-aoe-token-auth-boundary.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/nip98-proxy/proxy.mjs, scripts/aoe-curl.sh, flake.nix] owner: jjohare review_trigger: next image rebuild (activation), or any new consumer of :9095, or per-process isolation becoming available @@ -246,3 +246,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `flake.nix` (`dc91e092a`) ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2002 — AoE interaction plane requires token auth — loopback is not a boundary) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2002 — AoE interaction plane requires token auth — loopback is not a boundary) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2009-nip98-proxy-identity-boundary.md b/docs/adr/ADR-2009-nip98-proxy-identity-boundary.md index 55c2f4f95..d7d1868fa 100644 --- a/docs/adr/ADR-2009-nip98-proxy-identity-boundary.md +++ b/docs/adr/ADR-2009-nip98-proxy-identity-boundary.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/nip98-proxy/proxy.mjs, flake.nix, docs/INGRESS-identity.md] owner: jjohare review_trigger: A second identity ingress is proposed, or aoe serve stops binding loopback @@ -254,3 +254,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `flake.nix` (`dc91e092a`) ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2009 — The nip98-proxy is the fail-closed AoE identity boundary) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2009 — The nip98-proxy is the fail-closed AoE identity boundary) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2012-relay-allowlist-only-ingress.md b/docs/adr/ADR-2012-relay-allowlist-only-ingress.md index d3dd68643..27965b1ea 100644 --- a/docs/adr/ADR-2012-relay-allowlist-only-ingress.md +++ b/docs/adr/ADR-2012-relay-allowlist-only-ingress.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agentbox.toml, flake.nix] owner: jjohare review_trigger: ingress_policy changes from allowlist, or the ADR-040 D3 governance-publisher key-split lands @@ -326,3 +326,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2012 — Relay ingress is allowlist-only with no fallback and no auto-add) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2012 — Relay ingress is allowlist-only with no fallback and no auto-add) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2013-loopback-publish-except-9096.md b/docs/adr/ADR-2013-loopback-publish-except-9096.md index 578ff5606..15c72aa08 100644 --- a/docs/adr/ADR-2013-loopback-publish-except-9096.md +++ b/docs/adr/ADR-2013-loopback-publish-except-9096.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [scripts/ci/check-ports-loopback.sh, .github/workflows/invariants.yml, flake.nix, docker-compose.yml] owner: jjohare review_trigger: Any new entry on the SANCTIONED list, or a new compose overlay file @@ -327,3 +327,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `.github/workflows/invari ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2013 — Every compose publish binds 127.0.0.1 unless on the sanctioned-exposure list, CI-enforced across all overlays) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `invariants.yml` runs `tests/config/ruflo-console.test.mjs` and widens its path filter; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2013 — Every compose publish binds 127.0.0.1 unless on the sanctioned-exposure list, CI-enforced across all overlays) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2019-model-lifecycle-384-dim-freeze.md b/docs/adr/ADR-2019-model-lifecycle-384-dim-freeze.md index 0bff9bc77..824463d76 100644 --- a/docs/adr/ADR-2019-model-lifecycle-384-dim-freeze.md +++ b/docs/adr/ADR-2019-model-lifecycle-384-dim-freeze.md @@ -7,7 +7,7 @@ implementation_status: none activation_status: inactive supersedes: [] superseded_by: [] -verified_commit: 4ea3181b5296081411e95ca3687f03ed9aa11785 +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [mcp/servers/lib/aggregate-effectiveness.js, scripts/ruvector-sona-feeder.mjs, agentbox.toml] owner: jjohare review_trigger: A SONA binary with configurable embedding_dim (384-capable) ships, or a dimension migration is planned @@ -236,3 +236,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ab-poker-citizen role) `b41d9486c..4ea3181b5` changes one governed line: `agentbox.toml` `[poker_citizen].state` becomes a comment (the runner's default is the same path flag-off), for the poker seat's role (`4ea3181b5`). Nothing this record governs (ADR-2019 — Model-lifecycle freeze — 384-dim bge is the active column, SONA and attention-rerank stay off) reads that key. The decision holds. Re-verified by `git log b41d9486c..4ea3181b5 -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`4ea3181b5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`. Nothing this record governs (ADR-2019 — Model-lifecycle freeze — 384-dim bge is the active column, SONA and attention-rerank stay off) changes meaning. The decision holds. Re-verified by `git log 4ea3181b5..451823ca8 -- `. diff --git a/docs/adr/ADR-2020-capability-gating.md b/docs/adr/ADR-2020-capability-gating.md index 927e649fe..588782716 100644 --- a/docs/adr/ADR-2020-capability-gating.md +++ b/docs/adr/ADR-2020-capability-gating.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: live supersedes: [] superseded_by: [] -verified_commit: 4ea3181b5296081411e95ca3687f03ed9aa11785 +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agentbox.toml, skills/tree-search-coder/SKILL.md, services/agentbox-ops/src/bin/tree-search-cap.rs] owner: jjohare review_trigger: any new optional skill/feature block added to agentbox.toml, or any change to the tree-search-coder spend/route posture @@ -243,3 +243,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ab-poker-citizen role) `b41d9486c..4ea3181b5` changes one governed line: `agentbox.toml` `[poker_citizen].state` becomes a comment (the runner's default is the same path flag-off), for the poker seat's role (`4ea3181b5`). Nothing this record governs (ADR-2020 — Optional capabilities are manifest-gated and byte-identical-when-off; execution-gated tools are spend-capped and never auto-routed) reads that key. The decision holds. Re-verified by `git log b41d9486c..4ea3181b5 -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`4ea3181b5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`. Nothing this record governs (ADR-2020 — Optional capabilities are manifest-gated and byte-identical-when-off; execution-gated tools are spend-capped and never auto-routed) changes meaning. This record's rule is exercised directly: the gate defaults off, flake.nix bakes nothing under it when off, and the boot projection leaves `settings.json` and `installed_plugins.json` byte-identical when none of the three ids is present (asserted in `tests/config/ruflo-console.test.mjs`). The decision holds. Re-verified by `git log 4ea3181b5..451823ca8 -- `. diff --git a/docs/adr/ADR-2023-loom-facade.md b/docs/adr/ADR-2023-loom-facade.md index eeafb16f1..0667e213b 100644 --- a/docs/adr/ADR-2023-loom-facade.md +++ b/docs/adr/ADR-2023-loom-facade.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: live supersedes: [] superseded_by: [] -verified_commit: 4ea3181b5296081411e95ca3687f03ed9aa11785 +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agentbox.toml, mcp/servers/lib/ontology-retrieval.js] owner: jjohare review_trigger: model swap behind the Loom, or ADR-051 deferred-distillation MCP tools becoming a discrete server @@ -306,3 +306,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ab-poker-citizen role) `b41d9486c..4ea3181b5` changes one governed line: `agentbox.toml` `[poker_citizen].state` becomes a comment (the runner's default is the same path flag-off), for the poker seat's role (`4ea3181b5`). Nothing this record governs (ADR-2023 — The Loom is a façade — consumers hold the :8084 door and the model is a swappable URL behind it) reads that key. The decision holds. Re-verified by `git log b41d9486c..4ea3181b5 -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`4ea3181b5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`. Nothing this record governs (ADR-2023 — The Loom is a façade — consumers hold the :8084 door and the model is a swappable URL behind it) changes meaning. The decision holds. Re-verified by `git log 4ea3181b5..451823ca8 -- `. diff --git a/docs/adr/ADR-2028-vault-manifest-path-authority.md b/docs/adr/ADR-2028-vault-manifest-path-authority.md index 0b29dd87e..fc9d00372 100644 --- a/docs/adr/ADR-2028-vault-manifest-path-authority.md +++ b/docs/adr/ADR-2028-vault-manifest-path-authority.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: 4ea3181b5296081411e95ca3687f03ed9aa11785 +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agentbox.toml, setup/agentbox.default.toml, schema/agentbox.toml.schema.json, config/entrypoint-unified.sh, mcp/servers/lib/ontology-local.js, mcp/servers/lib/ontology-index-build.js, scripts/ontology-condense-scheduler.mjs, scripts/ontology-condense-refresh.sh, skills/podcast-knowledge-ingest/SKILL.md, skills/ontology-core/SKILL.md, skills/ontology-enrich/SKILL.md, skills/ontology-augment/SKILL.md, skills/web-summary/SKILL.md] owner: jjohare review_trigger: any new skill, MCP server, or supervised program that reads or writes authored markdown @@ -229,3 +229,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ab-poker-citizen role) `b41d9486c..4ea3181b5` changes one governed line: `agentbox.toml` `[poker_citizen].state` becomes a comment (the runner's default is the same path flag-off), for the poker seat's role (`4ea3181b5`). Nothing this record governs (ADR-2028 — `[vault]` is the single path authority for the authored corpus) reads that key. The decision holds. Re-verified by `git log b41d9486c..4ea3181b5 -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`4ea3181b5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`; `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before; the schema declares `toolchains.ruflo_console` (boolean, default false). Nothing this record governs (ADR-2028 — `[vault]` in agentbox.toml is the single path authority for the authored corpus; no consumer hard-codes a Logseq path) changes meaning. The decision holds. Re-verified by `git log 4ea3181b5..451823ca8 -- `. diff --git a/docs/adr/ADR-2029-rune-markdown-tui-notes-window.md b/docs/adr/ADR-2029-rune-markdown-tui-notes-window.md index 6fe3d04dd..691d06528 100644 --- a/docs/adr/ADR-2029-rune-markdown-tui-notes-window.md +++ b/docs/adr/ADR-2029-rune-markdown-tui-notes-window.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [flake.nix, lib/rune.nix, config/tmux-autostart.sh, config/tmux.conf, agentbox.toml, setup/agentbox.default.toml, schema/agentbox.toml.schema.json] owner: jjohare review_trigger: a Rune release that changes its CLI (`-w`), its keyboard-protocol requirement, or its licence; or the AoE plane absorbing note editing @@ -252,3 +252,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2029 — Rune is the first-class markdown TUI; tmux window 9 \"Notes\" opens it at the vault root) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure; the schema declares `toolchains.ruflo_console` (boolean, default false). Nothing this record governs (ADR-2029 — Rune is the first-class markdown TUI; tmux window 9 \"Notes\" opens it at the vault root) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2031-consultant-model-manifest-projection.md b/docs/adr/ADR-2031-consultant-model-manifest-projection.md index 2d5ba40ca..c5c4acb9c 100644 --- a/docs/adr/ADR-2031-consultant-model-manifest-projection.md +++ b/docs/adr/ADR-2031-consultant-model-manifest-projection.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: f93586b9e52fda0d0b367881e2d2ff3014509faf +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/entrypoint-unified.sh, services/agentbox-manifest/src/tui_write.rs, mcp/consultants/antigravity/server.js, skills/mcp.json] owner: jjohare review_trigger: any change to a consultant's default model, a Gemini model retirement, the 2027-01-01 Gemini tariff step, or a wizard that starts exposing the consultant model field @@ -172,3 +172,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `config/entrypoint-unifie ## Re-verification — 2026-10-03 (`f93586b9e52fda0d0b367881e2d2ff3014509faf`, custody W2b/W4) Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the sidechain state move). `config/entrypoint-unified.sh` changes only in three custody blocks (ADR-2122, design 3.2). (1) A new at-rest step before Phase 3: `ab_custody_migrate` when `[security].role_isolation` is on, otherwise `ab_custody_revert`, which changes nothing on a volume that was never migrated (`tests/config/role-custody-migrate.test.sh` shows the stat set, ctime included, byte-identical). (2) Under the flag only, the volume-root chown loop skips `/var/lib/agentbox/secrets`. (3) After the identity bootstrap, the identity file goes to ab-identity 0400 under the flag; with the flag off, the devuser 0600 statements are unchanged. The consultant-model projection block is untouched. The decision holds. Re-verified by `git log dc91e092a..f93586b9e -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`f93586b9e..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before. Nothing this record governs (ADR-2031 — Consultant model selection is projected from the manifest at boot; environment wins, TUI preserves the operator's choice, and tariffs are dated) changes meaning. The decision holds. Re-verified by `git log f93586b9e..451823ca8 -- `. diff --git a/docs/adr/ADR-2033-deepsec-security-gate.md b/docs/adr/ADR-2033-deepsec-security-gate.md index 1bca17012..c95636fbb 100644 --- a/docs/adr/ADR-2033-deepsec-security-gate.md +++ b/docs/adr/ADR-2033-deepsec-security-gate.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: staged supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [flake.nix, agentbox.toml, schema/agentbox.toml.schema.json, scripts/agentbox-config-validate.js, management-api/lib/system-manifest.js, skills/build-with-quality/scripts, skills/build-with-quality/references/deepsec-security-gate.md, .github/workflows/deepsec.yml] owner: jjohare review_trigger: a deepsec major version, a change to its CLI exit-code contract or model-route schema, any new model route, or the first paid full-repo run @@ -293,3 +293,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2033 — deepsec is the executed Security gate of build-with-quality, baked as a manifest-gated CLI under a names-only credential policy) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure; `system-manifest.js` adds the rebuild-class `ruflo-console` catalogue entry; the schema declares `toolchains.ruflo_console` (boolean, default false). Nothing this record governs (ADR-2033 — deepsec is the executed Security gate of build-with-quality, baked as a manifest-gated CLI under a names-only credential policy) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2082-orchestration-proxy-behind-governed-memory-server.md b/docs/adr/ADR-2082-orchestration-proxy-behind-governed-memory-server.md index cd61498bd..709f9d1bc 100644 --- a/docs/adr/ADR-2082-orchestration-proxy-behind-governed-memory-server.md +++ b/docs/adr/ADR-2082-orchestration-proxy-behind-governed-memory-server.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: f93586b9e52fda0d0b367881e2d2ff3014509faf +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [mcp/servers/lib/orchestration-proxy.js, mcp/servers/ruvector-mcp.cjs, mcp/servers/lib/ruvector-gates.js, config/entrypoint-unified.sh] owner: jjohare review_trigger: next image rebuild (activation), a ruflo major bump that renames the swarm/agent/task/coordination tools, or any proposal to forward a memory_* tool @@ -154,3 +154,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `config/entrypoint-unifie ## Re-verification — 2026-10-03 (`f93586b9e52fda0d0b367881e2d2ff3014509faf`, custody W2b/W4) Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the sidechain state move). `config/entrypoint-unified.sh` changes only in three custody blocks (ADR-2122, design 3.2). (1) A new at-rest step before Phase 3: `ab_custody_migrate` when `[security].role_isolation` is on, otherwise `ab_custody_revert`, which changes nothing on a volume that was never migrated (`tests/config/role-custody-migrate.test.sh` shows the stat set, ctime included, byte-identical). (2) Under the flag only, the volume-root chown loop skips `/var/lib/agentbox/secrets`. (3) After the identity bootstrap, the identity file goes to ab-identity 0400 under the flag; with the flag off, the devuser 0600 statements are unchanged. The orchestration-proxy registration is untouched. The decision holds. Re-verified by `git log dc91e092a..f93586b9e -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`f93586b9e..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before. Nothing this record governs (ADR-2082 — The governed claude-flow server forwards orchestration tools to a filtered ruflo child; memory never crosses) changes meaning. The decision holds. Re-verified by `git log f93586b9e..451823ca8 -- `. diff --git a/docs/adr/ADR-2091-live-skill-router.md b/docs/adr/ADR-2091-live-skill-router.md index dd0cd6fb7..b98031bbd 100644 --- a/docs/adr/ADR-2091-live-skill-router.md +++ b/docs/adr/ADR-2091-live-skill-router.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: f93586b9e52fda0d0b367881e2d2ff3014509faf +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/hooks/lib/skill-route.cjs, config/hooks/skill-route.cjs, skills/skill-router/scripts/route.mjs, config/entrypoint-unified.sh, tests/config/skill-route.test.js] owner: jjohare review_trigger: the first project that needs a per-project routing bypass (ADR-2090), a Jev model change, or a measured runtime-path accuracy below 85% on the 40-item set @@ -193,3 +193,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `config/entrypoint-unifie ## Re-verification — 2026-10-03 (`f93586b9e52fda0d0b367881e2d2ff3014509faf`, custody W2b/W4) Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the sidechain state move). `config/entrypoint-unified.sh` changes only in three custody blocks (ADR-2122, design 3.2). (1) A new at-rest step before Phase 3: `ab_custody_migrate` when `[security].role_isolation` is on, otherwise `ab_custody_revert`, which changes nothing on a volume that was never migrated (`tests/config/role-custody-migrate.test.sh` shows the stat set, ctime included, byte-identical). (2) Under the flag only, the volume-root chown loop skips `/var/lib/agentbox/secrets`. (3) After the identity bootstrap, the identity file goes to ab-identity 0400 under the flag; with the flag off, the devuser 0600 statements are unchanged. The skill-router projection is untouched. The decision holds. Re-verified by `git log dc91e092a..f93586b9e -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`f93586b9e..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before. Nothing this record governs (ADR-2091 — Route each turn to a skill with one typed judgement, failing open to the table) changes meaning. The decision holds. Re-verified by `git log f93586b9e..451823ca8 -- `. diff --git a/docs/adr/ADR-2092-govern-the-agent-and-command-registries.md b/docs/adr/ADR-2092-govern-the-agent-and-command-registries.md index dc241f0ec..bbc231d6d 100644 --- a/docs/adr/ADR-2092-govern-the-agent-and-command-registries.md +++ b/docs/adr/ADR-2092-govern-the-agent-and-command-registries.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agents/registered-agents.txt, scripts/reconcile-agents.sh, scripts/reconcile-commands.sh, scripts/project-skill-roots.mjs, config/registered-commands.txt, config/entrypoint-unified.sh, flake.nix, tests/config/agent-reconcile.test.sh] owner: jjohare review_trigger: a new subagent worth always-loading, or evidence the router surfaces baked-but-unregistered skills too slowly @@ -243,3 +243,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. ruflo 3.51.1 `init` still writes its agent and command template trees, which the reconcile scripts govern unchanged; the image's own `init` calls now pass `--no-mods` (`config/agentbox-aliases.sh`), so they also leave the mods' project settings unwritten (`tests/config/ruflo-init-no-mods.test.sh`). Nothing this record governs (ADR-2092 — Agents and slash-commands get the same manifest governance skills already have) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2092 — Agents and slash-commands get the same manifest governance skills already have) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2093-jev-verbatim-compaction.md b/docs/adr/ADR-2093-jev-verbatim-compaction.md index 65ad7974b..4e1c0dd89 100644 --- a/docs/adr/ADR-2093-jev-verbatim-compaction.md +++ b/docs/adr/ADR-2093-jev-verbatim-compaction.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [lib/factrail.nix, config/entrypoint-unified.sh, lib/claude-code-binary.nix] owner: jjohare review_trigger: the first measured residency bill that exceeds the summary path's re-read savings, a Claude Code function-hook API change, a request to fence a class other than email, or a change to ADR-2121 (its implementation) @@ -337,3 +337,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `f93586b9e..daba195e5`: `lib/claude-code-binary.nix` moves Claude Code 2.1.285 → 2.1.288 (`c23592687`); both per-arch hashes equal Anthropic's 2.1.288 manifest checksums. The factrail plugin baked at the pinned rev (`share/factrail/plugin`) passes `claude plugin validate --strict` under the 2.1.288 binary with the hook surface it reports under 2.1.285, unchanged: session.start, tool.call, skill.prompt, command.run, session.compact, turn.start, turn.complete; calls include `$.session.compact` and `$.command.register`. Nothing this record governs (ADR-2093 — Compact context by Jev judgement, verbatim, with email fenced out and a switch) changes meaning. The decision holds. Re-verified by `git log f93586b9e..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before. Nothing this record governs (ADR-2093 — Compact context by Jev judgement, verbatim, with email fenced out and a switch) changes meaning. The compaction policy is untouched: CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is still set whenever `[features.jev_compaction]` is on, and is now also kept on for the console. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2105-agentbox-kind-bands-and-the-colloquy-move.md b/docs/adr/ADR-2105-agentbox-kind-bands-and-the-colloquy-move.md index 762d7adb1..add9e7a52 100644 --- a/docs/adr/ADR-2105-agentbox-kind-bands-and-the-colloquy-move.md +++ b/docs/adr/ADR-2105-agentbox-kind-bands-and-the-colloquy-move.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: staged supersedes: [] superseded_by: [] -verified_commit: 4ea3181b5296081411e95ca3687f03ed9aa11785 +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [crates/colloquy/colloquy-nostr/src/kinds.rs, docs/PROTOCOL-registry.md, services/nostr-pod-bridge/src/colloquy_publish.rs, agentbox.toml] owner: jjohare review_trigger: the next agentbox Nostr kind allocation, or any change to the band table in docs/PROTOCOL-registry.md @@ -220,3 +220,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ab-poker-citizen role) `b41d9486c..4ea3181b5` changes one governed line: `agentbox.toml` `[poker_citizen].state` becomes a comment (the runner's default is the same path flag-off), for the poker seat's role (`4ea3181b5`). Nothing this record governs (ADR-2105 — The agentbox 38xxx bands below 38400 are all reserved, so colloquy and settlement move to 38400-38499) reads that key. The decision holds. Re-verified by `git log b41d9486c..4ea3181b5 -- `. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`4ea3181b5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`. Nothing this record governs (ADR-2105 — The agentbox 38xxx bands below 38400 are all reserved, so colloquy and settlement move to 38400-38499) changes meaning. The decision holds. Re-verified by `git log 4ea3181b5..451823ca8 -- `. diff --git a/docs/adr/ADR-2118-own-the-instruction-tiers-and-claude-home-in-the-repo.md b/docs/adr/ADR-2118-own-the-instruction-tiers-and-claude-home-in-the-repo.md index b738125ba..f066a9b9b 100644 --- a/docs/adr/ADR-2118-own-the-instruction-tiers-and-claude-home-in-the-repo.md +++ b/docs/adr/ADR-2118-own-the-instruction-tiers-and-claude-home-in-the-repo.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/instructions, services/agentbox-manifest/src/instructions.rs, services/agentbox-manifest/src/cred_sync.rs, config/entrypoint-unified.sh, agentbox.sh, flake.nix, docker-compose.yml, docker-compose.override.yml, docker-compose.hp.yml, tests/config/claude-home-migration.test.sh, tests/config/compose-persistence.test.cjs] owner: jjohare review_trigger: the connected node runs migrate-claude-home; or Claude Code starts reading AGENTS.md natively (drop the @AGENTS.md wrappers and the embed); or a Claude Code release changes where credentials live @@ -144,3 +144,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2118 — Own the instruction tiers and the Claude home in the repo) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2118 — Own the instruction tiers and the Claude home in the repo) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2119-remove-retired-outliner-ontology-runtime.md b/docs/adr/ADR-2119-remove-retired-outliner-ontology-runtime.md index 08c345ece..249ebff78 100644 --- a/docs/adr/ADR-2119-remove-retired-outliner-ontology-runtime.md +++ b/docs/adr/ADR-2119-remove-retired-outliner-ontology-runtime.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [flake.nix, lib/ontology-tools.nix, services/ontology-tools, services/agentbox-mcp/src/web_summary, skills/ontology-core, skills/ontology-enrich, dream.config.json] owner: jjohare review_trigger: commit and rebuild the image; or introduce a corpus writer or output format @@ -110,3 +110,7 @@ Tripped by the W10 gap fixes on `custody/integration`. `flake.nix` (`dc91e092a`) ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2119 — Remove the retired outliner ontology runtime) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2119 — Remove the retired outliner ontology runtime) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2120-codex-daemon-package-volume.md b/docs/adr/ADR-2120-codex-daemon-package-volume.md index bb07ce80c..d2b465ecd 100644 --- a/docs/adr/ADR-2120-codex-daemon-package-volume.md +++ b/docs/adr/ADR-2120-codex-daemon-package-volume.md @@ -7,7 +7,7 @@ implementation_status: complete activation_status: live supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [agentbox.sh, config/entrypoint-unified.sh, flake.nix, docker-compose.yml, scripts/refresh-compose.sh, tests/config/compose-persistence.test.cjs, tests/config/refresh-compose.test.cjs] owner: jjohare review_trigger: commit verification and rebuild; or change Codex daemon packaging @@ -123,3 +123,7 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2120 — Give Codex daemon packages executable persistent storage) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure. Nothing this record governs (ADR-2120 — Give Codex daemon packages executable persistent storage) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. diff --git a/docs/adr/ADR-2121-factrail-implements-jev-compaction.md b/docs/adr/ADR-2121-factrail-implements-jev-compaction.md index e0e41edb5..6f51f8d4f 100644 --- a/docs/adr/ADR-2121-factrail-implements-jev-compaction.md +++ b/docs/adr/ADR-2121-factrail-implements-jev-compaction.md @@ -7,8 +7,8 @@ implementation_status: complete activation_status: staged supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a -verified_paths: [lib/factrail.nix, lib/lockfiles/factrail-57ac25b5.Cargo.lock, lib/claude-code-binary.nix, config/entrypoint-unified.sh, config/claude-plugins/.claude-plugin/marketplace.json, scripts/factrail-store-migrate.mjs, tests/config/factrail-store-migrate.test.mjs, tests/config/factrail-projection.test.sh, schema/agentbox.toml.schema.json] +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 +verified_paths: [lib/factrail.nix, lib/lockfiles/factrail-57ac25b5.Cargo.lock, lib/claude-code-binary.nix, config/entrypoint-unified.sh, config/claude-plugins/.claude-plugin/marketplace.json, scripts/factrail-store-migrate.mjs, tests/config/factrail-store-migrate.test.mjs, tests/config/factrail-projection.test.sh, schema/agentbox.toml.schema.json, scripts/bake-ruflo-console.sh, scripts/ruflo-console-project.mjs, tests/config/ruflo-console.test.mjs] owner: jjohare review_trigger: a factrail rev bump in lib/factrail.nix, the end of the post-rebuild residency soak, a Claude Code function-hook API change, or a decision to train a local judge on recorded Jev decisions repo: agentbox @@ -197,3 +197,13 @@ Tripped by `f93586b9e` (custody W2b and W4: the at-rest migrate/revert and the s ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `b41d9486c..daba195e5`: `lib/claude-code-binary.nix` moves Claude Code 2.1.285 → 2.1.288 (`c23592687`); both per-arch hashes equal Anthropic's 2.1.288 manifest checksums. The factrail plugin baked at the pinned rev (`share/factrail/plugin`) passes `claude plugin validate --strict` under the 2.1.288 binary with the hook surface it reports under 2.1.285, unchanged: session.start, tool.call, skill.prompt, command.run, session.compact, turn.start, turn.complete; calls include `$.session.compact` and `$.command.register`. Nothing this record governs (ADR-2121 — Implement Jev compaction with factrail — fact rails in Rust, baked at a pinned commit) changes meaning. The decision holds. Re-verified by `git log b41d9486c..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification and note — 2026-10-03 (ruflo mods join the `agentbox` marketplace, `451823ca8`) + +`daba195e5..451823ca8`: the `agentbox` directory marketplace this record introduced for factrail now also carries ruflo-console, ruflo-mods and ruflo-swarm. These are ruflo's function-hook mods, baked from the ruflo v3.51.1 tag (flake input `rufloConsole`, `09a1cb0`) behind `[toolchains].ruflo_console`, default off, rebuild-class. Three consequences for this record: + +1. **Shared marketplace.** Factrail's gate-off branch removed the whole `agentbox` marketplace. It now does so only when `[toolchains].ruflo_console` is also off, and the ruflo block registers the marketplace itself when on. `marketplace.json` lists the three mods beside factrail. `scripts/bake-ruflo-console.sh` fails the build if a baked `plugin.json` version differs from the catalogue. +2. **Shared function-hook switch.** `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` is set when either gate is on and cleared only when both are off. +3. **A second registration pattern in one marketplace.** Factrail is still installed with `claude plugin install` into the persistent cache, with the content-digest and config-stamp reinstall. The ruflo mods follow the codex-plugin-cc pattern instead: `scripts/ruflo-console-project.mjs` registers them in `installed_plugins.json` at their stable `/opt/agentbox/config/claude-plugins/` paths, so no cache copy can go stale. It rewrites a wrong path or version every boot, writes `pluginConfigs` cli=`ruflo` (the baked bin; upstream's npx-offline default fails ENOTCACHED on a fresh npm cache), and removes the three ids when the gate is off. + +The schema and entrypoint diffs are otherwise limited to the new gate. Factrail's pin, projection and userConfig keys are unchanged (`tests/config/factrail-projection.test.sh` passes). The three new files join `verified_paths`. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `. Nix was not evaluated in this container (no `nix` binary); the image is unverified until the owner's rebuild. diff --git a/docs/adr/ADR-2122-role-service-accounts-run-secrets-and-the-identity-port.md b/docs/adr/ADR-2122-role-service-accounts-run-secrets-and-the-identity-port.md index 85f841802..ddbe4378b 100644 --- a/docs/adr/ADR-2122-role-service-accounts-run-secrets-and-the-identity-port.md +++ b/docs/adr/ADR-2122-role-service-accounts-run-secrets-and-the-identity-port.md @@ -7,7 +7,7 @@ implementation_status: partial activation_status: inactive supersedes: [] superseded_by: [] -verified_commit: daba195e5671cdf3906095965d323cef3f80aa3a +verified_commit: 451823ca8ec0b5452ceb8fdc52e777f77a2bbc43 verified_paths: [config/role-accounts.json, services/agentbox-manifest/src/role_accounts.rs, services/agentbox-manifest/src/main.rs, lib/agentbox-manifest.nix, config/lib/role-custody.sh, config/entrypoint-unified.sh, flake.nix, docker-compose.yml, agentbox.toml, setup/agentbox.default.toml, schema/agentbox.toml.schema.json, management-api/lib/system-manifest.js, tests/config/role-isolation-supervisor.test.sh, tests/config/role-secrets-delivery.test.sh, tests/config/role-isolation-boot.test.sh, tests/config/fixtures/role-isolation/supervisord.conf, config/custody/env-classes.json, scripts/ci/env-secret-inventory.js, management-api/lib/role-secret.js, services/nostr-pod-bridge/src/role_secret.rs, services/nostr-pod-bridge/src/bootstrap.rs, tests/runtime-contract/RC-X1-06.sh, config/custody/identity-port-acl.json, services/nostr-pod-bridge/src/identity_port/mod.rs, services/nostr-pod-bridge/src/identity_port/server.rs, management-api/lib/pod-signer.js, scripts/activation/role-isolation-rehearsal.sh, scripts/activation/role-isolation-rehearsal.host.sh, tests/config/role-isolation-rehearsal.test.sh, config/bake-devuser-privilege.sh, tests/runtime-contract/RC-X1-07.sh, tests/security/compose-role-env.test.mjs, docker-compose.override.yml, docker-compose.hp.yml, tests/config/role-custody-migrate.test.sh, config/sidechain/run-producer.sh, config/sidechain/run-faucet.sh, config/sidechain/mirror-sync.sh] owner: jjohare review_trigger: the role-isolation rehearsal (scripts/activation/role-isolation-rehearsal.sh) passing or failing on a rebuilt image; a new secret-bearing supervisor program; a new [sidechain.] chain; a change to the host docker gid; the identity port's consumer cutover (W3b: JunkieJarvis, the mirror hook, the gateway, dream-engine); a change to config/custody/identity-port-acl.json @@ -537,3 +537,7 @@ Tripped by `ad5d0b91a`, a shellcheck-only change to `config/lib/role-custody.sh` ### Re-verification — 2026-10-03 (ruflo 3.51.1, Claude Code 2.1.288) `e3b06d688..daba195e5`: `flake.nix` changes only the `rufloPkg` pin: version 3.51.1, its lock (`config/npm-locks/ruflo-3.51.1.package-lock.json`) and both hashes (`daba195e5`), with the rationale comment. The ruflo closure's bins and extraBins aliases, every gate and every other derivation are unchanged. Nothing this record governs (ADR-2122 — Role service accounts, /run/secrets, and the identity port) changes meaning. The decision holds. Re-verified by `git log e3b06d688..daba195e5 -- `. Nix was not evaluated here; the image is unverified until the host rebuild. + +### Re-verification — 2026-10-03 (ruflo-console gate, `451823ca8`) + +`daba195e5..451823ca8`: `agentbox.toml` adds `[toolchains].ruflo_console = false`; `env-classes.json` classifies the new boot locals `_RC_ON` and `_RC_MARKET` as NON_SECRET beside factrail's `_JC_*` (neither carries a secret or reaches a role); `config/entrypoint-unified.sh` adds the ruflo-console boot block after factrail's, and factrail's function-hook switch and `agentbox` marketplace removal also respect the new gate; with it off both behave as before; `flake.nix` adds the pinned `rufloConsole` input (ruflo v3.51.1, files-only), the `rufloConsolePlugins` bake (only the three mod directories) and its copy into the `agentbox` marketplace under `[toolchains].ruflo_console`, and lets that gate pull in the ruflo closure; `system-manifest.js` adds the rebuild-class `ruflo-console` catalogue entry; the schema declares `toolchains.ruflo_console` (boolean, default false). Nothing this record governs (ADR-2122 — Role service accounts, /run/secrets, and the identity port) changes meaning. The decision holds. Re-verified by `git log daba195e5..451823ca8 -- `.