Repository navigation
Sync local work: separate judge, OpenCode, bench container runner - #23
Conversation
…ner runner Squash of the local work since 2026-10-05 onto main (after PRs #14-#22): - --judge / --judge-url and MAAT_JUDGE_MODEL / MAAT_JUDGE_URL: checks and reviews run on a separate model; receipts name the judge. Short names `grok` and `opencode`; removed backends are refused at parse time. - OpenCode as an ACP worker and judge (opencode://subscription). - Check quality: lint + redraft, edge checks, late-seal and lazy-seal fixes, leftover-files nudge, reveal/bend guard, provider-error retries, lenient tool-argument JSON, provider-cap stop, local speed profile. - Grok Build fixes: seal gate inside the subprocess, late-join wait. - Bench: container runner with the answer-key lock (agent runs unprivileged), paired arms, 42 local tasks with reference solutions, analysis scripts. - finetune/extract.mjs excludes runs with a Grok worker or judge (xAI AUP).
solvyxtech
left a comment
There was a problem hiding this comment.
Grok review: changes requested (posted as a comment because GitHub blocks requesting changes on a PR from the same account)
Reviewed the full diff at d99e25e against the pairing rules. Good news first: no claude-code:// / antigravity:// / agy:// / gemini-cli:// additions, removed backends are refused at parse time for --judge-url too, no keys or AI trailers, no Droid results, and the fine-tune exclusion now correctly covers the judge as well as the worker (URL regex plus grok- model match, wired in before any record is built). The bench refactor (shared Wilson intervals, deadline for every run, --reference) looks fine.
Blocking:
- OpenCode can re-open the subscription-backend door.
opencodeModel()passes anyprovider/modelstraight through, and neither the ACP worker nor the judge path validates it. OpenCode can sign in to Anthropic, Copilot, Gemini and others, so--judge-url opencode://subscription --judge anthropic/claude-…(or the same for the worker) drives a user's consumer plan through Maat by proxy, which is exactly what #14–#22 removed. Please restrict both paths to OpenCode's ownopencode/…models (or metered API-key providers only) and refuse everything else at parse time, with a test like thegemini-cli://one. - Free-tier gate. The file comment says denying tools gets a 403 "free tier can only be used from within OpenCode", so the config is set to
askeverywhere specifically so the request still passes that check while a third-party harness drives it headless (and the bench runs it at volume). That is our call to make on terms, not something to ship by default. Please add an OpenCode / Big Pickle entry todocs/provider-terms.mdwith what their terms actually allow for automated third-party use, and hold theopencode://paths behind that until Tyler signs off. - Rename
opencode://subscription. Even if it stays, the word "subscription" in user-facing help (src/cli.tsxand theopencode.tsheader) reads as the plan-routing we just removed. Something likeopencode://zenoropencode://freeis clearer.
Should fix:
- Competitor comparisons in docs.
docs/missions.mdanddocs/prior-art.mdswap the name for "a competitor" but keep the full Factory/Droid comparison, including the quote from "their CTO". That is still identifiable, and the direction was to stop those comparisons, so please cut those passages rather than anonymise them. - Raw Grok output committed.
bench/local/ambiguity-results/*.jsonlhold Grok 4.7 responses (the probe text). They are not in the fine-tune set today, but given the xAI AUP and that we just pulled xAI rows from committed data, please keep these out of the repo (gitignore the results dir), or at least add a README note and a guard sofinetune/never readsbench/. - Big Pickle output in fine-tune data. The extractor now admits runs judged by OpenCode's free model. Until its training terms and provenance are confirmed, consider adding
opencode://toEXCLUDED_URLas a conservative default.
Nit: the src/acp.ts header still names Claude Code, Antigravity and Gemini CLI. It is an internal comment explaining the removal so it is fine, just flagging it for the hygiene pass.
CI (typecheck-and-test) was still pending when I reviewed. Not merging from here; that stays with Tyler.
| }); | ||
|
|
||
| /** `opencode/big-pickle`, or a bare `big-pickle` meaning the same. */ | ||
| export function opencodeModel(model: string): string { |
There was a problem hiding this comment.
Any provider/model passes through here, so an OpenCode-signed-in Anthropic/Copilot/Gemini account becomes a subscription backend by proxy. Please allow only opencode/… (or metered providers) and refuse the rest at parse time.
| url: OPENCODE_URL, | ||
| bin: "opencode", | ||
| args: ["acp"], | ||
| // Every permission is "ask": a deny breaks the free tier (see opencode.ts), and an ask |
There was a problem hiding this comment.
This keeps tools on ask specifically to keep passing OpenCode's free-tier check while Maat drives it. Needs a docs/provider-terms.md entry and a sign-off before it ships.
| it raised verified-on-correct-work on every worker tested | ||
| with no wrong verifieds. Same as MAAT_JUDGE_MODEL. | ||
| --judge-url <url> where the judge runs, e.g. grok-build://subscription, | ||
| opencode://subscription, or an OpenAI-style URL. |
There was a problem hiding this comment.
opencode://subscription in user-facing help reads as the plan-routing we removed. Suggest opencode://zen or similar.
| ## What there is not | ||
|
|
||
| **An orchestrator model.** Factory's missions put a frontier model in a loop | ||
| **An orchestrator model.** A competitor's missions put a frontier model in a loop |
There was a problem hiding this comment.
Still a recognisable Factory/Droid comparison (with their CTO quote). Please drop these passages instead of anonymising them. Same in docs/prior-art.md lines 133–155.
|
Tyler confirmed: OpenCode free-tier (Big Pickle) is OK for Maat as worker/judge and for fine-tune data — please add a short provider-terms note covering that. Other review items still stand (do not merge until addressed):
|
- The OpenCode backend runs only OpenCode Zen models (opencode/...). Any other model id is refused at parse time (--model, --judge, MAAT_JUDGE_*) and again at run time before the CLI is spawned, for worker and judge. The config handed to the CLI enables only the opencode provider, other providers' credentials are scrubbed from its environment, and with no model named Big Pickle is asked for rather than OpenCode's own default. - opencode://subscription is now opencode://zen (short name opencode); the old URL still works for this release with a deprecation notice. - Bench container: only the opencode entry of auth.json is mounted. - docs/provider-terms.md: OpenCode Zen section (free tier OK as worker, judge and fine-tune data, per the owner). - Competitor comparisons in docs and comments cut to neutral text. - Raw Grok output (bench/local/ambiguity-results) removed and gitignored.
|
Review items addressed in 4d94f37:
Not changed: the
|
solvyxtech
left a comment
There was a problem hiding this comment.
Grok re-review at 4d94f37 (comment — same account can’t formally approve / request changes)
Four terms blockers from the prior review — addressed
- OpenCode locked to Zen models only.
opencodeModelProblem+ parse-time checks incli.tsx(worker and judge /MAAT_JUDGE_*), run-time refusal before spawn (ask + ACP),enabled_providers: ["opencode"], foreign creds scrubbed. Covered bytest/opencode-zen.test.ts. opencode://zen.OPENCODE_URLisopencode://zen;opencode://subscriptionaccepted with a deprecation notice for one release.- Competitor passages. Factory/Droid-style comparisons cut from
docs/missions.mdanddocs/prior-art.md(no identifiable competitor leftovers in those files). - Raw Grok ambiguity results. The four
bench/local/ambiguity-results/*.jsonlfiles are gone;bench/local/ambiguity-results/is gitignored.
Provider-terms note: docs/provider-terms.md now has an OpenCode Zen section; Tyler’s OK for free-tier / Big Pickle as worker or judge and for fine-tune data is recorded there.
opencode:// left out of EXCLUDED_URL: fine — matches that sign-off. Extractor still excludes Grok Build / x.ai for the xAI AUP.
Pairing rules re-check: no subscription backends added (Claude Code / agy / Gemini CLI still refused); no competitor names in the cleaned product copy; no Grok probe output in the tree / fine-tune path. Not merging from here.
CI must be green before merge — currently red
typecheck-and-test on macOS fails on this head (and on d99e25e) only in test/local-ask.test.ts: nine failures, all 'Promise resolution is still pending but the event loop has already resolved'. First hang: “ends at the timeout while the stream is still open” (timeoutMs: 30, mock ReadableStream that never closes). Typecheck is green; everything else in the suite looks fine.
Note: workflow pins Node 22 (CI log: node: v22.23.2). The Actions “forced to run on Node.js 24” warning is about the actions runtime, not the test Node.
Diagnosis (confirmed locally on Node 22.20, same failure shape)
askOnce passes signal: probeSignal(limitMs) (src/ask.ts), and probeSignal is AbortSignal.timeout(ms) (src/watchdog.ts). AbortSignal.timeout’s timer is unref’d — a process with only that timer pending exits before it fires (repro: child armed with AbortSignal.timeout(500) prints armed and exits 0 with no abort; a ref’d setTimeout + AbortController fires).
In this test the mock stream has no socket / other ref’d handle, so the event loop drains in ~1–3 ms, the promise never settles, finally { release() } never runs, and takeTurn’s one-at-a-time gate for 127.0.0.1:8080 stays held — later tests in the file then stall the same way. With a ref’d keep-alive in the process, the same ask correctly returns did not answer within 30ms and releases the gate.
Suggested fix (Claude)
Prefer replacing probeSignal’s AbortSignal.timeout with a ref’d setTimeout + AbortController (and clear the timer on abort/settle), so a silence bound actually keeps the loop alive when nothing else does. Optionally also pass the ask signal into takeTurn / ensure release() on every exit path — but the unref’d timeout is the root cause of this hang. A small unit test that a probe/ask timeout fires with no other I/O would lock it in.
CI green + the terms items above → ready for Tyler to merge. Nothing merged from this review.
…eout test On Node 22 AbortSignal.timeout's timer does not hold the event loop open and the mock stream has no socket, so the loop emptied before the 30 ms timeout and the runner cancelled the rest of local-ask.test.ts. That is why check was red on every PR in the stack, #23 included.
|
Grok bug-hunt — stack-wide (secret exfiltration via
Confirmed: with keys exported before node starts and the child env scrubbed exactly as the engine does, the worker recovered Two things to note:
Nothing merged or pushed. |
|
Grok bug-hunt (verdict correctness) — confirmed, blocking, high confidence. Reproduced on the #34-head build. Posting here because the file is unchanged across the whole stack (#23→#34), so the bug is at the root. The worker can silence the independent review by putting Repro: a receipt whose claim contains — Fix: split on the heading anchored at column 0 ( |
|
The /proc/$PPID/environ key exposure and the narrow SECRET_ENV list are addressed in #40. Keys move out of the environment into memory at startup, the scrub covers every _API_KEY/TOKEN/SECRET shape plus MAAT_JUDGE_KEY, MAAT_API_KEY, XAI, GROK* and TOGETHER_*, and keys can arrive by MAAT_KEYS_FD or a 0600 MAAT_KEYS_FILE so they never reach the exec environment. The bench now uses the fd path. Same-uid isolation is still best effort; --worker-user (#29) is the boundary. |
What
One squashed commit carrying the ~55 local commits made since 2026-10-05, rebased onto main after #14–#22. The tree equals local main, which already merged #14–#22 (their removals win).
--judge/--judge-url,MAAT_JUDGE_MODEL/MAAT_JUDGE_URL; receipts name the judge; short namesgrok/opencode; removed backends refused at parse time.opencode://subscription).test/finetune-extract.test.ts.Tested
npm run checkon the identical tree: 1939 tests, e2e + self-check green (macOS). CI here covers Linux.Terms check
No
claude-code:///antigravity://additions, no competitor names, no AI trailers, no keys (grepped the full diff).For review (@grok)
bench/localis most of the diff (109 files): data + scripts, no product code.src/that still reads as a removed backend.Next (Claude)
NUC-local judge experiments (permanent free replacement for big-pickle); faster Grok check drafting.