Repository navigation
feat(conditions): generic evaluator + migrate Codex onto conditions-core (PR-2) - #326
Merged
Juliusolsson05 merged 2 commits intoJun 22, 2026
Conversation
…ore (PR-2) Add the GENERIC HEADLESS EVALUATOR (src/shared/conditions-core/evaluator.ts): makeEvaluator(provider, modules, now) loops the module registry, assembles the unified ConditionSnapshot, and owns the dedupe latch. keyOf is IDENTICAL to the legacy codexConditionSnapshotKey (JSON.stringify of the conditions map, ts excluded, insertion order preserved — order is a wire contract). Pure; clock injected via now() for deterministic golden testing. Add scripts/sync-conditions-core.mjs: vendors ONLY the headless slice (contract.ts + evaluator.ts, NOT the React view/outlet/dispatch) into each headless submodule's src/conditions/core/, header-stamped GENERATED. Submodules can't import the app's src/shared, so the contract+evaluator are copied in and this script is the single source of that copy. codex-headless is synced now; claude-code-headless's destination is wired (sync:false) for the next PR. Bump the packages/codex-headless gitlink to the migration commit, which moves Codex's trust-dialog + approval builders onto ConditionModules driven by the shared evaluator. Verified byte-for-byte against the prior path. NOTE: the codex-headless submodule PR (Juliusolsson05/codex-headless#17) must merge before this gitlink resolves on integration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… bump — PR-2 review fixes Completes the agent-code side of the PR-2 adversarial-review fixes (the prior fix agent pushed codex-headless#17 but stalled before delivering this). Reconstructs app-side evaluator.ts to match the reviewed vendored copy (codex-headless@9480f580): makeEvaluator now defensively copies its module list (`const registry = [...modules]`) so external mutation of an exported CODEX_MODULES cannot reorder/alter a running evaluator. Bumps the codex-headless gitlink to 9480f580 (fresh per-call action objects + softened "golden matrix" comment wording). Verified zero drift: the sync script regenerates the vendored copy byte-identically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 tasks
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.
PR-2 of the conditions framework
Migrate Codex's headless conditions onto the new generic conditions-core, proven byte-for-byte against the current working implementation. Cross-repo: the
packages/codex-headlesssubmodule PR + this gitlink bump.1. Generic headless evaluator —
src/shared/conditions-core/evaluator.tsmakeEvaluator<P,I>(provider, modules, now)returns{ evaluate, keyOf, changed, resolveAction }:evaluate(inputs)loops the modules; for each,state = detect(inputs); skip onnull; else insertconditions[kind] = { kind, state, actions: actions(state) }in module-array order.keyOf(snap)=JSON.stringify(snap.conditions)— IDENTICAL to the legacycodexConditionSnapshotKey(ts excluded, keys NOT sorted; insertion order is observable and load-bearing).changed(key)is the dedupe latch (seeded'{}', matching the oldlastConditionKey).resolveActionroutes acustomaction to the first module with aresolve— dormant today, wired for the later answering driver.Pure; clock injected via
now()so the golden can pints.2. Vendor-sync —
scripts/sync-conditions-core.mjsSubmodules can't import the app's
src/shared, so the headless slice only (contract.ts+evaluator.ts, NOT the Reactview.ts/ConditionOutlet.tsx/dispatch.ts) is copied into each headless package'ssrc/conditions/core/, each header-stamped// GENERATED by scripts/sync-conditions-core.mjs — edit src/shared/conditions-core, do not edit here. codex-headless is synced in this PR; claude-code-headless's destination is wired (sync: false) so PR-3 reuses the script unchanged.3. Codex migration (in the submodule PR)
trustDialogModule+approvalModuleviadefineModule, preserving the EXACT two-source approval merge (screenapproval+ rolloutapprovalMetadata) and the exact pty actions (\r,p,\x1b; trustaccept/2\r).CODEX_MODULES = [trustDialogModule, approvalModule]— order preserved (trust first).codex.switch-model-promptleft as-is (typed, no detector) — unchanged.evaluateCodexConditions+codexConditionSnapshotKeykept exported (re-implemented via the evaluator).CodexHeadless.publishConditionSnapshotrepointed to a long-livedmakeEvaluator('codex', CODEX_MODULES, () => Date.now()). Emitted snapshot shape + both events byte-identical; legacy per-eventtrust-dialogemission untouched.4. Byte-for-byte golden matrix
Throwaway script (not committed) ran OLD (inline copy of prior builders) vs NEW over the matrix; assert
JSON.stringify(snapshot.conditions)identical (normalize onlyts; keys NOT sorted):ALL PASS.
Typecheck (vs integration baseline)
tsc -b tsconfig.web.json, cleared.tsc-out): 7 errors — unchanged from baseline (all pre-existing, unrelated).tsc -b tsconfig.node.json, cleared.tsc-out): 4 errors — unchanged from baseline.tsc --noEmit+ full build): 1 error — unchanged from baseline (SemanticChannel.ts:283, pre-existing).Submodule gitlink:
packages/codex-headless→c2f5dc4f61799380c9256b8fd7d2663f86b6aea3.🤖 Generated with Claude Code
--merge), never squash/rebase — squash/rebase mints a new SHA and orphans gitlink commit9480f580, breakinggit submodule updateon fresh clones.integration/conditions-frameworkwith--merge.