Files
Garry Tan df4feda12e v0.46.7.0 feat(plugins): gbrain as native Codex + Claude Code plugins — manifests, curated skill tree, --source-guard, coexistence, real-binary doors (#4167)
* skills: delete deprecated install tombstone (frontmatterless SKILL.md breaks plugin skill scanners)

The skills/install/ dir was a deprecation pointer to the setup skill with no
YAML frontmatter — absent from skills/manifest.json and unreachable via the
resolver, but visible to any harness that scans skills/ for SKILL.md files.
Codex errors at session start on frontmatterless skills (the same class is
recorded for an external stray copy in TODOS.md), so it must not ship in the
plugin lanes. skills.lock.json regenerated by the commit gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skills: portability, consent, and privacy sweep for the plugin lanes (10 skills, 50 fixes)

Prepares the sweep set (the 8 openclaw-excluded host-inversion skills plus
cold-start, signal-detector, eiirp, gbrain-upgrade) for publication to plugin
consumers who own their brain but are not gbrain-repo developers:

- setup: sanctioned global pinned install command; synthetic example names;
  daemon-only prose generalized; repo-relative doc refs resolved; the invalid
  discovery-loop bash fixed; PGLite-first framing restored
- cold-start: raw OAuth-token curl path deleted; ClawVisor phases labeled as
  host-integration-only with offline (Takeout) equivalents leading
- signal-detector + eiirp: first-fire consent announcement + per-user off
  switch; always-on reframed as a harness convention, not a runtime guarantee
- smoke-test: 'gbrain smoke-test' is the user entrypoint; container paths and
  OpenClaw-service checks labeled conditional
- schema-author/schema-unify/skill-optimizer/frontmatter-guard/gbrain-upgrade:
  repo-relative links, internal review-provenance citations, container paths,
  and read-only-snapshot caveats cleaned

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* plugin-tree: curated skill tree for the codex/claude plugin lanes (generator + committed tree + drift gate)

One publication decision per lane, review-visible: lane set = (openclaw
bundle minus repo-dev exclusions) plus the 8 host-inversion additions —
skills/plugin-lanes.json is the curation record (a reason per entry),
scripts/generate-plugin-tree.ts emits the committed plugin/ tree (65 skills,
shared conventions/_*.md deps, generated README with the CLI-primary starter
note), and scripts/check-plugin-tree.sh byte-diffs tree-vs-generator (the
check-bootstrap-templates part-c posture). The starter_gaps snapshot pins
each bundled skill's beyond-starter MCP ops (computed from frontmatter
tools:, namespace-filtered) so a new gap is a conscious curation event —
every gap op has a first-class gbrain CLI path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* serve: --source-guard fail-closed write routing for user-global plugin serves

A plugin-managed MCP server runs with the plugin snapshot as its cwd, so the
dotfile and local-path source-resolution tiers lose their meaning — an
ambient-tier resolution could silently route writes into whatever source it
fell through to (worst case: a registered source whose local_path contains
the snapshot dir). Under the flag, dispatch blocks write/admin ops with an
actionable source_binding_required envelope unless the winning tier proves
the binding deliberate (flag/env/dotfile/brain_default) or unambiguous
(sole_non_default; seed_default while 'default' is the sole source). Reads
pass on every tier; sole-source brains are a pure no-op; engine errors fail
closed. Both plugin manifests pass the flag; hand-run serves are untouched.

- src/core/source-resolver.ts: WRITE_SAFE_SOURCE_TIERS + sourceGuardBlocksWrite
- src/mcp/dispatch.ts: the gate (before validation, so a blocked caller
  learns the routing rule, not the op's parameter shape); verb envelopes
  carry protocol_version
- src/mcp/server.ts: resolveMcpStdioSourceScope now reports the winning tier
- src/commands/serve.ts: flag parse + seam type; flag registry regenerated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* codex-plugin,claude-plugin: lane manifests, shared launcher, manifest contract test

One repo, two plugin lanes:
- codex: .agents/plugins/marketplace.json (codex-native marketplace) +
  .codex-plugin/plugin.json (skills → the curated plugin/ tree; mcpServers →
  .codex-plugin/mcp.json, deliberately NOT a repo-root .mcp.json — a root
  file would auto-offer a checkout-controlled launcher to every contributor's
  session) + mcp.json serving --surface starter --source-guard with a
  code-derived env_vars passthrough contract
- claude code: .claude-plugin/marketplace.json (content-equivalent so codex's
  dual-format marketplace reading cannot fork the install) + plugin.json with
  the inline env-OBJECT-shape MCP declaration via ${CLAUDE_PLUGIN_ROOT}
- .agents/gbrain-launcher (sh, 755, Unix-only): GBRAIN_BIN → PATH →
  ~/.bun/bin resolution with one stderr resolution line, GBRAIN_SURFACE
  substitute-or-append override (serve argv only), actionable exit-127
  recovery copy, no auto-install by design

test/codex-plugin-manifest.test.ts pins the whole contract: 4-manifest
version lockstep, exact MCP declarations, marketplace equivalence, launcher
statics + all seven resolver/override branches behaviorally (HOME pinned to
a tempdir in every case), curated-tree membership algebra, scanner guards,
env_vars ⊇ derived set, and a generator round-trip. Wired into the skills
commit gate alongside the openclaw manifest test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* bootstrap,doctor: plugin-lane coexistence — the plugin as a third owner of the gbrain MCP name

Detectors (src/core/bootstrap/harness.ts, all fail-open): codexPluginProvidesName
(line-anchored [plugins."<name>@<mkt>"] header + enabled=true — commented
lookalikes and next-table enabled keys never match), claudePluginProvidesName
(~/.claude/settings.json enabledPlugins, the shape verified on a live install),
and the doctor-side any-registration scans that DELIBERATELY count foreign/
manual entries (codexBlockOwnsName's managed-block scope is the wrong question
for coexistence).

runHooks: a healthy plugin-owned skip is a NEW state, not mcpSkipped (which
means host-failure and exits 2) — the MCP substep skips registration argv +
smoke (plugin MCP servers are invisible to mcp get/list), hooks and every
other phase proceed, exit 0, receipt records plugin ownership
('plugin-mcp' / 'hooks+plugin-mcp'). --mcp-even-if-plugin forces the
hand-wired registration (plugin enabled is a config signal, not health).

Harness lane: WARN (never refuse) when wiring the codex managed block next to
an enabled plugin — two same-name servers in different layers is host-defined
behavior; both off-ramps named.

Doctor: plugin_lane_collision (ops category, registered in doctor-categories)
— rows emitted ONLY when a gbrain plugin is enabled: warn on a real
double-registration, ok when the plugin is sole owner; runs before the
bootstrap-state gate (a manual mcp add + plugin needs no bootstrap to
collide).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(helpers): agent-harness oracle fixes for the plugin doors

- parseCodexJsonl retains mcp_tool_call items ({server, tool}, best-effort
  field fallbacks) — e2e assertions no longer regex raw JSONL lines
- claudeHeadlessTurn / codexExecTurn spawn the RESOLVED binary path: the
  hermetic child env can make a bare 'claude'/'codex' resolve differently
  than the resolver the skip-gate consulted
- codexSupportsPlugins / claudeSupportsPlugins probes ('plugin --help'
  exit 0) for the plugin-door skip-gates
- mcpToolsListProbe: deterministic MCP surface oracle — spawns a stdio MCP
  server command, runs the initialize handshake, returns the advertised tool
  names via a real tools/list (never an LLM-output assertion)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(e2e): codex + claude plugin doors — install, oracle, guard, coexistence, smoke

codex door (INSTALL verified GREEN against the real codex 0.147.0 in ~10s):
clean-tree staging via git archive HEAD (a dirty worktree never enters the
snapshot), marketplace add + plugin add, snapshot assertions (curated skills
present, repo-dev skills absent, NON-ROOT .codex-plugin/mcp.json honored,
launcher exec bit survives the copy), add-twice idempotency + exactly-one-row
dual-marketplace probe, deterministic tools/list surface oracle == the starter
surface through the SNAPSHOT launcher, cold-home fast-fail ('No brain
configured. Run: gbrain init'), --source-guard block/allow through the real
MCP pipe, hand-wired-next-to-plugin coexistence probe (succeeds — the doctor
warn scenario is real), marketplace-qualified removal. SMOKE (auth-gated):
plugin-provided server → seeded fact via mcp_tool_call evidence + the
recovery-loop probe (missing binary degrades, never bricks the session).

claude door: validate --strict (marketplace description added to pass),
marketplace add + install → enable entry in the exact claudePluginProvidesName
shape, uninstall clears it; SMOKE via claude -p with the plugin-launched
server. Harness: CodexTurnResult carries mcpToolCalls; extraEnv threads
GBRAIN_* through both turn helpers into plugin-launched servers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci,dx: non-vacuous plugin-doors job + dx-explore codex-plugin-install scenario

heavy-tests.yml plugin-doors job (grok-door posture, EV11): PROVISIONS pinned
npm codex + claude binaries with version-output asserts (refuse on drift),
runs both INSTALL tiers, and refuses green unless the expected pass counts
executed — zero-pass/partial-pass never reports green. INSTALL tiers are
secretless by design; the auth-gated SMOKE tiers self-skip inside the suites
with the skip accounted for in the expected shape (integrity-metadata pinning
deliberately omitted: this job carries no secrets).

dx-explore codex-plugin-install: the plugin lane's first-run journey under a
real PTY — the two documented install commands up front, then an interactive
codex session answering a seeded brain question through the plugin-provided
MCP server; friction events recorded against docs/mcp/CODEX.md's plugin
section. Claude SMOKE oracle fixed to the observed plugin-namespaced tool
shape (mcp__plugin_gbrain_gbrain__*).

Both SMOKE doors verified GREEN live on this machine (codex attempt 1:
usedMcp=true gotFact=true; claude attempt 1 after the namespace fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* release: publish the slim codex-plugin dist branch on every release (EV4)

Force-publishes a history-less commit to the codex-plugin branch carrying
exactly the plugin artifacts (manifests + shared launcher + curated plugin/
tree) so 'codex plugin marketplace add garrytan/gbrain@codex-plugin' downloads
the plugin, not the dev repo. Gated on the same generator byte-diff as the
verify-time drift check; exec bit asserted before push; same force-advanced
trust model as latest-stable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: plugin install paths, routing + surface guidance, tag-check Rule 4, five-way version lockstep

- docs/mcp/CODEX.md: plugin install headlined (@codex-plugin slim ref +
  from-source form), prerequisites (binary + gbrain init, never the squatted
  npm name), what ships (starter surface, host-inversion note), launcher
  resolution + GBRAIN_BIN/GBRAIN_SURFACE, routing under the plugin lane
  (dotfiles dead; source axis env/--source; brain axis env ONLY;
  --source-guard semantics), one-owner-per-name + enabled≠healthy, both
  upgrade halves, removal
- docs/mcp/CLAUDE_CODE.md: Option 0 plugin section (permissions.allow
  pre-approval stays bootstrap-lane-only)
- README + INSTALL_FOR_AGENTS: plugin as the lightweight funnel next to the
  bootstrap paste block; MCP table rows updated
- docs/guides/bootstrap.md degradation row + harness one-owner note;
  BOOTSTRAP_FOR_AGENTS.md codex preflight covers the plugin-owned skip
- scripts/check-bootstrap-tag.sh Rule 4: marketplace refs in docs must pin
  @latest-stable or @codex-plugin
- KEY_FILES entries for every new artifact; CLAUDE.md version-locations row
  (five-file lockstep) + llms bundles regenerated; CHANGELOG under
  [Unreleased]; TODOS: Windows launcher, keyless cold-home auto-init,
  future harness lanes, post-release marketplace-upgrade probe

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(gates): flag-registry regen, serial-rename the source-guard test, register check-plugin-tree in the guards manifest

- cli-flag-registry regenerated (picks up --mcp-even-if-plugin and the other
  new bootstrap flag literals added after the commit-3 regen)
- test/serve-source-guard.test.ts → .serial.test.ts (R1: it mutates
  process.env.GBRAIN_SOURCE in its env-fallback cases)
- guards-manifest.tsv: check-plugin-tree.sh classified buildfresh/exempt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: pre-landing review fixes (1 critical + 13 hardening items from the specialist army)

- CRITICAL: claude plugin SMOKE door gains its missing auth skip-gate
  (hasClaudeAuth) — an unauthed machine burned 2×230s live-turn attempts and
  hard-failed instead of skipping
- source-guard probe: bounded LIMIT-1 existence query + memoized schema-shape
  probe (a legacy pre-archived-column brain paid an exception per guarded
  write); dispatch imports sourceGuardBlocksWrite statically (was a dynamic
  import per call); serve warns loudly that --source-guard is stdio-only when
  combined with --http (the --log-full-params posture precedent)
- generate-plugin-tree: canonical parseSkillFrontmatter replaces the
  hand-rolled block-only parser (which silently missed inline tools: lists —
  proven immediately by multi-word CLI entries surfacing), plus a
  GBRAIN_PLUGIN_TREE_ROOT fixture seam; three negative-fixture tests prove
  the curation gate can actually fail (short reason, addition-in-base, stale
  starter_gaps)
- tests: parseCodexJsonl mcp_tool_call field-fallback unit cases; legacy-
  schema fallback + memoization tests for the guard; dead var + unused import
  dropped from the door tests
- check-bootstrap-tag: Rule 4 moved before the status block (no more
  ok-then-FAIL transcripts); bare-form scope recorded as a deliberate
  decision (Claude marketplaces have no ref-pin syntax)
- check-plugin-tree wired into bun run verify (the comment now tells the
  truth); plugin-doors CI pins EXACT pass counts (grok-door posture);
  release publish job checkout gets persist-credentials:false;
  claudeUserMcpConfigPath() helper replaces the inline ~/.claude.json path;
  dx-explore fails fast when the paid scenario's seed write fails

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: bump version and changelog (v0.46.7.0)

Five-file lockstep (VERSION, package.json, openclaw.plugin.json, both plugin
manifests) + runbook stamp + template-repo regen + bun.lock + llms bundles.
User-pinned slot past the 0.46.2-0.46.6 in-flight queue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: adversarial + red-team review fixes (2 critical + coexistence/guard hardening)

Cross-model review (Claude adversarial + Codex adversarial + red team) after
the merge surfaced two real criticals and a set of correctness/honesty gaps —
all fixed:

- CRITICAL (red team): the e2e source-guard block-probe seeded a sole-source
  brain, which resolves to the unambiguous sole_non_default tier (WRITE_SAFE,
  correctly NOT blocked) — a latent CI redliner. The guard DESIGN is right
  (one real source can't be misrouted); the test now seeds a genuinely
  ambiguous 2-source brain, and the CHANGELOG/CODEX.md wording drops the
  "more than one source" overclaim for "multiple sources to choose from".
  Re-verified live: INSTALL door green, block+unblock both assert.
- CRITICAL (Claude): the plugin-mcp receipt marker was write-only — uninstall
  ran `mcp remove gbrain` for it, which would delete a user's later
  hand-wired registration bootstrap never created. Uninstall now skips the
  mcp-remove for plugin-owned receipts (hooks half still removed).
- launcher resolution prefers ~/.bun/bin over PATH (a hostile repo's
  node_modules/.bin gbrain can't shadow the sanctioned install with all the
  forwarded provider creds); GBRAIN_BIN stays the escape hatch.
- claudeAnyRegistrationExists scans projects.<path>.mcpServers (the LOCAL
  scope `claude mcp add` defaults to) so doctor stops printing a false
  "sole owner"; claudeUserMcpConfigPath honors CLAUDE_CONFIG_DIR.
- --source-guard: local_path now blocks only when another source exists (a
  sole-source brain whose local_path contains the serve cwd is unambiguous);
  the shape memo caches 'legacy' only on a genuine missing-column error (not
  a transient blip); GBRAIN_SOURCE=__all__ writes get a sentinel-specific
  block; a malformed GBRAIN_SOURCE no longer launders through as tier 'env'.
- claude manifest pins cwd=${CLAUDE_PLUGIN_ROOT} so the guard's
  "cwd is meaningless" premise holds on both lanes.
- release publish job: GIT_ASKPASS instead of token-in-argv, ships LICENSE;
  check-plugin-tree drops the now-permanent SKIP fail-open + guards mktemp;
  mcpToolsListProbe races reads against the deadline (no silent-child hang);
  env derivation covers transcription.ts (DEEPGRAM_API_KEY); removal-command
  copy unified to gbrain@gbrain; plugin/ added to the version-lockstep
  (CLAUDE.md + RELEASING); receipt-provenance edge filed as a P3 TODO.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: sync plugin docs to shipped behavior for v0.46.7.0

Post-ship /document-release cross-referenced every plugin doc against the
code and fixed eight drift points:

- starter surface is 26 ops, not "~20" (CODEX.md, plugin/README, generator
  template + regenerated tree, derive-starter-ops comment)
- the plugin stdio serve binds the source axis from GBRAIN_SOURCE env, NOT a
  --source flag (resolveMcpStdioSourceScope passes explicit=null) — dropped the
  over-promised --source from CODEX.md, CHANGELOG, and both source-guard
  dispatch envelopes
- --source-guard gates write AND admin ops (say "write/admin", not "write-only")
- Claude Code Remove section gains the Option 0 plugin uninstall command
- codex plugin remove is marketplace-qualified (gbrain@gbrain, not bare gbrain)
- BOOTSTRAP_FOR_AGENTS plugin-owned skip note now covers Codex AND Claude
- version-locations table: "six/trio" -> "seven files"; stale 0.46.1.0 example
- KEY_FILES: Claude manifest has no env block (command/args/cwd only)

CLAUDE.md edited -> llms bundle regenerated in the same commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: silence intentional SC2016 in the codex-plugin publish job

actionlint (via shellcheck) flagged the single-quoted $GH_TOKEN in the
GIT_ASKPASS heredoc of publish-codex-plugin. The non-expansion is the
point — the askpass script must carry the literal variable so /bin/sh
expands it at git prompt time, never in the workflow shell. Add the same
shellcheck disable=SC2016 directive the publish-template job already
carries for its identical pattern. actionlint now green across all
workflows locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 07:28:36 -07:00
..