refactor(engines): migrate codex and scheduled-agents onto the capture core - #67
Merged
Merged
Conversation
…e core Completes the migration begun for claude-code. All three engines now delegate hashing, component tree digests, the exclusion denylist, comparison shapes, observed and scope gating, and state read/write to the shared core, with the vendored fallback so a bare install still works. Test files are untouched in all three engines. That is the point of the exercise: behaviour is proven identical by not moving the contract. claude-code 59, codex 25, scheduled-agents 21, core 45. Four behaviours deliberately stayed local, each because unifying would have been silently wrong rather than merely different: codex _safe_hash rejects symlinks and the core's does not. Codex resolves instructions and policy from per-workspace paths, so a cloned repo could point a symlink at a file outside the tree and have its contents recorded as if they belonged to the workspace. The stricter version stays. codex _atomic_write writes 0o600, because the same helper writes the Ed25519 signing key. Rather than lose that, the core gained an optional mode parameter applied before the replace, so the file is never briefly readable at wider permissions. codex _save keeps sorted keys and a trailing newline. Switching to the core's format would rewrite every existing state file for no behavioural gain; the digest is over the mapping, not the bytes. scheduled-agents _sha_obj takes arbitrary objects, not just mappings: it is called on lists of allowed tools and MCP names. The core also gained the mode parameter noted above; the vendored copies are re-synced and the sync check passes. Not included: baseline sealing for codex and scheduled-agents. Both lack it and both would benefit, but that is a feature addition rather than a migration, and it touches their report layouts and hook messages. Kept separate so this PR is reviewable as a pure refactor. ruff clean at py39 across all three engines, the package and the scripts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
imran-siddique
added a commit
that referenced
this pull request
Aug 1, 2026
#67 added the mode parameter to core.atomic_write while this branch was open, so the copilot vendored copy was a version behind. Caught by the vendored-in-sync job against the merge with main, which is exactly what that check is for. Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
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.
Completes the migration begun for claude-code in #66. All three engines now delegate hashing, component tree digests, the exclusion denylist, comparison shapes, observed and scope gating, and state read/write to the shared core, each with the vendored fallback so a bare install still works.
Net -64 lines across the two engines, and one implementation of the logic that previously carried the same bypass in two places (#63, #65).
The proof it is behaviour-preserving
Test files are untouched in all three engines. That is the point: the contract does not move, so identical behaviour is demonstrated rather than asserted.
The one Codex failure is the pre-existing TRACE conformance check that
fix/codex-trace-pinsaddresses.Four things I deliberately did NOT unify
Each because unifying would have been silently wrong rather than merely different. This is the interesting part of the diff.
codex._safe_hashrejects symlinks; the core's does not. Codex resolves instructions and policy from per-workspace paths, so a cloned repo could point a symlink at a file outside the tree and have its contents recorded as if they belonged to the workspace. The stricter version stays local, with the reason in the docstring.codex._atomic_writewrites0o600, because the same helper writes the Ed25519 signing key. Rather than lose that, the core gained an optionalmodeparameter, applied before the replace so the file is never briefly readable at wider permissions.codex._savekeeps sorted keys and a trailing newline. Switching to the core's format would rewrite every existing state file for no behavioural gain. The digest is computed over the mapping, not the bytes, so the formats are interchangeable in meaning.scheduled-agents._sha_objtakes arbitrary objects, not just mappings: it is called on lists of allowed tools and MCP server names.Same reasoning as
_sha_treein #66. A shared core is only worth having if the things that genuinely differ are allowed to differ, and are documented where they do.Not included
Baseline sealing for codex and scheduled-agents. Both lack it and both would benefit, and it is now nearly free via
core.save_baseline. But it is a feature addition rather than a migration, and it touches their report layouts and hook messages, so it stays separate to keep this PR reviewable as a pure refactor.Test plan
ruff check --target-version py39clean across all three engines, the package, andscripts/.modeaddition.