Devin CLI + opencode CLI as worker backends - #3
Merged
Conversation
Adds devin + opencode to resolve-model.py (backend enum + tier->model maps; opencode uses provider/model strings, multi-provider) and validate-manifest.py VALID_BACKENDS + worktree-required set. New adapter-devin.md (Devin -p headless, --permission-mode dangerous; Devin -p run VERIFIED LIVE with an authenticated account: exit 0, bare-text stdout, a retryable transient-capacity error class, cwd-reset noted) + adapter-opencode.md (opencode run --format json, multi- provider, MANDATORY env-scrub — opencode authed with zero creds via inherited ANTHROPIC_BASE_URL). Both LOWER-TRUST, opt-in, WORKER-ONLY (excluded from the arbiter panel — multi-provider family != fixed backend family). xhigh stays codex-only; no-Haiku respected; no fabricated metrics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the codex/cursor worker template: worktree-at-HEAD (caller owns git base), timeout supervisor with </dev/null + bounded output, job_result in $ART (not $WT — avoids a false scope-gate BLOCKED), git-derived scope gate via compound-v-scope-check.py (never self-reported). opencode worker does a VERIFIED-live env-scrub (env -u ANTHROPIC_*/OPENAI_* — leak-canary proven) so it can't borrow the dispatcher's provider creds, + a restrictive opencode.json. Devin session-id/rich-failure-classification marked auth-pending (safe default: empty session, 'other' class; empty-stdout-on-exit-0 -> error). bash -n + shellcheck clean; E2E verified with inert stand-ins (success/BLOCKED/timeout). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
HIGH: validator now REJECTS a reviewer job on backend devin/opencode (worker-only enforced, not just documented); opencode.json backup/restore is symlink-safe + exit-trap-guarded (no write outside the worktree); opencode false-success closed (exit 0 with zero valid JSONL events / no final text -> error). MED: env-scrub is now an env -i ALLOWLIST (leak-canary proven vs ANTHROPIC_BASE_URL/API_KEY + OPENCODE_API_KEY); opencode provider/model shape enforced in resolver + validator; '--' terminator before the opencode prompt (live-verified leading-dash prompts); Devin --export dropped (was outside the byte cap); glob-safe WRITE_ALLOWED split (set -f). Selftests + bash-n + shellcheck green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n-string model) opencode.json BACKUP artifact is now symlink-safe: private artifact dir validated non-symlink, backup path must not pre-exist (rm -f before cp), and restore refuses a symlink/tampered backup (fail-closed -> scope gate BLOCKS rather than reading attacker-redirected content). Validator now rejects a NON-STRING opencode model (42/list/dict) that previously bypassed the provider/model shape check. Live-proven (mid-run symlink swap -> blocked) + 3 negative selftests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
copeus
added a commit
that referenced
this pull request
Jul 13, 2026
…; usage-object conformance; advisor_calls derived Codex findings #1(BLOCKER),#2,#3,#4: empty/string/negative usage no longer becomes measured:true 0/0; aggregate emits null (not real 0) when nothing measured; advisor_calls aggregates independent of token-measured; collect-results deep-validates the usage object + derives advisor_calls from the per-job advisor log (<run-dir>/logs/<job-id>.advisor.jsonl). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
copeus
added a commit
that referenced
this pull request
Sep 1, 2026
…ucer Round 3 found four judgment-layer defects the validator cannot see, and one of them would have deadlocked every run: - The gate receipt had a home (task-6's schema) and a consumer (task-17's postcondition) and NO PRODUCER. With no writer, every receipt is missing, the authority re-derives for every job, and integration never completes. task-9's Gate stage now emits the six fields and Record persists them. - task-17's own acceptance contradicted itself on the missing case -- 're-derive and that verdict wins' beside 'refuse until every job has a valid receipt'. MISSING now means RE-DERIVE (derived verdict authoritative); only a PRESENT receipt whose digests disagree is refused outright, because that is a forged claim rather than an absent one. - The declared waves lied: round 2 added task-16 depends_on task-4 without moving task-16 out of wave 2, ahead of its own prerequisite. Recomputed to seven waves. - Manifest AC-02 still demanded the mode-scoped default the spec had deleted, and two jobs explicitly disclaim it -- the reviewer would have been asked to rule on a deliberately unimplemented requirement. - AC-06's repo-wide sweep was contradicted by task-14, which correctly leaves the unamended invariant in CHANGELOG history. Scoped to live files. Also: task-17 drops write access to scope-check.py (invoking needs none, and task-15 consumes that matcher one wave earlier); AC-24 and AC-25 give the integration authority and the cross-vendor path feature-level criteria they lacked; task-0 names the third target its CI glob activates; the plan's partition map is refreshed for tasks 15-17. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
copeus
added a commit
that referenced
this pull request
Sep 1, 2026
…stead of shapes Three things, and each of them exists because the alternative fails silently. SCOPED becomes its own cohort. `_cohorts` collapsed everything that was not fast-path into `fullpipeline`, which was correct while there were two tiers and wrong the moment the engine grew a third: a whole new population would have been folded into the escalation-evidence figure `/v:status` renders beside fast-path precision, and nobody reading that number would have known it had changed meaning. `tier2_lookup` keeps its `escalation_evidence_n` aggregate exactly as it was (Iron-Invariant #3 is unchanged -- a SCOPED outcome is still evidence, never health) and reports `scoped_n` / `fullpipeline_n` alongside it, so the split is visible without shrinking a number other readers already consume. The decision strings and tier tokens are now READ FROM the engine. This file carried its own `FASTPATH_DECISION = "FASTPATH_ELIGIBLE"` literal, which was a second copy of a wire vocabulary the engine owns -- fine while nothing changed, and exactly how two halves drift apart when something does. `_preeval()` is deliberately not fail-soft: a load failure raises rather than falling back to hardcoded strings, because a silent fallback IS the duplicated vocabulary. A selftest reads this file's own source and fails if any decision string is ever assigned here again. The circuit breaker counts NEGATIVE ACTUAL OUTCOMES, not demotions. An earlier revision counted post-diff demotions alone and a cross-model review showed that cannot see the failure it exists to catch: a mis-sized change that stays one allowed, non-sensitive, sub-20-line file is never demoted, so twenty bad commits in a row leave the rate at zero and the breaker armed. The numerator is a demotion, a CI failure on the resulting commit, a revert of it, or a later escalation of the same work -- and the selftest builds exactly that blind spot (four DIRECT decisions, zero demotions, four CI failures) so a regression to demotion-only turns the suite red rather than quietly re-opening the hole. Three details are load-bearing: * It never delays the first decision. No warm-up gate, no min-sample floor of the kind `precision_stats` uses: an empty history is rate 0.0 and armed. The maintainer rejected a warm-up explicitly, so the safety property here is reactive, and the selftest proves both halves -- armed from decision one, disarmed once the rate crosses. * It walks the RAW event sequence, not the last-writer-wins reduction. A later `predicted` at a higher tier REPLACES the DIRECT one under LWW, so a reduction-based breaker would watch an escalated decision vanish out of its own denominator. * It reduces the WORKING-TREE stream while precision reduces the committed blob. Those answer opposite questions: precision must never be fabricated upward and so refuses what git cannot back; a safety latch must fail toward over-disarming, so a negative outcome that is recorded but not yet committed still disarms auto-route. "Disarms itself ... until a human re-arms it" is a latch, so it is appended to the same append-only stream as a `breaker` event rather than recomputed -- otherwise it un-disarms by itself the moment the bad outcomes roll out of the rolling window. A `rearm` requires a named human and sets the watermark the rolling rate counts from; without that watermark the re-arm is a no-op, because the decisions that tripped the breaker are still inside the window and the next evaluation re-disarms immediately. Nothing is erased: the stream stays append-only and the whole history remains readable. Selftest: 58 cases before, 110 after. Four mutations were run to prove the suite can fail -- demotion-only numerator (13 FAIL), SCOPED re-collapsed into fullpipeline (5 FAIL), a warm-up gate (8 FAIL), and a no-op re-arm (2 FAIL). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Adds Devin and opencode as opt-in, lower-trust, WORKER-ONLY headless backends (excluded from the arbiter panel — multi-provider family ≠ fixed backend family).
provider/model; xhigh stays codex-only; no-Haiku intact).</dev/null, git-derived scope gate, job_result in $ART). opencode env-scrub is live-proven (leak-canary) — it can't borrow the dispatcher's provider creds (it authed with zero creds via inheritedANTHROPIC_BASE_URL).-prun VERIFIED LIVE with an authenticated account (exit 0, bare-text stdout; a retryable transient-capacity error class; cwd-reset noted). Devin session-id + rich failure-classifier are auth-pending (safe defaults).Codex-reviewed (cross-model). Selftests + bash-n + shellcheck green.
🤖 Generated with Claude Code