Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/agentic-kit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const PLUMBING = Object.assign(Object.create(null), {
'mcp': () => import('../src/commands/x/mcp.mjs'),
'host': () => import('../src/commands/x/host.mjs'),
'reference': () => import('../src/commands/x/reference.mjs'),
'ruflo-mcp': () => import('../src/commands/x/ruflo-mcp.mjs'),
'statusline': () => import('../src/commands/x/statusline.mjs'),
'verify': () => import('../src/commands/x/verify.mjs'),
});
Expand Down Expand Up @@ -176,7 +177,7 @@ async function main() {
// setup and host own complete mutation/reporting flows. Running the generic
// nudge after a declined trust preflight could write version-cache state and
// violate their "before any changes" boundary.
if (!values.json && !values['dry-run'] && !['sync', 'usage', 'setup', 'host'].includes(cmd)) {
if (!values.json && !values['dry-run'] && !['sync', 'usage', 'setup', 'host', 'ruflo-mcp'].includes(cmd)) {
try {
const { driftReport } = await import('../src/lib/versions.mjs');
for (const r of await driftReport()) {
Expand Down
14 changes: 14 additions & 0 deletions docs/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ the project Claude-to-Codex MCP bridge, the user-scope Codex-to-Ruflo MCP
registration, and the AQE Codex integration that project setup will create.
Agentic-kit does not alter Codex's sandbox or approval policy.

Codex also retains exclusive ownership of third-party plugins. Agentic-kit never
installs or enables a Codex plugin (including `security-guidance`), and setup/sync
never rewrites Codex's plugin tables or cache. `ak status` only reads enabled
bundles to report known hook and skill portability problems.

All enabled hosts converge on the same project-scoped Ruflo memory contract.
Claude receives the absolute `CLAUDE_FLOW_DB_PATH` in project settings. Codex's
user-scoped Ruflo MCP registration launches `ak x ruflo-mcp`, which derives the
pin from the workspace at process start. OpenCode's managed MCP gateway and
lifecycle bridge receive its project directory and set the same absolute pin.
Ruflo's native bridge may write `.swarm/agentdb-memory.db` beside the pinned
`.swarm/memory.db`; that sibling is the active native store, not configuration
drift. `ak x verify memory` proves the actual writer with a disposable round trip.

OpenCode's user-scope manifest names all four wildcard tool approvals, the
Ruflo and optional Brain MCP registrations, the lifecycle plugin, and the
managed agent/skill/guidance projection. These are workspace-trust grants, not
Expand Down
4 changes: 2 additions & 2 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ ak sync # apply it
| opencode: `status` reports a later `opencode.jsonc` override | stock OpenCode loads that file after `opencode.json`, so it can shadow the exact MCP/permission values ak receipts; ak cannot verify JSONC without rewriting user comments | merge the Agentic Kit entries into the later file and remove the duplicate override, or keep the override and use direct user-managed wiring; ak preserves both files and does not deploy its gateway against ambiguous effective config |
| opencode: an agent/skill/plugin file you created yourself keeps ak's version away | deploys are no-clobber: only exact receipt-matching bytes are repairable; an unreceipted or edited destination is user-owned and preserved (`status` reports it as `foreign`) | rename yours (or remove it and run `ak sync` to get ak's managed copy) |
| opencode: `status` says `no ruflo catalog source` | the agent/skill catalog resolves override → `$RUFLO_REPO` → claude marketplace clone → `@claude-flow/cli` (direct, then nested under ruflo) — all missing | install ruflo (`ak setup` does), or point `integrations.ownership.opencode.catalogDir` / `$RUFLO_REPO` at a ruflo checkout |
| `ruflo memory store` says OK but reads return nothing | Absolute-DB-path pin missing, or an older check looked only at `.swarm/memory.db` while the native bridge selected `.swarm/agentdb-memory.db` | Run `ak x verify memory` for an isolated store/retrieve/on-disk/purge proof; `ak setup` re-pins and verifies the runtime-selected store |
| `status` shows a `codex-plugins` warning such as unknown field `_note` | An enabled plugin's newest cached hook file does not match Codex's `description` + `hooks` top-level schema | Open Codex `/plugins`, refresh or disable the named plugin, then start a new session. `ak sync` deliberately does not rewrite Codex-owned cache |
| `ruflo memory store` says OK but reads return nothing | Absolute project pin missing, a legacy Codex MCP launcher inherited the wrong cwd, or an older check looked only at `.swarm/memory.db` while the native bridge selected `.swarm/agentdb-memory.db` | Run `ak sync` to migrate an ak-owned Codex registration, then `ak x verify memory` for an isolated store/retrieve/on-disk/purge proof; `ak setup` pins Claude, Codex, and OpenCode to the project while accepting the runtime-selected native sibling |
| `status` shows a `codex-plugins` warning | An enabled plugin's newest cached hooks or skills fail a known Codex compatibility check. Examples include Claude-only hook metadata, a `SKILL.md` without YAML frontmatter, and `security-guidance` 2.0.7 emitting a Stop result Codex rejects | Open Codex `/plugins`, refresh or disable the named plugin, then start a new session. `ak setup` and `ak sync` never install, enable, refresh, or rewrite Codex-owned plugins |
| `status` shows a `memory-pin` warning | `CLAUDE_FLOW_DB_PATH` is pinned to a dead or foreign path, so every memory op targets the wrong DB ("Database not initialized" beside a healthy in-repo DB). The pin may be deliberate, so `sync` never touches it | repoint (or remove) the pin in `.claude/settings.local.json` `env` |
| Want to run `ak sync` but Claude/Codex/OpenCode sessions are open in other terminals | Upgrade-bearing syncs stop **all** ruflo daemons machine-wide and swap the global npm trees live sessions execute hooks/statusline/MCP calls from; converged syncs touch nothing | `ak sync --dry-run` first; a `versions` row means idle the other sessions or use `ak sync --no-upgrade`; see [Running `ak sync` while sessions are live](UPGRADING.md#running-ak-sync-while-sessions-are-live) |
| Suspicious token burn | Background automation vs interactive usage | ask Claude to run the **ruflo-token-audit** skill (deployed by `setup`) |
Expand Down
23 changes: 18 additions & 5 deletions docs/adr/0016-capability-driven-integration-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[ADR-0020](0020-ga-stable-surfaces.md); closed-registry clause superseded by
[ADR-0029](0029-host-adapter-extension-point.md)
- **Date:** 2026-07-28
- **Updated:** 2026-08-15
- **Updated:** 2026-08-20
- **Update note:** Added read-only Codex plugin-hook compatibility facts,
runtime-selected Ruflo project-memory store proofs, and the non-correlatable
OpenRouter account-analytics boundary; removed the pre-GA compatibility command,
Expand All @@ -25,6 +25,12 @@
flag; every other property that clause protected (zero-runtime-dependency,
offline-first normal operation, no in-process third-party code) remains
intact.
2026-08-20: the read-only Codex plugin fact now covers portable skill
frontmatter and version-bounded runtime-output advisories as well as hook
documents. Setup and sync explicitly install or enable no Codex plugins. The
project-memory pin is now projected through host-specific launch context:
Claude project env, Codex's workspace-aware MCP launcher, and OpenCode's
project-aware MCP/lifecycle processes.
- **Deciders:** agentic-kit maintainers
- **Related:** [ADR-0001](0001-one-routing-policy-many-projections.md),
[ADR-0003](0003-auto-seed-dual-host-provenance.md),
Expand Down Expand Up @@ -222,10 +228,11 @@ weakening it.

#### Externally-owned plugin cache and runtime-selected memory

Codex plugin configuration and `~/.codex/plugins/cache` are externally owned. Agentic-kit reads
every explicitly enabled plugin's newest cached manifest, follows its declared hook paths (or the
default `hooks/hooks.json`), and validates the Codex hook-file contract. It does not refresh,
rewrite, delete, or adopt any plugin cache entry. An invalid newest cached bundle is a diagnostic fact
Codex plugin configuration and `~/.codex/plugins/cache` are externally owned. Agentic-kit installs
or enables no Codex plugin. It reads every explicitly enabled plugin's newest cached manifest,
follows its declared hook paths (or the default `hooks/hooks.json`), validates the Codex hook-file
contract and portable skill frontmatter, and applies version-bounded runtime-output advisories. It
does not refresh, rewrite, delete, or adopt any plugin cache entry. An invalid newest cached bundle is a diagnostic fact
with native remediation: open Codex `/plugins` to refresh or disable the plugin, then start a new
session. The row has no `sync` fix.

Expand All @@ -237,6 +244,12 @@ compatibility store may coexist without representing drift. Read-only status ide
writer and counts observable entries. Setup and `ak x verify memory` prove persistence by storing
a disposable row, locating it in the runtime-selected store, retrieving it through the real CLI,
and removing it. File or package presence alone is never reported as a persistence proof.
Claude carries the absolute compatibility path in project settings. Codex's user-scoped MCP entry
uses an agentic-kit launcher that derives the same absolute pin from each runtime workspace;
agentic-kit migrates only a legacy entry it previously registered and preserves user-owned Codex
entries. OpenCode's receipt-owned gateway and lifecycle processes set their cwd and pin from the
host-provided project directory. These are host projections of one project-memory contract, not
separate stores.

### 5. Normalize field-level facts before rendering conclusions

Expand Down
3 changes: 2 additions & 1 deletion src/commands/setup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import * as adb from '../lib/agentdb.mjs';
import { readJson, writeJsonWithBackup } from '../lib/settings.mjs';
import { withDb } from '../lib/sqlite.mjs';
import { findMemoryEntry } from '../lib/project-memory.mjs';
import { projectMemoryEnv } from '../lib/ruflo-memory.mjs';
import {
setupTrustManifest, trustManifestLines,
} from '../lib/trust-manifest.mjs';
Expand Down Expand Up @@ -329,7 +330,7 @@ export async function run_project({ flags, cfg, trustDisclosed = false }) {
ok(`CLAUDE_FLOW_DB_PATH pinned → ${dbPath}`);

// 5. activate memory + swarm with the pin exported
const env = { CLAUDE_FLOW_DB_PATH: dbPath };
const env = projectMemoryEnv(root);
(await runCmd('ruflo', ['memory', 'init'], { cwd: root, env })).code === 0
? ok('memory initialized') : warn('ruflo memory init failed');
(await runCmd('ruflo', ['swarm', 'init', '--v3-mode'], { cwd: root, env })).code === 0
Expand Down
21 changes: 14 additions & 7 deletions src/commands/status.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -553,10 +553,16 @@ export async function collect({ pkgRoot, cwd = process.cwd() }) {
// reverse bridge: ruflo MCP → codex (a codex-driven session reaches ruflo's
// tools). The mirror of the claude→codex row above; makes the bridge two-way.
try {
const { registered, owned } = rufloCodexMcpStatus(cfg);
if (registered) {
const { registered, owned, command, args } = rufloCodexMcpStatus(cfg);
const workspacePinned = command === 'ak'
&& JSON.stringify(args) === JSON.stringify(['x', 'ruflo-mcp']);
if (registered && owned && !workspacePinned) {
rows.push(row('codex-mcp', 'warn',
'ak-owned ruflo MCP in codex uses the legacy cwd-only launcher',
'sync migrates it to workspace-pinned project memory'));
} else if (registered) {
rows.push(row('codex-mcp', 'ok',
`ruflo MCP registered in codex ([mcp_servers.ruflo])${owned ? '' : ' — pre-existing (not ak-managed)'}`));
`ruflo MCP registered in codex ([mcp_servers.ruflo])${owned ? ' — workspace memory pinned' : ' — pre-existing (not ak-managed)'}`));
} else if (await have('codex')) {
rows.push(row('codex-mcp', 'warn', 'codex enabled but ruflo MCP not registered in codex',
'sync registers the ruflo MCP into codex'));
Expand All @@ -566,9 +572,10 @@ export async function collect({ pkgRoot, cwd = process.cwd() }) {
}
}

// Codex owns plugin installation and refresh. Inspect every explicitly
// enabled cached plugin, but never attach a sync fix: the supported repair
// surface is Codex's /plugins UI followed by a fresh session.
// Codex owns plugin installation, enablement, and refresh. Inspect every
// explicitly enabled cached plugin's hooks and skills, but never attach a
// sync fix: the supported repair surface is Codex's /plugins UI followed by
// a fresh session.
try {
const plugins = inspectCodexPlugins();
if (plugins.enabled.length && plugins.issues.length) {
Expand All @@ -578,7 +585,7 @@ export async function collect({ pkgRoot, cwd = process.cwd() }) {
} else if (plugins.enabled.length) {
const versions = plugins.plugins.map((plugin) => `${plugin.ref} (${plugin.version})`).join(', ');
rows.push(row('codex-plugins', 'ok',
`${plugins.enabled.length} enabled Codex plugin(s); newest cached hook configs compatible (${versions})`));
`${plugins.enabled.length} enabled Codex plugin(s); newest cached hooks and skills pass known compatibility checks (${versions})`));
}
} catch (e) {
rows.push(row('codex-plugins', 'warn', `Codex plugin check unavailable: ${e.message}`));
Expand Down
27 changes: 27 additions & 0 deletions src/commands/x/ruflo-mcp.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Internal stdio launcher used by Codex's user-scoped MCP registration. The
// registration is global, but every process launch is pinned to the workspace
// Codex started it from, so projects never share a database accidentally.
import { spawn } from 'node:child_process';
import { rufloMcpLaunch } from '../../lib/ruflo-memory.mjs';

export const options = {};
export const help = `ak x ruflo-mcp — internal workspace-aware Ruflo MCP launcher

Used by agentic-kit's Codex registration. It pins CLAUDE_FLOW_DB_PATH to the
current repository before starting Ruflo's stdio MCP server.

Examples:
ak x ruflo-mcp start the stdio server (normally invoked by Codex)`;

export async function run() {
const spec = rufloMcpLaunch();
return new Promise((resolve) => {
const child = spawn(spec.command, spec.args, {
cwd: spec.cwd,
env: spec.env,
stdio: 'inherit',
});
child.once('error', () => resolve(1));
child.once('exit', (code) => resolve(code ?? 1));
});
}
8 changes: 4 additions & 4 deletions src/commands/x/verify.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import path from 'node:path';
import { run as runCmd, have } from '../../lib/exec.mjs';
import { aidefencePresent, securityPresent } from '../../lib/natives.mjs';
import { scanRvf } from '../../lib/rvf.mjs';
import { projectAqeDir, projectMemoryDb } from '../../lib/paths.mjs';
import { projectAqeDir } from '../../lib/paths.mjs';
import { findMemoryEntry } from '../../lib/project-memory.mjs';
import { projectMemoryEnv } from '../../lib/ruflo-memory.mjs';
import { loadKitConfig } from '../../lib/config.mjs';
import { HOSTS, collectIntegrationFacts, aqeRouterFile } from '../../lib/providers.mjs';
import { readJson } from '../../lib/settings.mjs';
Expand Down Expand Up @@ -67,10 +68,9 @@ async function verifyMemory() {
const namespace = `agentic-kit-verify-${process.pid}-${Date.now()}`;
const key = 'roundtrip';
const value = `memory-proof-${process.pid}-${Date.now()}`;
const env = {
CLAUDE_FLOW_DB_PATH: projectMemoryDb(tmp),
const env = projectMemoryEnv(tmp, {
RUFLO_DAEMON_AUTOSTART: '0',
};
});
let stored = false;
let purged = false;
try {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/adapters/registries.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ const hostEntries = [
approvalPolicy: 'unchanged',
changes: [
{ id: 'claude-to-codex-mcp', kind: 'mcp-registration', scope: 'project', owner: 'agentic-kit', value: 'codex mcp-server', effect: 'expose Codex to Claude Code as mcp__codex__codex in this project', operations: ['setup', 'host-pick', 'sync'], features: ['project'] },
{ id: 'codex-to-ruflo-mcp', kind: 'mcp-registration', scope: 'user', owner: 'agentic-kit', value: 'ruflo mcp start', effect: 'register the Ruflo MCP server in Codex configuration', operations: ['setup', 'host-pick', 'sync'], features: ['project'] },
{ id: 'codex-to-ruflo-mcp', kind: 'mcp-registration', scope: 'user', owner: 'agentic-kit', value: 'ak x ruflo-mcp', effect: 'register the Ruflo MCP server in Codex with workspace-pinned project memory', operations: ['setup', 'host-pick', 'sync'], features: ['project'] },
{ id: 'aqe-codex-integration', kind: 'host-integration', scope: 'project', owner: 'agentic-qe', value: 'aqe init --with-codex', effect: 'project Agentic-QE Codex skills and configuration', operations: ['setup'], features: ['project', 'aqe'] },
],
},
Expand Down
Loading
Loading