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
10 changes: 5 additions & 5 deletions docs/setup/marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ For this repo that resolves three different ways:
| Installed root | Manifest Codex actually reads |
|---|---|
| the repo root | root [`plugin.json`](../../plugin.json) — its AP1 `$schema` matches — with `.codex-plugin/plugin.json` merged over it. No catalogue installs this root as a Codex plugin |
| `plugins/magpie-setup/`, `plugins/magpie-utilities/` | their own `.codex-plugin/plugin.json`, read **instead of** `.claude-plugin/plugin.json` (an overlay applies only over an AP1 root manifest), so it is a complete manifest: the same name, description and shared metadata, `skills`, and a `hooks` path — see below |
| `plugins/magpie-setup/`, `plugins/magpie-utilities/`, `plugins/magpie-security/` | their own `.codex-plugin/plugin.json`, read **instead of** `.claude-plugin/plugin.json` (an overlay applies only over an AP1 root manifest), so it is a complete manifest: the same name, description and shared metadata, `skills`, and a `hooks` path — see below |
| every other `plugins/magpie-<family>/` | `.claude-plugin/plugin.json`, reached by fallback: those roots carry neither a `plugin.json` nor a `.codex-plugin/` |

The last row is why the family plugins install into Codex at all despite
Expand All @@ -356,11 +356,11 @@ carrying only a Claude-Code manifest.
Codex takes a plugin's hooks from its manifest's `hooks` entry, or, with none, from `hooks/hooks.json`.
It parses each hook file strictly, as an object with only `description` and `hooks`.
Any other top-level key rejects the whole file: Codex reports a load warning and runs none of its hooks.
The `hooks/hooks.json` of `magpie-setup` and `magpie-utilities` belongs to Claude Code and carries `$schema` and `modules` for the [Claude Code mods](../when-to-use-mods.md), so Codex cannot use it.
Each of those two plugins therefore has a `.codex-plugin/plugin.json` whose `hooks` names `./hooks/codex-hooks.json`, a file in the shape Codex parses.
The `hooks/hooks.json` of `magpie-setup`, `magpie-utilities` and `magpie-security` belongs to Claude Code and carries `$schema` and `modules` for the [Claude Code mods](../when-to-use-mods.md), so Codex cannot use it.
Each of those three plugins therefore has a `.codex-plugin/plugin.json` whose `hooks` names `./hooks/codex-hooks.json`, a file in the shape Codex parses.
Claude Code reads neither file and keeps using `hooks/hooks.json`.
For `magpie-setup` that file wires the [upgrade check](#automatic-upgrade-detection); for `magpie-utilities` it is empty, which only stops Codex from warning about `hooks/hooks.json` on every start.
`tools/dev/check-family-plugins.py` generates both files with `--fix`, and fails any plugin in the Codex catalogue whose Codex-visible hook file carries another top-level key.
For `magpie-setup` that file wires the [upgrade check](#automatic-upgrade-detection); for `magpie-utilities` and `magpie-security` it is empty, which only stops Codex from warning about `hooks/hooks.json` on every start.
`tools/dev/check-family-plugins.py` generates these files with `--fix`, and fails any plugin in the Codex catalogue whose Codex-visible hook file carries another top-level key.

> [!WARNING]
> A plugin root's `plugin.json` must be a regular file. If it is a **symlink**,
Expand Down
2 changes: 1 addition & 1 deletion tools/spec-loop/.last-sync
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cb51b53b5c75dc4d98421da894417a32dd7872cd
e03b74530acecd3b2312adb22ff68f220bef7c00
12 changes: 12 additions & 0 deletions tools/spec-loop/specs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ by swapping the adapter, not the skill.
- `tools/mail-archive/` — static mailing-list archive reader for
projects whose list history is in a local mbox/Maildir export.
- `tools/jira/` — issue-tracker adapter for projects on Jira.
Its `bridge.groovy` refuses a non-HTTPS base URL and does not follow
redirects (#1862).
- `tools/security-tracker/` — Markdown contract spec for
`contract:security-tracker`: the operations the `security-*` skills perform
on `<tracker>` (view, list, labels, field get/set, create, edit, comment,
Expand Down Expand Up @@ -167,6 +169,9 @@ by swapping the adapter, not the skill.
one; a new rollup always gets the canonical marker, in `github-rollup`
and `vetted-op-tracker rollup-append` alike (#1665).
A comment's id is read from its `#issuecomment-<id>` URL.
A comment counts as the rollup only when its author is the
authenticated `gh` user or a repository collaborator, in `github-rollup`
and vetted-ops alike, so a non-collaborator cannot plant one (#1862).
`tools/github/status-rollup.md` calls the tool instead of carrying its own
script.
Under the secure agent setup both tools' `gh` runs sandboxed and fails,
Expand Down Expand Up @@ -267,6 +272,9 @@ by swapping the adapter, not the skill.
status (`builds.sr.ht`), and repository reads (`git.sr.ht`/`hg.sr.ht`)
via GraphQL. Capability: `contract:tracker + contract:source-control +
contract:mail-archive`.
Comment bodies go through `--body-file` only, a GraphQL mutation needs
`--allow-mutation`, and the service must be a bare sr.ht subdomain
(#1862).
- `tools/asf-svn/` — ASF Subversion distribution backend: staging area
reads, `svn` command-sequence generation for releases and KEYS updates.
Never runs `svn commit`; emits paste-ready commands for the Release
Expand Down Expand Up @@ -325,6 +333,8 @@ by swapping the adapter, not the skill.
printing one JSON document; `create_draft` only `APPEND`s to the drafts
folder and never sends, and an operation whose folder is unset or missing
declines with exit 3 so the resolution chain falls through (#1466).
It connects with a verified TLS context (`ssl.create_default_context()`,
#1862).
`tools/mail-source/mailman3/` is a doc-only, read-only backend over
Hyperkitty's JSON API (`list_recent_threads`, `read_thread`,
`thread_url`); it has no drafts or sent view, so it pairs with a drafting
Expand All @@ -347,6 +357,8 @@ by swapping the adapter, not the skill.
patch-over-Jira adapters; implement `contract:change-request` for
projects that land patches via mailing-list review or Jira rather than
GitHub PRs.
`jira-patch` pins the reviewed attachment by id and sha256 before it is
applied (#1862).
- `tools/chat/` — the `contract:chat` interface (pure Markdown) for a
project's public chat.
Three verbs, `list_channels`, `resolve_user(github_handle)`, and
Expand Down
12 changes: 10 additions & 2 deletions tools/spec-loop/specs/adversarial-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ before the PR is created, through one shared block.
`stream-json` user message and no permission flag, so headless Antigravity
denies every tool that needs approval (the first denial ends its turn with
an empty answer, so its brief asks for file reads and searches only, and a
run that still hits one is an `error` naming the denied tool); `goose run --no-session` with
run that still hits one is an `error` naming the denied tool).
The brief names the tools that work (`list_dir`, `find_by_name`,
`grep_search`, `view_file` / `read_file`) and forbids subagents, because
headless agy asked to list or search a tree hands off to a research
subagent and ends its turn at once; a run that does so is reported as a
subagent handoff, and an empty run says why (#1871, #1852); `goose run --no-session` with
`GOOSE_MODE=chat`, Goose's chat-only mode, which runs no tools at all;
`claude -p` with
`--strict-mcp-config` and Bash, the editing tools, web access and `Task`
Expand All @@ -154,7 +159,10 @@ before the PR is created, through one shared block.
private tracker the report says so in `warnings`.
- **One findings schema, merged.** Every reviewer answers against the same
schema (`severity`, `file`, `line`, `claim`, `evidence`). Parsing keeps the
good findings from a partly malformed reply and reports the bad ones;
good findings from a partly malformed reply and reports the bad ones,
with the reviewer's stderr and its raw output cut to the first 500 and
last 1500 characters, since a streamed reply's last event says how the
run ended (#1854);
findings are de-duplicated across reviewers, keeping every reviewer's name,
and sorted by severity.
- **Bounded and interruptible.** Reviewers run in parallel, each with a
Expand Down
38 changes: 35 additions & 3 deletions tools/spec-loop/specs/agent-isolation-sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,20 @@ See `docs/adapters/goose.md`.
start no watcher when both are `off` (#1715).
`cached`, any other value, and an unknown policy (no card, no gpg, no
`uif:` record) keep the window.
A dismissal (Esc or a click) answers one signature only: its marker
names the signer processes it was made for, stops counting when one of
them ends or an unnamed signer starts, and never lasts past
`DISMISS_TTL_MIN`, so the next signature asks again even while other
sessions stay armed.
A window that closed unanswered (it lost the keyboard and exited with
`OVERLAY_UNANSWERED`, could not be drawn, or crashed) is no dismissal and
is shown again after `RESHOW_DELAY` while the signature still blocks
(#1832).
The matcher arms for every git that can reach the key — global options
with quoted values, git or ssh named by path or passed to a shell as a
string, remote-talking subcommands beyond fetch and push (`lfs`,
`archive --remote`, `subtree`, `svn`, `maintenance prefetch`), and the
`gh` commands that run git over ssh (#1833).
A definite answer is cached in the runtime directory for ten minutes,
but an `off` only for one, so a policy turned on since is honoured on
the next burst of commands (#1723). The
Expand Down Expand Up @@ -338,8 +352,8 @@ See `docs/adapters/goose.md`.
the update skill's drift check and verify check 8 recognise the
dispatcher flavour's symlinks as its installed shape, not as drift
or as inert per-repo hooks (#1322, #1358).
- `tools/hook-manager/` — the contract for git-hook managers (prek
today) under the secure setup, `contract:hook-manager`: a manager's
- `tools/hook-manager/` — the contract for git-hook managers under the
secure setup, `contract:hook-manager`: a manager's
install puts its hook in the repository's own
`$(git rev-parse --git-common-dir)/hooks`, where the dispatcher
chains, and its home is split — `env.<HOME>` gives sandboxed runs a
Expand Down Expand Up @@ -368,6 +382,14 @@ See `docs/adapters/goose.md`.
shim, verify check 19d; the default-home deny and the dispatcher's host
home stay on for every registered manager, because both homes are per
user (#1819).
`registry.json` registers prek (install strategy `git-dir-flag`) and
four managers with install strategy `none`, which already write to the
repository's own hooks directory whatever the global `core.hooksPath`:
`simple-git-hooks`, `cargo-husky`, `git-build-hook` and
`git-code-format-maven-plugin` (#1838).
A `none` manager gets no shim; its worktree caveats (cargo-husky skips
hook install in a linked worktree, the JGit plugins target
`<worktree>/hooks`) are recorded in the README's adapter table.
- `plugins/magpie-setup/hooks/check-upgrade.sh` — magpie-setup's
`SessionStart` hook.
It runs outside the sandbox over a sandbox-writable tree, so it trusts
Expand Down Expand Up @@ -460,7 +482,17 @@ The reference model is four layers, layered:
for the same reason (#1384).
2. **Filesystem + network sandbox** — Linux `bubblewrap` + `socat` SNI
proxy; macOS `sandbox-exec`. Default-deny reads outside the tree and
egress to non-allowed hosts. `sandbox.excludedCommands` carves out
egress to non-allowed hosts.
`sandbox.network.allowUnixSockets` is macOS-only: on Linux Claude Code's
seccomp filter rejects every `AF_UNIX` socket, so the container gateway
and ssh-agent signing need a per-machine `allowAllUnixSockets: true`,
which is only safe paired with absolute `denyRead` entries for the host's
daemon and IPC sockets (Docker / Podman, `/run/user/<uid>` with the user
D-Bus and systemd manager, the system bus, X11, WSL interop).
`docs/setup/secure-agent-setup.md` lists them; verify check 12e
(whenever the flag is set) and a `sandbox-lint` invariant require that
cover, the whole `/run/user/<uid>` of the current uid included (#1809).
`sandbox.excludedCommands` carves out
commands that need host auth the sandbox blocks:
- `gh` (OS keyring and, on macOS, Security.framework TLS
verification); the blast radius is held by layers 3 (`gh auth
Expand Down
8 changes: 8 additions & 0 deletions tools/spec-loop/specs/cve-tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ reviewable.
`--allocate` checks the allocate token without allocating).
The tool never sees the operator's password, MFA factor, or session
cookie.
- Reviewer comments round-trip through the tracker
(`tools/cve-tool-vulnogram/record.md`): `security-issue-sync` reads the
comment notification mail, proposes the body-field change, regenerates
and pushes the JSON, then appends the comment's slug to the
processed-comments ledger in the status rollup and drafts a courtesy
reply. Vulnogram has no resolve action for a comment, in the API or the
UI, so the ledger and the reply are the only record that it was handled
and sync never asks the operator to "resolve" one (#1840).
With `vulnogram-api-setup --pmc <pmc> [--scope read|write|allocate|auto]` the token
arrives without copy-paste: the operator approves it in the browser on
`/users/token/authorize`, and the script receives it over a loopback
Expand Down
9 changes: 9 additions & 0 deletions tools/spec-loop/specs/marketplace-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ directories (`plugins/magpie-<family>/skills/<alias>/SKILL.md`); the flat
`skills/<dir>` tree mirrors each one back with a single-hop symlink. Their
manifest is Claude Code's `.claude-plugin/plugin.json`, but they are not
Claude-Code-only: Codex installs them by falling through to that same file.
The exception is a plugin whose `hooks/hooks.json` carries Claude Code mods
(`$schema`, `modules`), which Codex's strict hooks schema rejects whole:
`magpie-setup`, `magpie-utilities` and `magpie-security` each carry a
complete `.codex-plugin/plugin.json` (read instead of the Claude manifest)
whose `hooks` names a clean `hooks/codex-hooks.json` — the `startup`
upgrade check (`/bin/bash -p check-upgrade.sh`) for `magpie-setup`, an empty
file for the other two (#1836, #1676).
`tools/dev/check-family-plugins.py --fix` generates them and fails a Codex
catalogue plugin whose Codex-visible hook file has any other top-level key.

`magpie-setup` also carries two non-skill payloads as real files inside its
plugin root, because `setup config` installs them from the plugin on a
Expand Down
4 changes: 2 additions & 2 deletions tools/spec-loop/specs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ Each mode is an independently toggleable set of skills. Maturity mirrors
| Mode | Spec | Maturity |
|---|---|---|
| Agentic Triage | [triage-mode.md](triage-mode.md) | stable (security) / experimental (PR, issue, contributor-nomination) |
| Agentic Mentoring | [mentoring-mode.md](mentoring-mode.md) | experimental (5 skills) |
| Agentic Mentoring | [mentoring-mode.md](mentoring-mode.md) | experimental (7 skills) |
| Agentic Drafting | [drafting-mode.md](drafting-mode.md) | stable (security) / experimental (issue, audit-finding-fix, release-announce-draft) |
| Agentic Pairing | [pairing-mode.md](pairing-mode.md) | experimental (2 skills) |
| Agentic Pairing | [pairing-mode.md](pairing-mode.md) | experimental (3 skills) |

> **Agentic Autonomous** is the fifth MISSION mode but is deliberately **off** by
> sequencing policy (`.asf.yaml` `allow_auto_merge: false`) — it has no
Expand Down
6 changes: 5 additions & 1 deletion tools/spec-loop/specs/pr-management-family.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ same directory; the behaviour is unchanged.
posts on maintainer confirmation. Ships `mode: Triage` + `experimental`.
Selection, scans, the slop threshold, disposition, inline anchoring and the
review payload run as code in `pr_management.code_review`; the agent judges
H1/H5/S2, writes the findings and assigns severity. Detail files include
H1/H5/S2, writes the findings and assigns severity.
An inline comment carries the finding as the review body does (its
`quoted_rule` and `suggestion`), minus the excerpt it is anchored on, and
a folded finding falls back to its suggestion before its rule, never the
bare rule label (#1853). Detail files include
`prerequisites.md`, `review-flow.md`, `review-loop.md`, `criteria.md`,
`adversarial.md`, `posting.md`, one file per outcome under
`classifications/` and per category under `criteria/`.
Expand Down
30 changes: 27 additions & 3 deletions tools/spec-loop/specs/sandbox-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ error, and two skills that probe or verify the setup on demand.
**Root cause** (which sandbox layer blocks it and why), **Fix** (a
settings widening with per-entry rationale, or — for the `gh` entry —
an invocation-shape rule, because there is nothing to widen), and
**Notes**. Twelve entries today: SSH agent / Yubikey, signed commit
**Notes**. Fourteen entries today: SSH agent / Yubikey, signed commit
failing before any touch (`gpg.format=ssh` key unreadable), signed
commit failing with `cannot exec` of the touch-overlay wrapper
(`gpg.ssh.program` under the read-denied `~/.claude/scripts/`),
Expand All @@ -66,7 +66,17 @@ error, and two skills that probe or verify the setup on demand.
skipped for sandboxed commits (whole-user `core.hooksPath` under the
read-denied home, #1364), and reads of `/tmp/claude-<uid>` asking for
approval every time (`permissions.blockReadsOutsideWorkingDirectories`
on and the directory not a working directory, #1418).
on and the directory not a working directory, #1418), every Bash call
failing with `bwrap: Can't create file … Read-only file system` (an
`allowRead` parent of an `allowWrite` path makes bubblewrap mount it
read-only, #1809), and `git status` listing files nobody created
(`.mcp.json`, `.bash_profile`: the character-device placeholders
bubblewrap mounts over denied paths, #1809).
The Docker / Podman entry also covers the container gateway's policy
refusals (BuildKit, a socket bind-mount, an untyped `docker inspect`):
a tool that drives the docker CLI may hide the 403 body or report only
`unable to upgrade to h2c, received 403`, and the expected behaviour is
to report the refusal, never to route around the gateway (#1810).
- `tools/agent-isolation/sandbox-error-hint.sh` — a Claude Code
`PostToolUse` hook on the `Bash` matcher. Scans the tool's stdout +
stderr for the catalogued symptom strings and, on a match, prints
Expand Down Expand Up @@ -152,7 +162,21 @@ error, and two skills that probe or verify the setup on demand.
`.roborev.toml` under the hook, ⚠; roborev agent hooks or MCP servers
in the harness, ⚠). Check 9 also fails a vetted-ops rule that
spells the versioned plugin-cache path with a `*`, since the rule must
name `~/.claude/magpie/vetted-ops` (#1406). The conditional checks —
name `~/.claude/magpie/vetted-ops` (#1406).
Checks are written to catch false passes (#1808): check 1 evaluates the
merged project, project-local and user settings, requires the home
`denyRead` entry, and names the file each key came from; check 7a probes
credential paths with `test -r` / `stat`, never `cat`, and check 7b lists
variable names only and warns on an IDE-launched session that never went
through `claude-iso`; checks 5, 10a and 13c report unchecked when the
pre-flight finds no framework checkout; check 10's overlay sub-checks
apply only to a hardware key (a card `gpg --card-status` finds, whatever
`gpg.format` is); and check 12e fails `allowAllUnixSockets` on Linux
without the host-socket `denyRead` cover, whether or not a container
runtime is present (#1809).
Verify and doctor both say how to recognise the sandbox's `/dev/null`
placeholders and to take host state from git or the user.
The conditional checks —
9, 10, 12, 13, 14, 17 and check 8's whole-user branch — live in
`conditional-checks.md`; the
body keeps each one's condition and its **n/a** answer, so a run
Expand Down
Loading