Add swarm plugin scaffold with backend adapter layer (P1) - #21
Merged
Conversation
gering
added a commit
that referenced
this pull request
Jul 3, 2026
Four robustness fixes surfaced by a codex+grok self-review of PR #21: - available_version: `|| true` + explicit `return 0` so a non-zero --version exit or SIGPIPE from head() under pipefail can't flip an installed backend to "unavailable" - run_codex: pass --model via an array, not unquoted ${model:+…}, so a model name with whitespace stays one argv word (matches run_grok) - run_grok parser: reject non-object JSON before .get() (no traceback), and include a snippet in the invalid-JSON message for triage E2E re-verified: codex + grok both return schema-valid JSON (exit 0) on a hyphen-leading prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
gering
added a commit
that referenced
this pull request
Jul 3, 2026
Second ensemble pass (Claude lenses + codex + grok) on PR #21 surfaced: - available_version claude branch: capture version separately so a SIGPIPE from head() under pipefail can't print both the real version AND the "in-session" fallback (regression from the prior hardening pass) - list without --json: route through column_or_cat so a host lacking util-linux `column` degrades to raw TSV instead of dying (exit 127) - CLAUDE.md: register the swarm plugin in "Current Plugins" - knowledge entry: add updatedAt/updatedFrom to match sibling files (A codex claim that --skip-git-repo-check needs a bypass flag was REFUTED by the verify pass — tested, runs fine outside a repo — so not applied.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
- swarm 0.1.0: local mixture-of-agents code review (marketplace entry + plugin.json) - scripts/agents.sh: uniform adapter over claude/codex/grok with list/available/ready/run verbs; run enforces the shared findings schema (codex exec --output-schema, grok --json-schema) and prints uniform JSON on stdout - scripts/schema/finding.schema.json: shared findings shape all backends normalize into (failure_scenario required, falsifiable) - /swarm:agents skill: backend status table - E2E-verified against codex 0.128 and grok 0.2.77; fixes found in testing: close codex stdin (hangs on inherited non-TTY stdin) and keep the mktemp path global so the EXIT trap survives set -u Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
grok-composer-2.5-fast rejects --effort and does not enforce --json-schema (structuredOutput stays null), so grok-build is the only schema-capable grok voice; a second same-family voice would also dilute the consensus signal. Prefer grok's native --best-of-n at high effort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Correctness (silent ensemble drops): - Pass prompts hyphen-safe: `--` before codex positional, --single= for grok (leading "-" no longer parsed as a flag; E2E-verified on both) - claude availability is in-session by definition — no PATH gate - Usage-class errors in `run` now exit 2, not 1 (missing flag value, missing prompt file); 256 KiB prompt guard with a clear message instead of an opaque ARG_MAX exec failure Robustness/diagnostics: - Placeholder `-` for empty fields (BSD column collapses adjacent tabs) and tab-sanitized version strings in list output - list rejects unknown flags (exit 2) instead of silent table fallback - --help exits 0; usage block bounded by pattern, not line numbers - python3 preflight; grok/codex JSON parsing catches decode errors cleanly instead of tracebacks / masked "invalid JSON" - --effort only sent to grok-build (other models reject it) Docs/cleanup: - /swarm:agents no longer references the unshipped /swarm:review - plugin description marks phase 1 honestly (plugin.json + marketplace) - Shared require_usable gate (was duplicated in ready/run); dead claude ready_hint removed; knowledge entry pluginVersion corrected Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Four robustness fixes surfaced by a codex+grok self-review of PR #21: - available_version: `|| true` + explicit `return 0` so a non-zero --version exit or SIGPIPE from head() under pipefail can't flip an installed backend to "unavailable" - run_codex: pass --model via an array, not unquoted ${model:+…}, so a model name with whitespace stays one argv word (matches run_grok) - run_grok parser: reject non-object JSON before .get() (no traceback), and include a snippet in the invalid-JSON message for triage E2E re-verified: codex + grok both return schema-valid JSON (exit 0) on a hyphen-leading prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Second ensemble pass (Claude lenses + codex + grok) on PR #21 surfaced: - available_version claude branch: capture version separately so a SIGPIPE from head() under pipefail can't print both the real version AND the "in-session" fallback (regression from the prior hardening pass) - list without --json: route through column_or_cat so a host lacking util-linux `column` degrades to raw TSV instead of dying (exit 127) - CLAUDE.md: register the swarm plugin in "Current Plugins" - knowledge entry: add updatedAt/updatedFrom to match sibling files (A codex claim that --skip-git-repo-check needs a bypass flag was REFUTED by the verify pass — tested, runs fine outside a repo — so not applied.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Capture the validated review-pipeline shape (scope+gate → fan-out → (file,mechanism) merge → verify solos → synthesis) as a reference for P2, with the erprobte workflow script and the dry-run learnings so the build doesn't start from scratch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Adapter (agents.sh):
- ARG_MAX guard now counts BYTES not chars (a multibyte prompt slipped
the ${#prompt} char check yet overflowed exec), and checks file size
BEFORE slurping it into a variable
- external codex/grok calls wrapped in a wall-clock timeout (coreutils
timeout/gtimeout when present, passthrough otherwise; SWARM_TIMEOUT)
- run_grok preflight-rejects non-default models (only grok-build enforces
--json-schema; others would fail late with structuredOutput:null)
Docs:
- README: cross-agent agreement is a strong signal WHEN it occurs, not
"primary" — solo + 3-state verify is the common path (dry-run showed
consensus is the exception)
- blueprint: drop the dead link to the uncommitted task file
Found by the ensemble reviewing its own PR; 5 REFUTED findings dropped
by the verify pass, the CRITICAL prompt-injection tracked as P2 design.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
The ensemble re-reviewed the fixed diff at xhigh and found regressions in the timeout hardening plus deeper issues: - validate SWARM_TIMEOUT up front (a malformed value made `timeout` exit 125, which the rc==124 checks missed → every external run misreported as a backend failure) - warn once when no timeout/gtimeout is on PATH (the cap silently never applied on stock macOS) - run_codex/run_grok now validate the findings SHAPE, not just JSON syntax, before emitting (a valid-but-wrong object would crash the merge) - run_grok: --disable-web-search as a defensive partial sandbox (the diff is untrusted; close the obvious network exfil channel). Full read-deny sandbox parity with codex is P2. - SKILL.md: grok Ready is a heuristic (auth file present ≠ valid token) — say so instead of overclaiming "authenticated" - finding.schema.json: line has minimum 0 (no negative lines) Security note: during this round an agentic backend actually read a real credentials file via the injection path — tracked as the P2 critical; round-2 prompts use fictional secret paths + an explicit no-exploit rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
The prompt is passed as ONE argv word, so the binding cap is the per-argument limit (Linux MAX_ARG_STRLEN = 128 KiB), not total ARG_MAX. The 256 KiB guard let a ~200 KiB prompt pass yet fail to exec on Linux. Cap at 120 KiB with headroom for the schema arg + environment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
The loop's real credential exfiltration + converged security findings drove adapter-level defenses (the parts fixable in P1): - grok now runs --tools "" (verified to block file reads; the diff is inlined so grok needs no tools) on top of --disable-web-search - scrub_secrets redacts secret-shaped content (AWS/GH/sk tokens, private keys, secret=… ) from findings JSON before it leaves run_codex/run_grok — a backstop even if a backend sandbox is bypassed - finding.schema.json caps summary/failure_scenario/recommendation length so a payload can't route a large blob through a field - blueprint gains a mandatory Security section (fencing, merge/verify scrub, consensus-verify, deterministic transport, OS jail = P2) Remaining injection hardening is pipeline-level (merge/verify stages, prompt fencing) and lands in P2; tracked in the task file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
R3 showed the whack-a-mole: closing grok's file access moved the critical to codex (`-s read-only` still permits reads). The fix is one OS jail around EVERY external call, not per-backend flags: - sandboxed(): sandbox-exec (macOS) / bwrap (Linux) deny-read of common secret stores, realpath-resolved (defeats ~/.ssh→dotfiles, /tmp, /etc symlinks) as both subpath (dirs) and literal (files) - VERIFIED: codex explicitly told to read a jailed file returns BLOCKED; normal reviews still work; secret never reaches the output - codex reasoning transcript (stderr) now discarded (could echo a read secret, never scrubbed) - schema gains maxItems:100 (per-field maxLength alone allowed volume exfil across many entries) - stdin guard (TTY → clear error, not a hang); knowledge frontmatter field order; CLAUDE.md /swarm:agents notation - blueprint: consensus gets a light verify not a blind skip; wrapper must distinguish error from empty Fixed a set -e footgun found while wiring this up: _sandbox_deny_paths ended in `[[ … ]] && …` returning 1 when SWARM_DENY_PATHS was unset, aborting the profile build (every default run silently produced nothing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
R4 flagged the OS jail's own gaps. Fixes: - per-backend deny: a backend keeps its OWN cred dir readable but the SIBLINGS' are denied (verified: codex can't read ~/.grok/auth.json) — closes cross-backend credential theft; also deny ~/.config/anthropic, ~/.config/openai, ~/.claude.json - env filter: strip secret-shaped vars (AWS_*, *_TOKEN, *_API_KEY, …) before the call — the jail blocks file reads but backends inherit the environment, so a secret in AWS_SECRET_ACCESS_KEY bypassed it - grok invalid-JSON error path no longer echoes raw bytes to stderr (never passed scrub_secrets; could carry injected/secret content) Honest limit documented in the blueprint: a full deny-$HOME allowlist jail is impractical for the node/bun CLIs (their loaders read all over $HOME). The denylist is a backstop; the real defense is that backends need no file reads (diff is inlined) and grok is tool-less. A minimal- runtime container is the P2 upgrade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
- README output-shape snippet was invalid JSON without the top-level findings array — replaced with a valid, complete example - bwrap jail: --tmpfs dies with ENOTDIR on regular-file deny paths (~/.netrc etc.); bind /dev/null read-only over files, --tmpfs only over directories Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA
gering
force-pushed
the
task/add-swarm-plugin
branch
from
July 3, 2026 21:05
a1c2c38 to
923fa33
Compare
gering
added a commit
that referenced
this pull request
Jul 16, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering
added a commit
that referenced
this pull request
Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering
added a commit
that referenced
this pull request
Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
codexandgrokCLIs, merges and verifies findings into one ranked report. Complementary to pr-flow's GitHub-side loop.Changes
plugins/swarm/.claude-plugin/plugin.json+ marketplace.json entry (version-synced 0.1.0)plugins/swarm/scripts/agents.sh— uniform adapter over claude/codex/grok withlist/available/ready/runverbs;runenforces the shared findings schema (codex exec --output-schema,grok --json-schema) and prints uniform findings-JSON on stdoutplugins/swarm/scripts/schema/finding.schema.json— shared findings shape all backends normalize into (failure_scenariorequired and falsifiable)plugins/swarm/skills/agents/SKILL.md—/swarm:agentsbackend status tablefeatures/swarm-backend-adapter.mdwith the verified CLI facts and gotchasReadiness
Test plan
agents.sh list/list --json/available/readyreturn correct status + exit codes for all three backendsrun codexandrun grokagainst a planted off-by-one diff — both returned schema-valid findings JSON, exit 0run claude→ exit 2 with hint/swarm:agentsrenders the status table in a live session🤖 Generated with Claude Code
https://claude.ai/code/session_013nWqHzv35iMe4mXrLGWKyA