Added
- Literal provider-model routing objects. Object-form
routing.rolesandrouting.phasesentries are now treated as literal provider/model selections, so an exact model ID such asminimaxai/minimax-m3is no longer reinterpreted as a capability alias. Legacy string routing, capability aliases, and cross-provider fallthrough are unchanged. (#734) - Three test systems that catch the class of defect this release fixes, rather than the instances. Documented
OCTOPUS_*variables are now held accountable against a manifest, so a variable can no longer ship inert with its documentation promising otherwise — the root cause shared by four separate bugs last cycle (closes #749). Any new test file unreachable by a CI gate now fails a test instead of silently asserting nothing; 34 already-unreachable files are baselined so the debt cannot grow while #741 triages them (#752, #757). And the unit suite additionally runs through a symlinked path, which is where two hermiticity failures hid (#758).
Fixed
- Orphaned provider children survived a spawn PID-capture timeout. When
spawn_agent_capture_pid()exhausted its wait budget, the timeout path abandoned the process subtree instead of reaping it, leavingcodex execchildren spending tokens after the caller believed the run was cancelled. This is the process-leak root cause behind the nine identicalcode-reviewprocesses reported in #736. (#744) - Cancelling a detached council seat raced its own teardown. The subtree is now frozen before descendants are terminated, so intermediate shells cannot advance to the next command mid-cancel, and the detached-seat wrapper takes a dedicated
USR1cancellation signal so it can reap its direct children and exit cleanly.SIGKILLremains only as a bounded fallback. (#761) - Council cancellation depended on
pgrep. Wherepgrepis absent, cancellation silently did nothing and left providers running. Aps-based child-discovery fallback restores it. Verified by mutation: with the fallback removed, cancelled seats survive. (#765) - Codex silently skipped the telemetry-webhook hook. The hook declared
async: true, which Codex does not support, so it was dropped rather than run. The declaration was redundant with the script's own design:telemetry-webhook.shalready backgrounds itscurlwith output redirected, measured at 37ms against a deliberately five-second endpoint. (refs #766) - Phase-output checkpoints were write-only for any hyphenated phase name.
get_phase_output()spliced the phase into a jq filter, sodebate-probeparsed as subtraction and failed withprobe/0 is not defined— every debate-gate checkpoint written byworkflows.shwas unreadable. The phase is now bound with--arg, which also closes the injection path that splicing opened. (closes #724) - Valid explicit write scopes were replaced by heuristic inference. Explicitly declared new file and directory scopes are now preserved when safely anchored under an existing repository path, instead of being discarded in favour of context-file guessing. Unsafe absolute, traversal, glob,
.git, and unanchored invented paths are still rejected, and parent/child overlap detection is retained for newly declared scopes. (#740) - The
codex-reviewseat ignoredOCTOPUS_CODEX_SANDBOX.codex exec reviewaccepts neither-s/--sandboxnor-p/--profile, so the seat silently inheritedsandbox_modefrom the user's~/.codex/config.toml. The already-resolvedcodex_sandboxvalue is now threaded through as a-c sandbox_mode=...override, the mechanism that subcommand does support. (#747) - A single failed provider in Round 1 of
/octo:reviewcould abort the entire code-review run.review_run()treated any spawn failure in the parallel specialist fleet as fatal, so one dead seat took the whole review down with it instead of continuing with the roles that spawned successfully. (closes #736) - The OpenRouter council seat was unusable regardless of a valid
OPENROUTER_API_KEY, from three independent bugs:council_detect_providers()probed for a nonexistentopenrouterbinary instead of theOPENROUTER_API_KEYenv var (dispatch actually goes through a shell function);run_with_timeout()'s in-process fallback ran an unguardedwaitafter signaling its monitor, soset -eo pipefailkilled the function — and the seat's already-captured output — right after the provider call succeeded; andopenrouter_execute()ignoredOCTOPUS_OPENROUTER_MODEL/providers.json, always resolving a model from a hardcoded table instead of the one advertised on the roster. (closes #738) - Five more
set -estatus leaks on barekill/wait. Fixing the OpenRouter seat above addressed one instance and left five: two inworkflows.sh's progressive-synthesis monitor teardown, two inheartbeat.sh's timeout escalation, and one incursor-agent.sh's fallback path.killon an already-reaped PID returns non-zero, which underset -eo pipefailaborts the enclosing function after its work has already succeeded. Thecursor-agent.shsite needed care rather than a blanket|| true: itswaitresult feedsexit_code=$?, so suppressing the failure would have silently reported a failed provider as successful. (closes #751)
Internal
- Regression coverage for the three OpenRouter council-seat fixes above, which landed without tests of their own (#742).
memory_scopetest expectations are derived from git rather than a logical path, so the suite stops failing on macOS where/tmpresolves to/private/tmp(#760).- CI answers
merge_groupevents, so a merge queue can be enabled without a workflow change. Note this is currently latent: merge queues are an organization-only GitHub feature and unavailable on a user-owned repository (#763). - Nine independent PRs were merged through one integration branch rather than serially. Under
strict: truebranch protection every merge invalidates every other PR's status, so a queue of independent changes costs one full CI cycle each; batching them proved the serialization was pure overhead — all nine merged with zero conflicts (#764).