Feat/cesar verification suite - #276
Merged
Merged
Conversation
…hared review contract Each engine reviews the same diff through a role lens (security/correctness/ dryness/performance) with an always-seated overall generalist backstop, over the identical grounding + sentinel-JSON machine block, so consensus and the results pager work unchanged. Master-abort fan-out wired so Esc cancels the whole panel. Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
/review role was REPL-only — external agents (Claude Code, Codex, CI) had no path to it (mode-checklist surface gap). Adds: - agon review --roles auto|<list>: roles assigned AFTER risk routing / explicit selection, zipped per engine, overall backstop for extras; role tag shown per reviewer, roleId passed through runReviewCore - agon call review --roles passthrough (shared roles option with council) - README: role-lens review section with REPL + CLI + bridge usage - tests: call bridge forwards --roles ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…omposer
The pinned todo panel rendered directly under ChromeBar with the whole
live stream below it, so it floated mid-transcript ('free flight').
Moved to just above BottomChromeSection so Todos N/M pins to the bottom
chrome where the plan chip and composer live.
⚔️ Forged by [Agon](https://github.com/KERNlang/agon)
Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…, TuiProbe Closes Cesar's 'I edit my own UI blind' and 'I can't query engine reliability mid-turn' gaps (spec'd from Kimi-as-Cesar feedback, nero-challenged, 6-engine reviewed): - EngineReliability: read-only digest, two labeled sections (own-turn summarizers vs delegated dispatch ledger), never blended - RenderProbe: in-process Ink frame capture of fixture surfaces with true FINAL-frame semantics (last non-empty debug write, not the accumulated transcript) - TuiProbe: PTY-launches a throwaway agon (isolated AGON_HOME + cwd, empty roster, single-line safelisted input — control chars rejected, review finding) via py/agon-tui-probe.py; pyte screen-state grid, not ANSI-stripped bytes. Ships in the package (py/ in files; path resolution handles tsup's flat dist chunks — review finding) ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…nest provenance Delegated engines previously returned only a bare toolCalls count; Cesar had no evidence of which tool calls a delegate dropped, faked, or stalled on. Now every dispatch writes one record to ~/.agon/runs/delegate-tool-ledger.jsonl: - api-loop runs carry per-call outcomes (native provenance, exactly one outcome per counted call incl. pre-execution failures — review finding) - narrated stalls are heuristic and NEVER inflate native counts (review finding); text transports record unknown / dispatchFailed, never ok/error fabricated from prose - summaries group per engine x backend, engines never merge (review finding); reader never throws on corrupt or schema-invalid lines (review finding) - Delegate results append a one-line ledger digest; failed dispatches are recorded too (zai+kimi convergent review finding) ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…efault branch branch:NAME with NAME checked out errored loudly (itself a fix for the silent empty self-diff). Answer the intended question instead: resolve the base via origin/HEAD → origin/main/master → main/master (each verified to resolve to a commit — review finding: stale origin/HEAD must not short-circuit the fallback), diff base...branch, label '(auto-base)'. The loud error remains when the reviewed branch IS the default branch (review finding: never fall back to an unrelated legacy main). Un-breaks Cesar auto-firing 'review branch:X' while sitting on X. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…writes for new handlers
Today's cesar-verification-suite work added handlers that tripped the
kern:self-coverage gate (blockedHandlers 627>620, comments-present 190>185,
var-bad-expr 63>62, closure-loop 24>23, switch-stmt 23>22, closure-spread 10>9).
Restored the gate by converting the new blockers to native KERN or migratable
raw handlers — no behavior change (verified by the targeted unit suites).
Converted to native KERN (de-blocked):
- delegate-ledger.deriveOutcomeCounts — switch→if/else chain + native each/continue.
- delegate-ledger.recordDelegateDispatch — native try/catch node.
- delegate-ledger.readDelegateLedgerRecords — native try/catch/finally + nested
try + each (KERN has native try/catch/finally nodes).
- delegate-ledger.summarizeDelegateReliabilityByEngine — native each; Map generic
moved to the type= attribute (the expression parser rejects `new Map<K,V>()`).
- tool-tui-probe.resolveTuiProbePaths — native each over the candidate roots.
- review.resolveAutoReviewBase — native try/catch + each; extracted the
resolvesToCommit closure into a native refResolvesToCommit helper.
- tool-engine-reliability — factory is now a native return; extracted
engineReliabilityDefinition/Validate/CheckPermission/Execute + buildOwnReliabilityLines
(removes the arrow-const var-bad-expr and the execute closure-loop).
- tool-render-probe — factory is now a native return; extracted
renderProbeDefinition/Validate/CheckPermission/Execute + renderProbeSurface
(removes the arrow-const var-bad-expr, the `{...a,...b}` closure-spread, and the
execute closure-try). Spread rewritten as Object.assign in renderProbeSurface.
Baseline-bumped (1 category, +1):
- comments-present 185→186 for frame-capture.createPseudoTty. This factory returns
a record whose function fields (lastFrame/read) close over a mutable array
(chunks); KERN native rejects that ("stale/fresh array binding captured by a
record field"), so it stays a raw TS handler with a // KERN-GAP:
native-record-closure-capture note. blockedHandlers stayed at 619 (≤620), so no
blockedHandlers bump was needed.
Verification: kern:compile clean, build 0 TS errors, targeted vitest 40 passed
(1 skipped), check-kern-self-coverage green (619 blocked, 719 native, 76.27%
classified/migratable, 0 parse errors), kern:coverage 201/201.
⚔️ Forged by [Agon](https://github.com/KERNlang/agon)
Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…oring to 0 agy review finding: cols/rows in (0,1) passed the >0 guard then Math.floor'd to 0, which crashes Ink's per-cell layout. Both probes now clamp to [1, max]. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
… lock Reproduced lost updates under load (8 children x 50 locked RMW cycles lost 1-2 increments every ~5 rounds), then fixed both root causes: 1. Non-atomic lock content: O_EXCL writeFileSync has a window between open() and write() where contenders read an EMPTY lock file, judge it corrupt->stale, and rename-steal a LIVE holder's lock. Acquisition now stages the payload in a private file and linkSync()s it into place — same EEXIST exclusivity, but the lock file is born fully written. 2. ENOENT treated as reclaimable: a freed slot (holder just released) entered the stale-reclaim path, letting a contender rename-steal the lock a NEW holder acquired in between; the best-effort put-back then lost the race too (and renameSync would clobber a third acquirer — now linkSync, which refuses). readFileLockInfo now distinguishes 'missing' (re-contend via acquire, never reclaim) from corrupt (null). 10/10 stress rounds (4000 contended cycles) clean post-fix; full suite green including the cross-process oracle under parallel load. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…entirely Review follow-up (codex, blocking 1.00): renaming a stale entry away is blind — between judging the holder stale and the rename, the slot can be freed and re-acquired by a live holder, and any put-back recovery leaves a window where a third process acquires the emptied slot (two live holders). The mismatch-restore path was unsound by construction. Reclaim is now fenced: a reclaimer must exclusively hold lockPath.reclaim (linked from its staged payload, so the fence names its owner), re-judges the holder UNDER the fence, and only removes a still-stale entry; acquirers back out of any lock taken while a fence is up. Post-fence, every entry at rename time is either the re-judged stale one or a transient acquirer that will back out — the restore path is gone. Wedged fences (dead reclaimer) are cleared from both the reclaim and back-out branches; the deadline check moved to the top of the loop so a wedged fence times out instead of spinning. Also per review: a fresh EMPTY lock file (O_EXCL-fallback holder between open() and write()) and non-ENOENT read errors (EACCES/NFS) are now 'pending' — wait, never reclaim; empty files age out via mtime after staleMs so a crash there can't wedge the lock. 4 new regression tests; 10/10 stress rounds (4000 contended cycles) clean; full suite green. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
Diagnosed from a live Kimi-as-Cesar session that kept stopping mid-task: 1. The format-mismatch hint path in dispatch returned a DIFFERENTLY shaped first-chunk timeout (exitCode 1, 'API first-chunk timeout (60s). tip:') that slipped past decideFirstChunkRetry's message gate — so exactly the engines that trip the hint (Kimi/Moonshot Anthropic-compat) never got the retry and dead-stopped the turn. The hint is now APPENDED to the canonical retryable message (exitCode 124), and the retry-policy regex accepts both phrasings. 2. The hint itself was wrong for /coding/ endpoints: engine-registry deliberately whitelists them as legitimately Anthropic-format, but the dispatch hint advised switching to format='openai' — which would break the engine. The hint now shares the registry's /coding/ carve-out. 3. Empty-response retries were hardcoded 2x1.2s — shorter than real rate-limit windows on Kimi's coding plan. Now exponential backoff (1.5s/3s/6s, cap 8s), default 3, per-engine tunable via api.emptyResponseRetryCount. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
Second review pass (codex) verified two soundness holes in the fence: 1. Shared marker path: clearing a stale fence — or dropping your own in a finally — was read-then-unlink TOCTOU that could delete a SUCCESSOR reclaimer's live fence, leaving it operating unfenced. Fences now have unique names (lockPath.reclaim.<uuid>): every unlink is owner-precise. Exclusivity was never needed — rename(lock->claim) is the single-winner step — so fences are plain writeFileSync (also unwedges reclaim on filesystems without hard links, where the old linkSync fence could never be raised). The fence check is now a readdir that fails SAFE: unreadable dir reports a phantom live fence instead of treating 'could not look' as 'nothing there'. 2. Stale-at-birth payloads: acquiredAt was stamped at function entry, so a contender that out-waited staleMs published a lock already TTL-expired and instantly reclaimable while live. The staged payload now re-stamps when it ages past staleMs/2. 15/15 lock tests (new: fence back-out with unique names, dead-fence recovery, fresh-stamp after out-waiting a holder via child-process release), 10/10 stress rounds clean, full suite green. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
…hip verify Codex confirmation pass on the previous two commits, both verified: 1. ApiConfigSchema omitted emptyResponseRetryCount, so Zod silently stripped the new per-engine override from every registry-loaded engine (the exact failure mode the schema file documents for isolationHints). Field added; the schema fixture test now round-trips it and rejects negative/fractional values. 2. A clean fence scan alone was not proof of lock ownership: a reclaimer that fenced BEFORE our link could blind-rename our fresh lock (having judged the PREVIOUS stale holder) and drop its fence before our scan. Acquisition now re-reads the lock after a fence-free scan and accepts only its own uuid — any fence raised after that re-read must re-judge the slot, sees a live fresh holder, and aborts. 54 targeted tests green, 10/10 stress rounds clean, full suite green (4616 passed). ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.com>
chdir into the throwaway cwd failing is an isolation-contract breach: the child now reports it on stderr and exits 126 instead of silently running agon in an inherited directory. Cleanup failures (pty fd close, rmtree) are logged via _dbg instead of ignored. ⚔️ Forged by [Agon](https://github.com/KERNlang/agon) Co-Authored-By: agon (KERN) <292465531+KERN-Agon@users.noreply.github.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.
No description provided.