{"id":"3c302abb-ce70-471b-b0c9-ca668df6fdbe","entityType":"agent","slug":"clawhub-chpomob-adversarial-spec","name":"adversarial-spec","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chpomob-adversarial-spec","canonicalPath":"/agent/clawhub-chpomob-adversarial-spec","generatedAt":"2026-10-09T20:22:15.836Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":null},"description":"Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval. Skill: adversarial-spec Owner: chpomob Summary: Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval. Tags: latest:0.1.0 Version history: v0.1.0 | 2026-08-03T18:11:30.582Z | auto - Initial public release of adversarial","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 2.7K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s17435m3chty5jmw4jhpkyhnb58brn8g:adversarial-spec","sourceUrl":"https://clawhub.ai/chpomob/adversarial-spec","homepage":"https://clawhub.ai/chpomob/skills/adversarial-spec","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chpomob/adversarial-spec","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chpomob/skills/adversarial-spec","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":69,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criter"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":null},"stars":null,"forks":null,"downloads":2736,"packageName":null,"latestVersion":"0.1.0","tractionLabel":"2.7K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T11:52:55.999Z","lastCrawledAt":"2026-10-09T11:52:55.999Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T11:52:55.999Z","lastVerifiedAt":null,"highlights":[{"version":"0.1.0","createdAt":"2026-08-03T18:11:30.582Z","changelog":"- Initial public release of adversarial-spec: structured, adversarial specification writer. - Transforms a brief into a formal spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. - Git-aware pipeline with automatic branching, phase-driven challenge and revision loop, and squash-merge on approval. - CLI supports selectable writer/reviewer commands, provider registries, and robust configuration. - Emphasizes disk-based, English-only spec handling and clear prompt separation—no embedding of spec text in prompts. - Includes comprehensive pre-flight checklist and persona-based workflow for clear, repeatable spec development.","fileCount":33,"zipByteSize":82088}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17435m3chty5jmw4jhpkyhnb58brn8g:adversarial-spec","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T20:22:15.835Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chpomob-adversarial-spec/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":null},"readme":"Skill: adversarial-spec\n\nOwner: chpomob\n\nSummary: Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval.\n\nTags: latest:0.1.0\n\nVersion history:\n\nv0.1.0 | 2026-08-03T18:11:30.582Z | auto\n\n- Initial public release of adversarial-spec: structured, adversarial specification writer.\n- Transforms a brief into a formal spec.md with YAML frontmatter, requirements, acceptance criteria, and target files.\n- Git-aware pipeline with automatic branching, phase-driven challenge and revision loop, and squash-merge on approval.\n- CLI supports selectable writer/reviewer commands, provider registries, and robust configuration.\n- Emphasizes disk-based, English-only spec handling and clear prompt separation—no embedding of spec text in prompts.\n- Includes comprehensive pre-flight checklist and persona-based workflow for clear, repeatable spec development.\n\nArchive index:\n\nArchive v0.1.0: 33 files, 82088 bytes\n\nFiles: _retrospective (0b), _retrospective/ISSUES.md (344b), .gitignore (84b), LICENSE (665b), README.md (1864b), references (0b), references/adversarial-2026-07-14-pre-publication-review.md (6156b), references/claude-timeout-notes.md (814b), references/claude-tmux-wrapper-system-notes.md (2993b), references/phase-challenge-prompt-reduction.md (2299b), references/provider-agnostic-design.md (5290b), scripts (0b), scripts/__init__.py (196b), scripts/adversarial_spec.py (53586b), scripts/install.sh (1934b), scripts/phases (0b), scripts/phases/__init__.py (6697b), scripts/phases/phase_challenge.py (7123b), scripts/phases/phase_revise.py (4371b), scripts/phases/phase_spec.py (6367b), scripts/phases/phase_verify.py (6648b), scripts/phases/phase_write.py (4622b), skill-card.md (2757b), SKILL.md (14286b), spec.md (17363b), tests (0b), tests/conftest.py (1505b), tests/test_contract_gate.py (7337b), tests/test_orchestrator.py (41265b), tests/test_p14_integration.py (15111b), tests/test_p15_optional_modes.py (12438b), tests/test_phases.py (20656b), _meta.json (135b)\n\nFile v0.1.0:SKILL.md\n\n---\nname: adversarial-spec\ndescription: \"Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval.\"\nversion: 1.1.0\nauthor: Hermes Agent\nlicense: 0BSD\nplatforms: [linux, macos]\nmetadata:\n  hermes:\n    tags: [adversarial, spec, planning, specification, requirements]\n    related_skills: [grill-me, adversarial-plan, adversarial-code-loop]\n---\n\n# Adversarial Spec\n\n**Brief → structured specification.** Two-role adversarial pipeline that transforms a\nvague idea (from grill-me or direct input) into a formal spec.md with YAML frontmatter,\nrequirements, acceptance criteria, and target file descriptions.\n\n## Installation\n\nRequires the `adversarial-common` sibling repo (shared engine). One-line install:\n\ncurl -fsSL https://raw.githubusercontent.com/chpomob/adversarial-spec/main/scripts/install.sh | bash\n\nor, from an existing checkout:\n\nbash scripts/install.sh\n\nBoth place adversarial-spec and adversarial-common side by side under `~/.hermes/skills` (override the target with `$1` or `$HERMES_HOME`).\n\n## Workflow\n\n```\nPHASE 0 ──→ GIT SETUP (branch, stash, init)\nPHASE 1 ──→ WRITE  (spec-writer reads brief, writes spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger critiques for gaps/contradictions)\nPHASE 3 ──→ REVISE (spec-writer amends spec.md per findings)\nPHASE 4 ──→ VERIFY (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit\n```\n\n## CLI\n\n```bash\npython3 scripts/adversarial_spec.py \\\n  --brief <file>           # brief file (default: stdin)\n  --dev-cmd <cmd>          # default: pi --provider zai --model glm-5.2\n  --review-cmd <cmd>       # default: pi --provider deepseek --model deepseek-v4-pro\n  --workdir <dir>          # default: .\n  --max-loops <N>          # default: 2\n  --feature <name>         # default: from brief filename\n  --timeout <N>            # default: 600\n  --out <dir>              # default: .adversarial-spec\n  --provider-config <path> # external provider config (default: ~/.config/adversarial/providers.yaml)\n  --no-merge\n```\n\n## Pre-flight checklist (orchestrator)\n\nRun these BEFORE writing the brief, especially when contributing to an upstream project:\n\n1. **Check CONTRIBUTING.md** — Project-specific rules for branch naming (`feat/`, `fix/`, `docs/`), commit message format (Conventional Commits), PR template fields, and test requirements. Incorporate these into the spec's acceptance criteria.\n2. **Check for pre-existing PR review feedback** — If the feature already has an open PR with reviewer comments (automated or human), read the findings and incorporate them into the brief. The spec should address what the review flagged, not re-propose rejected patterns.\n3. **Choose the right parent branch** — For upstream contributions, create a feature branch from `upstream/main` (not your fork's main): `git checkout upstream/main -b feat/my-feature`. For work on your own fork, use your fork's main. The parent branch determines what the pipeline's squash-merge targets.\n4. **Gap analysis for partially-merged features** — If the feature already exists in upstream but is incomplete, run the gap-matrix pattern from `adversarial-code-loop` (partial-merge-gap-fill) before writing the brief. The spec should target gaps, not re-implement what's already merged.\n5. **Stash/commit local changes** — Dirty trees are auto-stashed in PHASE 0, but stash-pop conflicts can abort the pipeline. Manual pre-commit is safer when `--workdir` is a live install.\n\n## Output\n\nspec.md with YAML frontmatter:\n\n```yaml\n---\nname: \"feature-name\"\nversion: \"1.0\"\nauthor: \"adversarial-spec\"\nstatus: \"draft\"\ntargets:\n  - file: path/to/file.rs\n    description: \"What changes in this file\"\n---\n\n# Feature title\n\n## Problem\nWhat problem does this solve?\n\n## Requirements\n- Bullet list of functional requirements\n\n## Acceptance criteria\n1. Each requirement has at least one testable criterion\n```\n\n## Personas\n\nLoaded from adversarial-common/personas/:\n- spec-writer.md — reads brief, writes spec.md to disk\n- spec-challenger.md — reads spec.md, outputs JSON findings\n\n## Exit codes\n\n| Code | Meaning |\n|------|---------|\n| 0 | APPROVED — spec squash-merged |\n| 1 | Infrastructure failure |\n| 2 | Usage error |\n| 3 | REJECT |\n| 5 | CONTEXT_BLOCKED — CI/preflight context gate rejected the brief |\n\n## Language discipline\n\nAll pipeline-internal text (spec, commit messages, findings, JSON) is **English**.\nUser-facing conversation stays in the conversation's language. Write the brief in\nEnglish unless the user explicitly instructs otherwise. A spec written in French or\nother non-English languages will break later pipeline stages (plan, code loop) that\nexpect English section headings and identifiers.\n\n## Provider selection — registry, explicit commands, then fallbacks\n\nProvider-mode precedence is `--provider-config` (or the\n`ADVERSARIAL_PROVIDER_CONFIG`/implicit default registry) > explicit role flags >\nhardcoded fallback commands. A selected registry supplies the writer and challenger\nchains and enables quota-aware selection:\n\n```bash\npython3 scripts/adversarial_spec.py \\\n  --brief brief.md \\\n  --provider-config ~/.config/adversarial/providers.yaml\n```\n\nThe provider config is a YAML file where each role lists commands in preference order.\nThe pipeline checks real-time quota before each phase and picks the first available command.\nWithin registry mode, a non-empty `--dev-cmd` or `--review-cmd` is an explicit,\nquota-bypassing override for that role. Without a registry, each role resolves its\ncommand from the explicit flag, then `ASPEC_DEV_CMD`/`ASPEC_REVIEW_CMD`, then these\nhardcoded fallbacks:\n\n- Writer: `pi --provider zai --model glm-5.2`\n- Challenger: `pi --provider deepseek --model deepseek-v4-pro`\n\nThis means:\n\n- **Writer and challenger MUST be different models** — enforced by config, not by the code.\n  The pipeline refuses to run the same alias for both roles (same-model debate is\n  pointless).\n- **Configured fallback chains remain the user's choice.** The commands above are\n  legacy fallbacks only when no registry or role-specific command is available.\n- **Explicit role commands remain backward compatible.** In registry mode they\n  override that role's selected provider; in legacy mode they take precedence over\n  the environment and hardcoded command.\n\n## Prompt design\n\n- **NEVER embed the brief or spec text in the challenge prompt.** The challenge\n  prompt tells the model to read `spec.md` from the phase working directory.\n  It includes only bounded metadata such as the branch-point SHA and output\n  schema; the specification itself remains on disk. Embedding contradicts the\n  adversarial design principle that context lives on disk / in git.\n- Keep the challenge prompt under ~2K chars: \"Read and challenge `spec.md` from\n  the current working directory. Inspect the cumulative change from the\n  branch-point SHA. Output ONLY valid JSON.\"\n- **Fixed 2026-07-15:** `scripts/phases/phase_challenge.py` previously\n  concatenated the full spec text into the prompt (`f\"--- spec.md ---\\n{spec_text}\"`)\n  despite the SKILL.md saying not to. Patched to an under-1KB instruction with\n  no embedding; its size is independent of the spec length. The model reads\n  spec.md from disk via `--cwd`. See `adversarial-plan` pitfall about CHALLENGE\n  prompt reduction for the sibling fix.\n- If the claude-tmux wrapper is used as challenger, ensure `--cwd` points to the\n  workdir so the model can read the files. Without `--cwd`, the tmux session\n  starts in the wrong directory and the model cannot find plan.md/spec.md.\n- **claude-tmux wrapper v1 does NOT support `--yolo`.** The `--dangerously-skip-permissions`\n  behaviour is the DEFAULT (set `danger=True` in the wrapper, no flag needed). Passing\n  `--yolo` causes argparse exit code 2. Just omit it: `--model sonnet --timeout 900`\n  is sufficient. Validated on the v1 wrapper at `/home/chpo/claude-tmux-wrapper/claude-tmux.py`.\n\n## Pitfalls\n\n- Keep the brief concise but complete. Grill-me can expand a vague idea before feeding it here.\n- The spec-writer writes to disk (spec.md), not stdout. The challenger reads from disk.\n- YAML frontmatter is validated: name, version, author, targets are required.\n- The same code patterns as adversarial-code-loop v4: git branch isolation, phase modules, squash merge.\n- **Never hardcode model-specific fallback chains in a spec.** Provider selection is\n  purely configuration-driven. A spec that references \"Claude\", \"Codex\", or any model\n  by name in its requirements or acceptance criteria creates an implicit dependency on\n  specific providers and breaks when the user's lineup changes. The spec describes\n  *what* to build — the *who* (which model builds it) belongs in the provider config.\n- **Never list model-specific commands in a spec's targets.** The `cmd:` field in\n  target file descriptions should describe the change (\"add YAML config loader\",\n  \"expose ProviderConfig dataclass\"), not the tool that makes it.\n- **Pre-existing PR review feedback shapes the brief.** When the user already has an open PR with reviewer comments (e.g. hermes-sweeper, teknium1), read the full review before writing the brief. The spec must explicitly address each review finding so the pipeline doesn't re-propose code the reviewer already rejected. Document the review verdict and each finding in the brief's context section. The spec-challenger will independently validate the approach, which serves as a second opinion on whether the review feedback was correctly interpreted.\n- **Requirement ID format is regex-constrained.** The validation regex expects `R1:` or `R1-` (colon or hyphen after the ID). `R1 (P0) —` or `R1 —` with em dash will NOT match — shows \"Requirements section has no identifiable requirement ids\". Always write requirements as `- R1: description` or `- R1 - description`. The em dash `—` is not in the regex lookahead set.\n- **Acceptance criteria format similarly constrained.** The regex expects `AC1 (R1):` at the start of a bullet line. No space between `)` and `:`. Wrong: `- AC1 (R1) : text`. Right: `- AC1 (R1): text`.\n- **Keep the spec in English.** All pipeline-internal text (spec, plan, commit messages, findings) must be English. Only user-facing conversation stays in the user's language. Write the brief in English unless you explicitly instruct otherwise.\n- **If the spec-writer produces a valid spec in the wrong format**, fix the format with a script (regex replace em dashes and parenthesized markers) rather than re-running the WRITE phase. The validation regexes are strict — small formatting issues cause silent failures.\n\n## Known Issues (from 2026-07-14 pre-publication review)\n\nA full adversarial pre-publication review surfaced 20 findings (1 blocker, 8 major, 6 minor, 5 nit). Status below is reconciled against current code and tests; items marked \"fixed\" carry a test pointer, the rest remain accurate as open issues.\n\n- **Stash loss on setup failure (A1/B2/B3) — fixed.** `pipeline_base.setup_git` records the stash id onto the shared state the instant `git stash push` succeeds, before branch creation even runs, so a later failure within the same `setup_git` call still leaves `restore_git` able to pop it back onto the parent branch. See `tests/test_p14_integration.py::test_setup_git_partial_failure_leaves_recoverable_state`.\n- **Exit code masks merge failure (A5/B1) — fixed.** A failed squash-merge now forces `EXIT_INFRA` and rewrites the persisted `final.json` verdict to `INFRA`, so a merge failure can never read as `APPROVED`. See `tests/test_orchestrator.py::test_finish_merge_failure_returns_infra_and_records_error` and `::test_verdict_not_approved_on_merge_failure`.\n- **Spec validation is too loose (B4) — fixed.** `phase_spec._REQUIRED_SCALARS` now requires `name`, `version`, and `author`; a non-empty `targets` list (each entry needing `file`/`description`) and full requirement/acceptance-criteria coverage are enforced too. See `tests/test_phases.py::test_validate_spec_missing_name` and `::test_validate_spec_requires_all_frontmatter_fields`.\n- **Writer can commit arbitrary changes (B5):** `commit_all` still stages everything (`git add -A`), not just `spec.md`; a prompt-injected writer could modify source files. Still open.\n- **Convergence loop can relitigate settled findings (A2/B6):** partially mitigated — the REVISE/VERIFY loop no longer overwrites the findings list with an empty set when the verifier REJECTs while marking every finding settled (avoids a crash), but the underlying problem — the pipeline can still burn through `max_loops` re-running the same contradictory REJECT-with-all-settled findings — is unfixed and has no test coverage. Still open.\n- **final.json not written on infra failures (A3/B9):** `pipeline_base.phase_failure` only logs to `ISSUES.md`; it never writes or clears `final.json`, so a stale `final.json` from a prior run survives a phase crash untouched. Still open.\n- **Artifact directories overwrite silently (A4):** Artifact directories are keyed only by feature name, with no timestamp or run id; a rerun of the same feature silently overwrites all prior artifacts. Still open.\n- **Custom `--out` paths can be committed (B8) — fixed.** `_ensure_out_gitignored` anchors a normalized `.gitignore` entry at the nearest tracked ancestor of `--out` before the first commit — covering relative paths, `./`-prefixed paths, paths outside `workdir` but inside the enclosing repo, and paths that resolve to the repo root itself (e.g. `--out .`, where the naive `./` pattern would ignore nothing). See `tests/test_orchestrator.py::test_custom_out_dir_is_gitignored`, `::test_relative_out_with_dot_prefix_is_gitignored`, `::test_outside_workdir_but_inside_repo_is_gitignored`, `::test_custom_out_dot_dir_is_gitignored`, `::test_custom_out_abs_path_inside_repo_is_gitignored`, and `::test_custom_out_abs_repo_root_outside_workdir_is_gitignored`.\n- **Issues header stale (A7) — fixed.** `_retrospective/ISSUES.md`'s header now correctly states that pipeline failures go to `<out_dir>/ISSUES.md`, not this file.\n\nFile v0.1.0:README.md\n\n# adversarial-spec\n\n**Brief → structured spec.** Two-role adversarial pipeline that takes a product brief, specification request, or feature idea and produces a `spec.md` with YAML frontmatter, numbered requirements, and acceptance criteria.\n\nFor Hermes Agent, Claude Code, Codex, or any LLM CLI.\n\n## How it works\n\n```\nPHASE 1 ──→ WRITE     (spec-writer turns brief into structured spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger attacks for gaps, contradictions, untestable criteria)\nPHASE 3 ──→ REVISE    (spec-writer amends per findings)\nPHASE 4 ──→ VERIFY    (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit\n```\n\n## Output format\n\n```yaml\n---\nname: \"feature-name\"\nversion: \"1.0\"\nauthor: \"adversarial-spec\"\nstatus: \"draft\"\ntargets:\n  - file: path/to/file\n    description: \"What this file must do\"\n---\n\n## Requirements\n- R1: …\n- R2: …\n\n## Acceptance criteria\n- AC1 (R1): …\n- AC2 (R2): …\n```\n\nSpecs are consumed by `adversarial-plan` for planning and `adversarial-code-loop` for implementation.\n\n## Comparison\n\n| Feature | adversarial-spec | zscole/adversarial-spec |\n|---------|-----------------|----------------------|\n| Structured frontmatter | ✅ YAML + targets + requirements | ❌ Free-form |\n| Acceptance criteria | ✅ Per-requirement ACs | ❌ |\n| Git-native pipeline | ✅ Branch-per-spec, squash-merge | ❌ Single file |\n| plan/code-loop integration | ✅ Direct feed to plan + loop | ❌ Standalone |\n\n## Quick start\n\n```bash\npython3 scripts/adversarial_spec.py \\\\\n  --brief /path/to/brief.md \\\\\n  --dev-cmd \"<your-dev-cmd>\" \\\\\n  --review-cmd \"<your-review-cmd>\"\n```\n\n## Dependencies\n\n- Python ≥ 3.11\n- Git ≥ 2.5\n- Two LLM CLIs (spec-writer + spec-challenger)\n\nUses `adversarial-common` as the shared engine.\n\n## License\n\n0BSD — see [LICENSE](LICENSE).\n\nFile v0.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn7e26az9x7m8bgwfwg90q1wkh8bsqw0\",\n  \"slug\": \"adversarial-spec\",\n  \"version\": \"0.1.0\",\n  \"publishedAt\": 1785780690582\n}\n\nFile v0.1.0:references/adversarial-2026-07-14-pre-publication-review.md\n\n# Pre-Publication Adversarial Review — adversarial-spec (2026-07-14)\n\n**Scope:** Full `--project-dir` review of `adversarial-spec` (10 Python files, ~1.8 KLOC).\n**Models:** Claude Fable 5 (Architect + Synthesis), Codex GPT-5.6-Sol (Inspector + Cross).\n**Privacy pre-scan:** Clean — no hardcoded paths, credentials, or personal info.\n**Verdict:** REQUEST_CHANGES (both reviewers, before synthesis).\n\n## Findings Summary\n\n| Bucket | Count | Severities |\n|--------|-------|-----------|\n| Architect | 10 | 1 major, 5 minor, 4 nit |\n| Inspector | 10 | 1 blocker, 7 major, 1 minor, 1 nit |\n| **Total unique** | **20** | **1 blocker, 8 major, 6 minor, 5 nit** |\n\n## Architect Findings (Claude Fable 5)\n\n| ID | Severity | File | Summary |\n|----|----------|------|---------|\n| A1 | **major** | `scripts/adversarial_spec.py:138` | Stash stranded if `_setup_git` fails after `stash_dirty` — error path returns without `stash_id`, `_restore()` in `finally` can't pop it, user's uncommitted work silently lost |\n| A2 | minor | `scripts/adversarial_spec.py:260` | Verifier REJECT-with-all-settled causes non-converging re-litigation — `remaining` is empty so `findings` stays at full list, next round revises already-settled findings → exhausts `max_loops` → REJECT |\n| A3 | minor | `scripts/adversarial_spec.py:100` | `final.json` contract broken on infra failures — `_phase_failed` returns `EXIT_INFRA` without calling `write_final_json`, caller polls stale or absent artifact |\n| A4 | minor | `scripts/adversarial_spec.py:355` | Artifacts dir keyed only by feature name — reruns silently overwrite `final.json`, `ISSUES.md`, etc. across runs of the same feature |\n| A5 | minor | `scripts/adversarial_spec.py:165` | APPROVED exit code (0) masks failed squash-merge — `_finish` catches `GitError`, sets `merged=false` in JSON, but still returns 0 |\n| A6 | minor | `scripts/adversarial_spec.py:385` | Stash-pop after squash-merge can conflict when user's dirty tree touched the same files — no conflict detection or specific warning |\n| A7 | nit | `_retrospective/ISSUES.md:3` | Stale claim that failures are auto-appended to this file — actually goes to `<out_dir>/ISSUES.md` now |\n| A8 | nit | `scripts/adversarial_spec.py:78` | `_ensure_ids` dedup generates awkward compound ids like `S1-3-3` |\n| A9 | nit | `scripts/phases/phase_verify.py:55` | Prompt hard-codes `git diff HEAD~1..HEAD` — wrong if writer CLI made multiple commits |\n| A10 | nit | `scripts/phases/phase_challenge.py:44` | Full spec embedded in prompt with no size guard — large specs can overflow context window |\n\n## Inspector Findings (Codex GPT-5.6-Sol)\n\n| ID | Severity | File | Summary |\n|----|----------|------|---------|\n| B1 | **blocker** | `scripts/adversarial_spec.py:185` | Git finalization failure still returns exit 0 — CI receives success despite `merged=false` |\n| B2 | **major** | `scripts/adversarial_spec.py:127` | Stash restoration failure doesn't change exit result — `_restore` only warns, `main` returns EXIT_APPROVED while user's changes remain stashed |\n| B3 | **major** | `scripts/adversarial_spec.py:146` | Partial setup failure loses recovery state — stash done before branch/checkout/record, if later ops fail, `parent_branch` and `stash_id` never set in state |\n| B4 | **major** | `scripts/phases/__init__.py:108` | Spec validation enforces only the `name` field — accepts empty specs missing version, author, targets, requirements |\n| B5 | **major** | `scripts/phases/phase_write.py:59` | Writer can commit arbitrary repo changes — `commit_all` stages everything, not just `spec.md`; `phase_revise.py:51` repeats the same |\n| B6 | **major** | `scripts/adversarial_spec.py:269` | Later revisions can regress already-settled findings — unresolved-only narrowing means round 2 never re-checks earlier fixes |\n| B7 | **major** | `scripts/phases/phase_verify.py:28` | Contradictory duplicate verification results accepted — `{id:S1,status:resolved}` AND `{id:S1,status:disputed}` both pass, APPROVE can win incorrectly |\n| B8 | **major** | `scripts/adversarial_spec.py:150` | Custom artifact dirs can be committed and merged — only literal `.adversarial-spec/` in `.gitignore`, relative `--out` paths get swept by `commit_all` |\n| B9 | **major** | `scripts/adversarial_spec.py:105` | Infrastructure failures leave stale final verdicts — earlier `final.json` from a previous successful run persists when a later run fails mid-pipeline |\n| B10 | minor | `scripts/adversarial_spec.py:368` | Output-dir errors escape error contract — `out_dir.mkdir` before guarded block, permission errors raise uncaught traceback |\n\n## Privacy Pre-Scan Results\n\nAll scans passed clean:\n- **Hardcoded home paths:** None found\n- **Emails/phones:** None found\n- **Credentials/secrets/tokens:** None found\n- **Path.home()/expanduser:** None found\n\n## Git Repository State\n\nThe adversarial-spec directory has its own `.git` (not a submodule of Hermes skills). The review did NOT push or modify origin — findings are advisory for the next maintainer.\n\n## Key Action Items\n\n1. **Fix stash loss (A1, B2, B3):** Retain `stash_id` in error paths; `_restore` or auto-pop on setup failure.\n2. **Fix exit code masking (A5, B1):** Return `EXIT_INFRA` (or distinct code) on failed squash-merge.\n3. **Write final.json on infra failures (A3, B9):** Write `{\"verdict\":\"ERROR\",\"error\":...}` before early return.\n4. **Tighten spec validation (B4):** Require `version`, `author`, `targets`, `requirements`, `acceptance_criteria`.\n5. **Scope commit_all (B5):** Only commit `spec.md`, not arbitrary working-tree changes.\n6. **Fix convergence loop (A2):** Treat \"all settled + REJECT\" as terminal disagreement, not loop-retry.\n7. **Deduplicate results (B7):** Reject duplicate IDs in verification output.\n8. **Prevent artifact leakage (B8):** `.gitignore` any `--out` path, not just the default.\n9. **Version artifact directories (A4):** Mirror branch numbering `out_base/<feature>/<N>`.\n10. **Fix ISSUES.md header (A7):** Update stale claim about auto-append location.\n11. **Make verify prompt exact (A9):** Pass actual revise commit SHA instead of hardcoded `HEAD~1`.\n\nFile v0.1.0:references/claude-timeout-notes.md\n\n# Claude Fable 5 Timeout Notes\n\nThis is a provider-specific operational record, not provider-selection policy.\nProvider choice and fallback ordering come from external configuration.\n\nWhen using Claude Fable 5 as spec-challenger or plan-challenger:\n\n- Extended thinking takes 8-12 min per response\n- The default adversarial-spec/adversarial-plan timeout of 600s is often insufficient\n- Increase `--timeout` to at least 1200 when Claude is the `--review-cmd`\n- Pair with `--hard-timeout 1800` inside the claude-tmux command\n- If Claude exits code 3 (REJECT) due to non-parseable JSON, the output artifact\n  may contain conversation text instead of JSON — retry using the next eligible\n  provider from external configuration\n\nValidated: 2026-07-10, adversarial-spec with Claude Fable 5 succeeded at 1200s timeout.\n\nFile v0.1.0:references/claude-tmux-wrapper-system-notes.md\n\n# Claude-tmux Wrapper Usage Notes (v1)\n\nThis reference documents one wrapper's observed behavior. It does not prescribe\nprovider selection, model selection, or fallback ordering for adversarial skills;\nthose choices remain in external configuration.\n\n## Wrapper entry point\nConfigure the wrapper entry point externally and refer to it by its portable\ncommand name, `claude-tmux.py` (342 lines in the reviewed v1). Shared reference\nassets must not record a user-specific absolute path.\n\n## Key flags\n\n| Flag | Purpose | Note |\n|------|---------|------|\n| `--timeout N` | Inactivity timeout in seconds (default 300) | Sets how long to wait for Claude to write output. If Claude responds in chat instead of using Write tool, the wrapper waits this long before timing out. |\n| `--hard-timeout N` | Wall-clock timeout (default 0 = disabled) | Total max runtime regardless of activity. Useful as safety net. |\n| `--cwd DIR` | Working directory for the tmux session | Required for adversarial pipeline — without it, tmux starts in $HOME and models can't find spec.md/plan.md. |\n| `--model MODEL` | Model name (default: sonnet) | Do NOT force this from provider config — let the wrapper use its default. If the user wants a specific model, they change it here, not in adversarial configs. |\n| `--max-turns N` | Max conversation turns (default 10) | Rarely needs changing for adversarial pipeline (single-turn prompt). |\n| `--no-danger` | Skip `--dangerously-skip-permissions` | Default is DANGEROUS (danger=True). Pass `--no-danger` for untrusted input. |\n\n## Does NOT support `--yolo`\nThe v1 wrapper has `args.danger = True` by default. There is no `--yolo` flag.\nPassing `--yolo` causes argparse to exit with code 2 (unknown argument).\nJust omit it entirely. Validated 2026-07-16.\n\n## Known failure mode: Claude replies in chat instead of Write tool\nThe wrapper appends to the prompt:\n```\nWhen you are done, write your response to {output_file}\nusing the Write tool. After the file is written, create an empty\nfile at {done_sentinel} using the Write tool to signal completion.\n```\n\nSometimes Claude outputs JSON into the chat pane instead of using the Write tool.\nThe wrapper then waits indefinitely (until --timeout) for output.txt/done.sentinel to appear.\n\n**Workaround:** The findings are visible in the tmux pane. Capture with:\n```\ntmux capture-pane -t claude-tmux-<PID> -p\n```\nThen kill the stuck session:\n```\ntmux kill-session -t claude-tmux-<PID>\n```\n\nThe adversarial pipeline will get an error exit from the wrapper. If the WRITE phase\nalready committed, the branch is recoverable from git reflog.\n\n## Model defaults\nDefault model is `sonnet` (defined in the wrapper's argparse). In the reviewed\nenvironment, the aliases resolved as follows:\n- `sonnet` = Claude Sonnet 4 (fast, reliable JSON, no extended thinking)\n- `best` = Claude Fable 5 (extended thinking 8-12 min, separate quota from Pro)\n\nDon't hardcode `--model` in provider config. The wrapper's default is fine for most cases.\n\nFile v0.1.0:references/phase-challenge-prompt-reduction.md\n\n# Challenge prompt reduction — 2026-07-15\n\n`phase_challenge.py` in adversarial-spec was embedding the full spec text\n(~1-15 KB) into the challenge prompt at line 55:\n\n```python\nf\"--- spec.md ---\\n{spec_text}\"\n```\n\nThis contradicted the SKILL.md's own rule: \"NEVER embed the brief or spec text\nin the challenge prompt.\" The model was instructed to read from disk via `--cwd`,\nbut then received the entire file again inline.\n\n## Fix\n\n`_build_prompt()` no longer accepts `spec_text` as a parameter. The signature\nchanged from `_build_prompt(spec_text, branch_point=\"\")` to\n`_build_prompt(branch_point=\"\")`. The spec text reference was removed from the\nreturn string. An earlier version of this fix was incomplete because\n`run_challenge()` still appended the text as `--- current spec.md ---`; the\nfinal fix removes that caller-level append as well. The only prompt supplied to\nthe provider is now the result of `_build_prompt(branch_point)` (plus the\nstatic JSON-only reminder on retry).\n\nPrompt size dropped from ~1-15KB (variable, depends on spec length) to under\n1KB (the instruction plus the branch-point identifier). With the current\ntemplate it is 705 chars for a 40-character SHA and does not vary with the\nspec length.\n\n## Files changed\n\n- `scripts/phases/phase_challenge.py`: `_build_prompt()` signature and body,\n  call site `run_challenge()` updated to pass `branch_point` only and no longer\n  append the on-disk spec text.\n- `tests/test_phases.py`: regression coverage verifies that `run_challenge()`\n  produces the same base prompt for short and long specs, excludes both known\n  spec markers and both file contents, and preserves those properties on the\n  invalid-JSON retry path.\n\n## Validation\n\n```\nPASS — 8/8 checks\n  prompt length: 705 chars with a 40-character branch-point SHA\n  provider prompt equals _build_prompt(branch_point)\n  prompt does not vary with short versus long spec content\n  no spec marker or spec content in prompt\n  retry prompt contains only the base prompt and static JSON reminder\n  no spec_text in function signature\n  JSON schema present\n  branch_point still accepted\n```\n\n## Sibling fix\n\n`adversarial-plan/scripts/phases/phase_challenge.py` was patched earlier\n(2026-07-14) with the same approach: removed embedded plan+spec text, prompt\nnow ~724 chars.\n\nFile v0.1.0:references/provider-agnostic-design.md\n\n# Provider-Agnostic Design — Architectural Decision\n\n## Decision\n\nAll adversarial skills (spec, plan, code-loop, code-review) shall be **fully\nmodel-agnostic**. No skill hardcodes model names, provider aliases, CLI commands,\nor fallback chains. Provider selection is entirely external configuration.\n\n**Exception — legacy zero-config defaults.** A single, narrow, explicitly\ndocumented exception is permitted: a skill MAY carry a built-in last-resort\ndev/review command pair, used ONLY when no provider registry\n(`--provider-config`) and no role-specific env var / flag is supplied. This\nexception exists solely so the skill runs zero-config. It is not a license to\nhardcode model pairing rules, per-phase model choices, or fallback chains in\nskill guidance; those remain prohibited. The two defaults are intentionally\ndifferent models so writer and challenger never collapse into one voice.\n\n## Scope\n\nThis rule governs normative skill behavior and guidance. Reference notes may name\na provider, model, or wrapper when recording observed, provider-specific behavior,\nbut they must not prescribe provider selection or fallback chains. Any retry that\nchanges providers follows the externally configured provider order. The sole\ndeviation permitted is the legacy zero-config default-command exception named\nabove; nothing in skill guidance may resurrect hardcoded fallback chains.\n\n## Rationale\n\nThe user corrected this directly on 2026-07-16:\n\n> *\"Les fallback devraient pas etre hardcodés, ca devrait aussi se configurer,\n> j aimerais que les skills ne mentionnent pas specifiquement les modeles et outils,\n> ca devrait etre juste notre configuration\"*\n\n## What changed\n\n**Before:** adversarial-spec SKILL.md had:\n- A \"Model pairing rules\" section with specific models (Codex writer, Claude Fable 5 challenger)\n- Pitfalls with hardcoded commands and model-specific workarounds\n- `--dev-cmd`/`--review-cmd` defaults hardcoded to specific model commands\n\n**After:**\n- \"Provider selection\" section says model selection is external, skills don't hardcode\n- Pitfalls reference generic \"provider command\" failures, not specific models\n- CLI docs show no default commands\n- related_skills updated to include `grilling` alongside `grill-me`\n\n## What was removed (model-specific content previously in the skills)\n\nFrom adversarial-spec:\n- \"Preferred pairing: Codex (writer) + Claude Fable 5 via tmux (challenger)\"\n- \"Fallback when Claude quota low / timeout: GLM-5.2...\"\n- \"DeepSeek is NOT a fallback for spec-challenger unless explicitly requested\"\n- Pitfall about Fable 5 reliability, Codex stalling with reasoning=high\n- \"Validated pairing (2026-07): Codex DEV + Claude REVIEW\"\n\nFrom adversarial-plan:\n- CLI default commands with pi/glm-5.2 and pi/deepseek\n- Integration example with hardcoded commands\n- Pitfall about Fable 5 success as plan-challenger\n- \"Validated end-to-end pairing: Codex DEV + Claude Fable 5 REVIEW\"\n\n## What remains as an intentional legacy zero-config fallback\n\nTwo literal defaults are **deliberately kept** in `scripts/adversarial_spec.py`\n(`DEFAULT_DEV_CMD`, `DEFAULT_REVIEW_CMD`) as the last-resort command used when\nno provider registry (`--provider-config`) and no role-specific env/flag is\nsupplied:\n\n- Writer (dev): `pi --provider zai --model glm-5.2`\n- Challenger (review): `pi --provider deepseek --model deepseek-v4-pro`\n\nThis is the **one narrow, documented exception** to the \"no skill hardcodes\nmodel names\" rule, not an oversight. They exist so the skill runs zero-config,\nand the two are intentionally different models so writer and challenger never\ncollapse into the same voice. Resolution order in this legacy mode is:\nexplicit `--dev-cmd`/`--review-cmd` → `ASPEC_DEV_CMD`/`ASPEC_REVIEW_CMD` →\nthese hardcoded defaults. With a provider registry configured, the registry's\nordered providers are used instead and these literals never apply.\n\n## How provider selection works now\n\n1. User configures `~/.config/adversarial/providers.yaml` with ordered provider lists per role\n2. Each entry has: alias, command string, optional quota_check flag, optional stop_threshold\n3. Pipeline loads this config at startup via `--provider-config` flag or default path\n4. Before each phase, the resolver checks quotas and picks the first available provider\n5. Explicit `--dev-cmd`/`--review-cmd` still bypass quota checks (backward compat)\n\nSee the `quota-aware-provider-registry` spec for the full design.\n\n## Threshold-based provider selection\n\nProviders can specify a `stop_threshold`:\n- **Percentage-based** (Claude, Codex, GLM sliding window): provider skipped when\n  `used_pct > threshold` (default 100).\n- **Balance-based** (DeepSeek, Gemini credits): provider skipped when\n  `balance < threshold` (default 0).\n\n## Force modes\n\n- `--force`: globally bypass quota checks, use first provider per role\n- `--force-provider <role>:<alias>`: force one role's alias, others still check quotas\n\n## User's ordering preference\n\n**DeepSeek always last** in every fallback chain. Rationale: DeepSeek has prepaid\ntoken balance (stop in $), so consume free/rate-limited options (Claude, GLM) first.\nGLM-5.2 (Z.AI Lite, 80 req/5h) preferred before DeepSeek credits.\n\n## Config is external\n\nConfig: `~/.config/adversarial/providers.yaml`. Skills never ship or install it.\n\nFile v0.1.0:_retrospective/ISSUES.md\n\n# adversarial-spec — retrospective issues\n\nThis file is only for manually curated post-mortem notes; the pipeline does not\nwrite to it. `scripts/adversarial_spec.py` instead appends failures to the\n`ISSUES.md` inside the configured output directory (`<out_dir>/ISSUES.md`), one\nentry per failed phase (phase, branch, error, and last stdout).\n\nFile v0.1.0:skill-card.md\n\n## Description:\n\nAdversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chpomob](https://clawhub.ai/user/chpomob)\n\n### License/Terms of Use:\n\n0BSD\n\n## Use Case:\n\nDevelopers and engineers use this skill to turn a brief or feature idea into a structured specification with frontmatter, requirements, acceptance criteria, and target file descriptions. It is intended for specification work that benefits from a writer and challenger review loop before implementation planning.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installer and model-driven phases have broad code execution and repository mutation authority.\n\nMitigation: Run provider CLIs in a sandbox or disposable worktree, require safe permission modes, and merge only after reviewing that the final diff is limited to expected files such as spec.md.\n\nRisk: The one-line curl-to-shell install path can execute unreviewed remote code.\n\nMitigation: Avoid the one-line install path; install only from pinned, reviewed commits.\n\nRisk: Briefs, repository files, generated spec.md, and provider outputs may be untrusted.\n\nMitigation: Treat all pipeline inputs and generated outputs as untrusted and review them before using the resulting specification.\n\n## Reference(s):\n\n- [Server-resolved GitHub source](https://github.com/chpomob/adversarial-spec)\n- [Provider-Agnostic Design](references/provider-agnostic-design.md)\n- [Challenge Prompt Reduction](references/phase-challenge-prompt-reduction.md)\n- [Pre-Publication Adversarial Review](references/adversarial-2026-07-14-pre-publication-review.md)\n- [Claude-tmux Wrapper Usage Notes](references/claude-tmux-wrapper-system-notes.md)\n- [Claude Fable 5 Timeout Notes](references/claude-timeout-notes.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown specification files with YAML frontmatter and supporting CLI guidance]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces spec.md on disk and may create git branches, commits, and run artifacts during execution.]\n\n## Skill Version(s):\n\n0.1.0 (source: server release metadata; artifact frontmatter states 1.1.0)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v0.1.0:spec.md\n\n---\nname: quota-aware-provider-registry\nversion: \"1.0\"\nauthor: adversarial-spec\nstatus: draft\ntargets:\n  - file: adversarial_common/quota.py\n    description: \"New module — quota resolver that checks provider availability and returns the best command to run.\"\n  - file: adversarial_common/runner.py\n    description: \"Modified — run_phase() integrates quota check before executing commands, with automatic fallback chain.\"\n  - file: adversarial_common/__init__.py\n    description: \"Export new quota module symbols.\"\n  - file: adversarial_common/providers.py\n    description: \"Add ProviderConfig dataclass and YAML config loader for provider registries.\"\n  - file: adversarial_common/report.py\n    description: \"Add quota metadata to the final report (which provider was used, quota state at decision time).\"\n  - file: adversarial_common/jsonio.py\n    description: \"No changes needed — already handles JSON read/write used by check-ai-quota.py output.\"\n---\n\n# Quota-Aware Provider Registry\n\n## Problem\n\nThe adversarial pipeline (spec, plan, code loop) launches model commands blindly — it\nexecutes `--dev-cmd` and `--review-cmd` without knowing whether the target model has\nremaining quota. When a model exhausts its rate limit mid-pipeline (Claude 5h sliding\nwindow, Codex monthly cap, GLM-5.2 80 req/5h, Fable 5 separate limit), the running\nphase fails with a cryptic timeout or error, the pipeline restarts from scratch after\na manual `--resume`, and the user must manually check quotas with `check-ai-quota.py`\nbefore each launch.\n\nThe user already has:\n- A `hermes-quota-status` plugin with `quota_api.py` that checks Claude, Codex, Gemini,\n  GLM (Z.AI), and DeepSeek quotas via direct API calls.\n- A `check-ai-quota.py` script in adversarial-code-review that wraps the plugin into\n  a CLI with `--json` output for programmatic consumption.\n- Hardcoded fallback rules in SKILL.md (\"if Claude quota low → GLM\", \"if Fable 5\n  blocked → Sonnet\") that the user applies manually.\n- A clear preference: **Claude as primary**, **Codex as secondary**, with fallback\n  chains that differ per role (DEV vs REVIEW vs CHALLENGER).\n\nWhat's missing: an automated, model-agnostic layer that selects the right command for\neach phase based on real-time quota data, without the pipeline needing to know what\n\"Claude\" or \"Codex\" actually are.\n\n## Requirements\n\n- R1: The pipeline shall check provider quotas before launching each phase (DEV,\n  REVIEW, VERIFY, ARBITER, CHALLENGE) and select the best available command.\n- R2: The quota integration shall be fully model-agnostic — the pipeline never\n  hardcodes model names; it works with provider aliases resolved by an external\n  quota checker.\n- R3: The system shall support a configurable ordered list of providers per role\n  with fallback semantics (try #1 → quota low → try #2 → quota exhausted → try #3).\n- R4: When no provider in the fallback chain has available quota, the phase shall\n  report a clear \"no provider available\" error with per-provider quota snapshots\n  and exit the pipeline cleanly (not timeout).\n- R5: The quota check shall be fast — a single parallel call to\n  `check-ai-quota.py --json` that resolves all known providers in one shot, using a\n  **global cache** shared across all roles (Claude à 80% l'est pour tous les rôles).\n  Configurable TTL (default 30s) to avoid hammering provider APIs on every sub-phase.\n  The cache is keyed by provider alias, not by role.\n- R6: The system shall log which provider was selected, why (quota state), and the\n  raw quota snapshot in the pipeline's final report (final.json / final.md).\n- R7: The provider registry shall be loaded from an external YAML file specified via\n  `--provider-config` CLI flag, `ADVERSARIAL_PROVIDER_CONFIG` environment variable,\n  or default path `~/.config/adversarial/providers.yaml`. No provider config file\n  shall be shipped inside any skill directory.\n- R8: The quota resolver shall handle the case where `check-ai-quota.py` is not\n  installed or returns errors — fall back to executing the primary command directly\n  (legacy behaviour) with a warning logged.\n- R9: The system shall support environment variable overrides per role\n  (`ADVERSARIAL_DEV_PROVIDERS`, `ADVERSARIAL_REVIEW_PROVIDERS`) as inline JSON\n  that overrides the YAML config without touching files — useful for pipeline\n  orchestration where the config file is read-only or in CI.\n- R10: The existing `--dev-cmd`, `--review-cmd`, `--arbiter-cmd` CLI flags shall\n  still work as positional overrides: when the user passes an explicit `--dev-cmd`,\n  quota checking for that role is skipped (the explicit command wins). This preserves\n  backward compatibility and manual override.\n- R11: The report shall include a `provider_history` array tracking every phase's\n  provider decision: which alias was selected, quota state at decision time, and\n  whether a fallback was triggered.\n- R12: All error messages and report fields shall be in English (pipeline convention).\n  User-facing CLI output (--help, warnings) stays in the conversation's language.\n- R13: Command strings in the provider config shall support `{workdir}` as a placeholder\n  that the resolver substitutes with the effective workdir at execution time. This allows\n  commands like claude-tmux's `--cwd` to point to the correct project directory without\n  hardcoding paths.\n- R14: The system shall include and maintain a `check-ai-quota.py` CLI wrapper that exposes\n  `--glm` and `--deepseek` flags in addition to the existing `--claude`, `--codex`,\n  `--gemini` flags. This ensures all providers used in the fallback chain have a quota\n  check path, preventing silent UNKNOWN fallback for GLM and DeepSeek.\n- R15: Each provider entry in the config may specify a `stop_threshold` field. For\n  **percentage-based** providers (Claude, Codex, GLM sliding window), it represents the\n  max used_pct before the provider is skipped (default: 100). For **balance-based**\n  providers (DeepSeek, Gemini credits), it represents the minimum remaining balance\n  before the provider is skipped (default: 0 — use until empty). The resolver shall\n  detect which model a provider uses from its quota response schema (presence of\n  `balance` vs `session.used_pct`).\n- R16: A `--force` CLI flag shall bypass all quota checks for all roles. The pipeline\n  uses the first provider in each role's config chain regardless of quota state.\n  Useful for degraded mode, testing, or when quota APIs are down.\n- R17: A `--force-provider <role>:<alias>` CLI flag shall force a specific provider\n  alias for a single role (e.g. `--force-provider review:deepseek`). Other roles\n  still check quotas normally. This allows unblocking a specific phase without\n  disabling quota awareness globally.\n\n## Acceptance criteria\n\n- AC1 (R1): Run a pipeline with two providers configured (claude→deepseek). Block\n  Claude's quota artificially (set session_pct=100 in mock). Pipeline selects deepseek.\n  Phase completes. Verified via final.md showing deepseek as selected provider.\n- AC2 (R2): Add a new provider with alias \"my-model\" to the YAML config. Provide\n  a working quota check script that returns OK for it. Pipeline uses it without any\n  code changes. No model name string appears in runner.py or quota.py source.\n- AC3 (R3): Configure providers.prod: cmd1, cmd2, cmd3. Set cmd1 to simulate\n  RATE-LIMITED, cmd2 DRAINING, cmd3 OK. Pipeline selects cmd3. Set cmd3 to\n  RATE-LIMITED too. Pipeline reports \"no provider available\" with a snapshot of\n  all three states and exits code 3 (REJECT).\n- AC4 (R5): Run a pipeline with 4 phases. First check-quota call takes ~1s (parallel\n  HTTP calls). Subsequent phase checks in the same 30s window return cached results\n  (sub-millisecond). Verify only one HTTP batch per TTL window.\n- AC5 (R6): After a pipeline run, final.json contains a `provider_history` array.\n  Each entry has phase name, selected alias, quota state (OK/DRAINING/RATE-LIMITED),\n  and `fallback: true/false`.\n- AC6 (R8): Run a pipeline on a system without check-ai-quota.py or quota_api.py.\n  Pipeline runs normally using the primary command for each role, with a warning\n  logged to stderr. Exit code is the same as if the script had run without quota\n  awareness (legacy behaviour).\n- AC7 (R9): Set `ADVERSARIAL_DEV_PROVIDERS='[{\"alias\":\"claude\", \"cmd\":\"...\"}]'`\n  in environment. Pipeline ignores the YAML file's dev section and uses the env var.\n- AC8 (R10): Run pipeline with `--dev-cmd \"echo primary\"`. Pipeline skips quota\n  check for DEV role, executes the explicit command directly. Other roles still\n  check quotas.\n- AC9 (R11): After a 3-phase pipeline run, provider_history has exactly 3 entries\n  (one per phase that checks quotas). Each entry has `phase`, `alias`, `quota_state`,\n  `fallback` fields. Fields are non-empty.\n- AC10 (R12): All fields in final.json are in English. CLI --help output may be in\n  French when the user's session language is French.\n- AC11 (R13): A provider config entry with cmd containing `{workdir}` executes with\n  `{workdir}` replaced by the absolute path of the pipeline's working directory.\n  Verified by running `echo {workdir}` as a provider command and checking the phase log\n  contains the expected path. Trailing slashes shall be stripped.\n- AC12 (R14): `check-ai-quota.py --json --glm` returns a structured JSON response\n  with GLM quota data. `check-ai-quota.py --json --deepseek` returns structured JSON\n  with DeepSeek balance data. Both return exit 0 on success, exit 1 with an error\n  message if credentials are missing.\n- AC13 (R15): Configure DeepSeek with `stop_threshold: 2.0`. Set mock balance to $1.50.\n  Pipeline skips DeepSeek and falls to next provider. Set mock balance to $5.00.\n  Pipeline selects DeepSeek. Same test with Claude: `stop_threshold: 90`, mock\n  used_pct=95 → skipped, mock used_pct=80 → selected.\n- AC14 (R16): Run pipeline with `--force`. All providers in RATE-LIMITED state.\n  Pipeline uses the first provider for each role anyway. Phases execute normally.\n- AC15 (R17): Run pipeline with `--force-provider review:deepseek`. Set Claude to\n  RATE-LIMITED, DeepSeek to OK. Review phase uses DeepSeek (the forced one, not\n  the fallback chain). DEV phase still checks quotas normally for its own chain.\n\n## Provider config file — user provided\n\nThe user creates this file (e.g. at `~/.config/adversarial/providers.yaml`)\nand passes it to the pipeline via `--provider-config`. Example content:\n\n```yaml\n# One provider section per role. Ordered by preference.\n# First provider with available quota wins.\ndev:\n  - alias: codex\n    cmd: \"codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --sandbox workspace-write\"\n    quota_check: --codex\n    stop_threshold: 95       # skip if used_pct > 95%\n\n  - alias: glm\n    cmd: \"pi -p --provider zai --model glm-5.2 --thinking high\"\n    # No quota_check → treated UNKNOWN (used anyway with warning)\n\nreview:\n  - alias: claude\n    cmd: \"python3 /path/to/claude-tmux.py --timeout 600 --hard-timeout 1800 --cwd {workdir}\"\n    quota_check: --claude\n    stop_threshold: 90       # skip if used_pct > 90%\n\n  - alias: glm\n    cmd: \"pi -p --provider zai --model glm-5.2 --thinking high\"\n\n  - alias: deepseek\n    cmd: \"pi -p --provider deepseek --model deepseek-v4-pro --thinking high\"\n    quota_check: --deepseek\n    stop_threshold: 2.0      # skip if balance < $2.00\n\nchallenger:\n  - alias: claude\n    cmd: \"python3 /path/to/claude-tmux.py --timeout 900 --hard-timeout 1800 --cwd {workdir}\"\n    quota_check: --claude\n\n  - alias: glm\n    cmd: \"pi -p --provider zai --model glm-5.2 --thinking high\"\n\n  - alias: deepseek\n    cmd: \"pi -p --provider deepseek --model deepseek-v4-pro --thinking high\"\n    quota_check: --deepseek\n    stop_threshold: 3.0\n\n# Global quota_check command (used when per-entry quota_check is absent)\nquota_cmd: \"python3 /path/to/check-ai-quota.py --json\"\n\n# Cache TTL in seconds (default 30)\nquota_cache_ttl: 30\n```\n\n## Provider configuration is purely external\n\nNo skill — adversarial-code-loop, adversarial-spec, adversarial-plan, or adversarial-code-review —\nshall hardcode or ship defaults for any provider alias, command string, or fallback chain.\nThe skills know only about **roles** (dev, review, verify, arbiter, writer, challenger).\nWhich provider commands map to which role is entirely determined by the user's config.\n\n**Rationale:** The skills are a model-agnostic orchestration framework. They run commands\nand check quotas; they do not know what \"Claude\", \"Codex\", \"GLM\", or \"DeepSeek\" are.\nHardcoding a default chain in any skill would:\n- Break when the user's preferred model lineup changes\n- Create a false implicit coupling between skills and specific providers\n- Violate separation of concerns (provider selection is operational config, not skill logic)\n\n## Quota state resolution\n\nThe quota resolver interprets `check-ai-quota.py --json` output via a simple\nstate machine:\n\n```\ncheck-ai-quota.py --json --claude\n  → {\"results\": {\"claude\": {\"session\": {\"used_pct\": 45}, \"status\": \"OK\"}}}\n\nState → OK:         used_pct < 50  → green, command can run\nState → DRAINING:   used_pct 50-99 → yellow, command can run but warn\nState → RATE-LIMITED: used_pct >= 100 or HTTP 429 → skip, try next provider\nState → KEY_INVALID: token missing or expired → skip, try next provider\nState → UNKNOWN:    no data or error → use command anyway (conservative)\n```\n\nWhen `--all` is passed, the script checks all known providers in parallel, returns\na combined JSON. The resolver picks the first provider in the config whose state\nis OK or DRAINING (in order of preference). RATE-LIMITED and KEY_INVALID are\nskipped; UNKNOWN is used but logged as a warning.\n\n## Impact analysis\n\n### adversarial-common (new code)\n- `quota.py` (~150 lines) — quota_cache, resolve_provider(), parse_quota_state().\n- `providers.py` (~60 lines added) — ProviderConfig dataclass, load_provider_config()\n  with YAML path arg + env override support.\n- `runner.py` (~40 lines modified) — run_phase_cmd() wraps command execution with\n  pre-flight provider selection.\n- `report.py` (~20 lines added) — provider_history appended to final report.\n\n### No defaults shipped\nNo skill ships a `.adversarial-providers.yaml`. Provider config is entirely external —\nloaded from a path provided by the user via:\n- `--provider-config <path>` CLI flag (available on all pipeline entry points)\n- `ADVERSARIAL_PROVIDER_CONFIG` environment variable\n- Fallback: `~/.config/adversarial/providers.yaml`\n\nSkills are clean of any model references. The pipeline is a pure orchestration framework.\n\n### Pipeline entry points affected\n- `adversarial-code-loop/scripts/adversarial_loop.py` — accept `--provider-config` flag\n- `adversarial-spec/scripts/adversarial_spec.py` — accept `--provider-config` flag\n- `adversarial-plan/scripts/adversarial_plan.py` — accept `--provider-config` flag\n- `adversarial-code-review/scripts/adversarial_review.py` — accept `--provider-config` flag\n\nEach entry point loads the config and passes it to `adversarial_common.runner.run_phase()`.\n\n### Not modified\n- `adversarial_common/jsonio.py` — already handles structured JSON output.\n- `adversarial_common/gitops.py` — no quota awareness needed.\n- `adversarial_common/gates.py` — no quota awareness needed.\n- `adversarial_common/snapshot.py` — no quota awareness needed.\n- `adversarial_common/costs.py` — orthogonal, already tracks costs post-hoc.\n\n## Out of scope (v2)\n\n- **Automatic retry after quota reset** — detecting that a provider came back and\n  retrying a failed phase. v1 just fails cleanly with a useful message.\n- **Quota-aware step scheduling** — reordering pipeline steps to fit within\n  available quota windows. v1 selects a provider per phase independently.\n- **Cost-aware provider selection** — preferring cheaper models when within quota.\n  v1 selects by availability only (preference order from config).\n- **Cross-pipeline quota coordination** — two concurrent pipelines sharing quota\n  state. v1's cache is per-process.\n- **HTML / visual quota dashboard** — the quota data is available in final.json\n  but no dashboard is built in v1.\n\n## Known open questions\n\n1. **How to detect Fable 5 separate quota from Claude Pro quota?** The\n   `quota_api.py` fetches Claude's 5h sliding window. Fable 5 has an independent\n   limit that requires a separate endpoint or heuristic (probe a small prompt\n   before launching the real one). v1 may treat Fable 5 as claude-claude alias\n   with an additional heuristic probe.\n2. **Should the quota check be per-phase or per-call?** The pipeline has\n   BUILD→REVIEW→FIX→VERIFY cycles. A single phase (e.g. REVIEW) may run a model\n   for 10+ minutes and consume quota. v1 checks before the phase starts; it cannot\n   detect mid-phase exhaustion. That's acceptable — the error would surface as a\n   phase timeout which is already handled.\n3. **What happens when two providers share the same alias name?** (e.g. \"claude\"\n   for both DEV and REVIEW). They have separate entries in separate role sections\n   of the YAML, so no collision. Same alias can have different quota_check commands\n   per role if needed.\n4. **How to handle GLM and Codex quotas?** `quota_api.py` already supports both.\n   The resolver just passes `--glm` or `--codex` to `check-ai-quota.py --json`.\n\nFile v0.1.0:LICENSE\n\nCopyright (C) 2026 chpomob\nSPDX-License-Identifier: 0BSD\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.","readmeExcerpt":"Skill: adversarial-spec Owner: chpomob Summary: Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval. Tags: latest:0.1.0 Version history: v0.1.0 | 2026-08-03T18:11:30.582Z | auto - Initial public release of adversarial","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"PHASE 0 ──→ GIT SETUP (branch, stash, init)\nPHASE 1 ──→ WRITE  (spec-writer reads brief, writes spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger critiques for gaps/contradictions)\nPHASE 3 ──→ REVISE (spec-writer amends spec.md per findings)\nPHASE 4 ──→ VERIFY (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit"},{"language":"bash","snippet":"python3 scripts/adversarial_spec.py \\\n  --brief <file>           # brief file (default: stdin)\n  --dev-cmd <cmd>          # default: pi --provider zai --model glm-5.2\n  --review-cmd <cmd>       # default: pi --provider deepseek --model deepseek-v4-pro\n  --workdir <dir>          # default: .\n  --max-loops <N>          # default: 2\n  --feature <name>         # default: from brief filename\n  --timeout <N>            # default: 600\n  --out <dir>              # default: .adversarial-spec\n  --provider-config <path> # external provider config (default: ~/.config/adversarial/providers.yaml)\n  --no-merge"},{"language":"yaml","snippet":"---\nname: \"feature-name\"\nversion: \"1.0\"\nauthor: \"adversarial-spec\"\nstatus: \"draft\"\ntargets:\n  - file: path/to/file.rs\n    description: \"What changes in this file\"\n---\n\n# Feature title\n\n## Problem\nWhat problem does this solve?\n\n## Requirements\n- Bullet list of functional requirements\n\n## Acceptance criteria\n1. Each requirement has at least one testable criterion"},{"language":"bash","snippet":"python3 scripts/adversarial_spec.py \\\n  --brief brief.md \\\n  --provider-config ~/.config/adversarial/providers.yaml"},{"language":"text","snippet":"PHASE 1 ──→ WRITE     (spec-writer turns brief into structured spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger attacks for gaps, contradictions, untestable criteria)\nPHASE 3 ──→ REVISE    (spec-writer amends per findings)\nPHASE 4 ──→ VERIFY    (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit"},{"language":"yaml","snippet":"---\nname: \"feature-name\"\nversion: \"1.0\"\nauthor: \"adversarial-spec\"\nstatus: \"draft\"\ntargets:\n  - file: path/to/file\n    description: \"What this file must do\"\n---\n\n## Requirements\n- R1: …\n- R2: …\n\n## Acceptance criteria\n- AC1 (R1): …\n- AC2 (R2): …"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: adversarial-spec\ndescription: \"Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval.\"\nversion: 1.1.0\nauthor: Hermes Agent\nlicense: 0BSD\nplatforms: [linux, macos]\nmetadata:\n  hermes:\n    tags: [adversarial, spec, planning, specification, requirements]\n    related_skills: [grill-me, adversarial-plan, adversarial-code-loop]\n---\n\n# Adversarial Spec\n\n**Brief → structured specification.** Two-role adversarial pipeline that transforms a\nvague idea (from grill-me or direct input) into a formal spec.md with YAML frontmatter,\nrequirements, acceptance criteria, and target file descriptions.\n\n## Installation\n\nRequires the `adversarial-common` sibling repo (shared engine). One-line install:\n\ncurl -fsSL https://raw.githubusercontent.com/chpomob/adversarial-spec/main/scripts/install.sh | bash\n\nor, from an existing checkout:\n\nbash scripts/install.sh\n\nBoth place adversarial-spec and adversarial-common side by side under `~/.hermes/skills` (override the target with `$1` or `$HERMES_HOME`).\n\n## Workflow\n\n```\nPHASE 0 ──→ GIT SETUP (branch, stash, init)\nPHASE 1 ──→ WRITE  (spec-writer reads brief, writes spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger critiques for gaps/contradictions)\nPHASE 3 ──→ REVISE (spec-writer amends spec.md per findings)\nPHASE 4 ──→ VERIFY (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit\n```\n\n## CLI\n\n```bash\npython3 scripts/adversarial_spec.py \\\n  --brief <file>           # brief file (default: stdin)\n  --dev-cmd <cmd>          # default: pi --provider zai --model glm-5.2\n  --review-cmd <cmd>       # default: pi --provider deepseek --model deepseek-v4-pro\n  --workdir <dir>          # default: .\n  --max-loops <N>          # default: 2\n  --feature <name>         # default: from brief filename\n  --timeout <N>            # default: 600\n  --out <dir>              # default: .adversarial-spec\n  --provider-config <path> # external provider config (default: ~/.config/adversarial/providers.yaml)\n  --no-merge\n```\n\n## Pre-flight checklist (orchestrator)\n\nRun these BEFORE writing the brief, especially when contributing to an upstream project:\n\n1. **Check CONTRIBUTING.md** — Project-specific rules for branch naming (`feat/`, `fix/`, `docs/`), commit message format (Conventional Commits), PR template fields, and test requirements. Incorporate these into the spec's acceptance criteria.\n2. **Check for pre-existing PR review feedback** — If the feature already has an open PR with reviewer comments (automated or human), read the findings and incorporate them into the brief. The spec should address what the review flagged, not re-propose rejected patterns.\n3. **Choose the right parent branch** — For upstream contributions, create a feature branch from `upstream/main` (not your fork's main): `git checkout"},{"path":"README.md","content":"# adversarial-spec\n\n**Brief → structured spec.** Two-role adversarial pipeline that takes a product brief, specification request, or feature idea and produces a `spec.md` with YAML frontmatter, numbered requirements, and acceptance criteria.\n\nFor Hermes Agent, Claude Code, Codex, or any LLM CLI.\n\n## How it works\n\n```\nPHASE 1 ──→ WRITE     (spec-writer turns brief into structured spec.md)\nPHASE 2 ──→ CHALLENGE (spec-challenger attacks for gaps, contradictions, untestable criteria)\nPHASE 3 ──→ REVISE    (spec-writer amends per findings)\nPHASE 4 ──→ VERIFY    (spec-challenger checks findings resolved)\nMERGE  ──→ squash-merge (APPROVED) or [REJECTED] commit\n```\n\n## Output format\n\n```yaml\n---\nname: \"feature-name\"\nversion: \"1.0\"\nauthor: \"adversarial-spec\"\nstatus: \"draft\"\ntargets:\n  - file: path/to/file\n    description: \"What this file must do\"\n---\n\n## Requirements\n- R1: …\n- R2: …\n\n## Acceptance criteria\n- AC1 (R1): …\n- AC2 (R2): …\n```\n\nSpecs are consumed by `adversarial-plan` for planning and `adversarial-code-loop` for implementation.\n\n## Comparison\n\n| Feature | adversarial-spec | zscole/adversarial-spec |\n|---------|-----------------|----------------------|\n| Structured frontmatter | ✅ YAML + targets + requirements | ❌ Free-form |\n| Acceptance criteria | ✅ Per-requirement ACs | ❌ |\n| Git-native pipeline | ✅ Branch-per-spec, squash-merge | ❌ Single file |\n| plan/code-loop integration | ✅ Direct feed to plan + loop | ❌ Standalone |\n\n## Quick start\n\n```bash\npython3 scripts/adversarial_spec.py \\\\\n  --brief /path/to/brief.md \\\\\n  --dev-cmd \"<your-dev-cmd>\" \\\\\n  --review-cmd \"<your-review-cmd>\"\n```\n\n## Dependencies\n\n- Python ≥ 3.11\n- Git ≥ 2.5\n- Two LLM CLIs (spec-writer + spec-challenger)\n\nUses `adversarial-common` as the shared engine.\n\n## License\n\n0BSD — see [LICENSE](LICENSE)."},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7e26az9x7m8bgwfwg90q1wkh8bsqw0\",\n  \"slug\": \"adversarial-spec\",\n  \"version\": \"0.1.0\",\n  \"publishedAt\": 1785780690582\n}"},{"path":"references/adversarial-2026-07-14-pre-publication-review.md","content":"# Pre-Publication Adversarial Review — adversarial-spec (2026-07-14)\n\n**Scope:** Full `--project-dir` review of `adversarial-spec` (10 Python files, ~1.8 KLOC).\n**Models:** Claude Fable 5 (Architect + Synthesis), Codex GPT-5.6-Sol (Inspector + Cross).\n**Privacy pre-scan:** Clean — no hardcoded paths, credentials, or personal info.\n**Verdict:** REQUEST_CHANGES (both reviewers, before synthesis).\n\n## Findings Summary\n\n| Bucket | Count | Severities |\n|--------|-------|-----------|\n| Architect | 10 | 1 major, 5 minor, 4 nit |\n| Inspector | 10 | 1 blocker, 7 major, 1 minor, 1 nit |\n| **Total unique** | **20** | **1 blocker, 8 major, 6 minor, 5 nit** |\n\n## Architect Findings (Claude Fable 5)\n\n| ID | Severity | File | Summary |\n|----|----------|------|---------|\n| A1 | **major** | `scripts/adversarial_spec.py:138` | Stash stranded if `_setup_git` fails after `stash_dirty` — error path returns without `stash_id`, `_restore()` in `finally` can't pop it, user's uncommitted work silently lost |\n| A2 | minor | `scripts/adversarial_spec.py:260` | Verifier REJECT-with-all-settled causes non-converging re-litigation — `remaining` is empty so `findings` stays at full list, next round revises already-settled findings → exhausts `max_loops` → REJECT |\n| A3 | minor | `scripts/adversarial_spec.py:100` | `final.json` contract broken on infra failures — `_phase_failed` returns `EXIT_INFRA` without calling `write_final_json`, caller polls stale or absent artifact |\n| A4 | minor | `scripts/adversarial_spec.py:355` | Artifacts dir keyed only by feature name — reruns silently overwrite `final.json`, `ISSUES.md`, etc. across runs of the same feature |\n| A5 | minor | `scripts/adversarial_spec.py:165` | APPROVED exit code (0) masks failed squash-merge — `_finish` catches `GitError`, sets `merged=false` in JSON, but still returns 0 |\n| A6 | minor | `scripts/adversarial_spec.py:385` | Stash-pop after squash-merge can conflict when user's dirty tree touched the same files — no conflict detection or specific warning |\n| A7 | nit | `_retrospective/ISSUES.md:3` | Stale claim that failures are auto-appended to this file — actually goes to `<out_dir>/ISSUES.md` now |\n| A8 | nit | `scripts/adversarial_spec.py:78` | `_ensure_ids` dedup generates awkward compound ids like `S1-3-3` |\n| A9 | nit | `scripts/phases/phase_verify.py:55` | Prompt hard-codes `git diff HEAD~1..HEAD` — wrong if writer CLI made multiple commits |\n| A10 | nit | `scripts/phases/phase_challenge.py:44` | Full spec embedded in prompt with no size guard — large specs can overflow context window |\n\n## Inspector Findings (Codex GPT-5.6-Sol)\n\n| ID | Severity | File | Summary |\n|----|----------|------|---------|\n| B1 | **blocker** | `scripts/adversarial_spec.py:185` | Git finalization failure still returns exit 0 — CI receives success despite `merged=false` |\n| B2 | **major** | `scripts/adversarial_spec.py:127` | Stash restoration failure doesn't change exit result — `_restore` only warns, `main` returns EXIT_APPROVED whil"},{"path":"references/claude-timeout-notes.md","content":"# Claude Fable 5 Timeout Notes\n\nThis is a provider-specific operational record, not provider-selection policy.\nProvider choice and fallback ordering come from external configuration.\n\nWhen using Claude Fable 5 as spec-challenger or plan-challenger:\n\n- Extended thinking takes 8-12 min per response\n- The default adversarial-spec/adversarial-plan timeout of 600s is often insufficient\n- Increase `--timeout` to at least 1200 when Claude is the `--review-cmd`\n- Pair with `--hard-timeout 1800` inside the claude-tmux command\n- If Claude exits code 3 (REJECT) due to non-parseable JSON, the output artifact\n  may contain conversation text instead of JSON — retry using the next eligible\n  provider from external configuration\n\nValidated: 2026-07-10, adversarial-spec with Claude Fable 5 succeeded at 1200s timeout."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval. Skill: adversarial-spec Owner: chpomob Summary: Adversarial specification writer. Takes a brief (from grill-me or user) and produces a structured spec.md with YAML frontmatter, requirements, acceptance criteria, and target files. Git-aware pipeline: each run on its own branch, squash-merge on approval. Tags: latest:0.1.0 Version history: v0.1.0 | 2026-08-03T18:11:30.582Z | auto - Initial public release of adversarial","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1393,"uniquenessScore":52,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T11:52:55.999Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T20:22:15.836Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}