Skip to content

refactor(backends): decompose execution-authority files (#4664) - #4676

Merged
Trecek merged 52 commits into
developfrom
impl-decompose-execution-authority-files-20260817-123010
Aug 18, 2026
Merged

refactor(backends): decompose execution-authority files (#4664)#4676
Trecek merged 52 commits into
developfrom
impl-decompose-execution-authority-files-20260817-123010

Conversation

@Trecek

@Trecek Trecek commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Decomposed 10 execution-authority files (12,385 LOC) at 750/1000-line decomposition seams per issue #4664. 10 of 11 groups shipped in this PR; evidence_reader.py (#4) is deferred to follow-up issue #4674 due to circular-dependency risk in the launch/probe/auth decomposition. E9 (codex.py), E13 (_codex_session_storage.py), and E25 (_headless_result.py) line-cap exemptions were narrowed to acknowledge the residual after extraction. pre-commit run --all-files (ruff, mypy, contract checks, doc count, version consistency, stub completeness, merge-conflict check, secret detection) all pass.

Decomposition seams:

  • codex.py (2444) → codex.py (1216) + _codex_cmd_builders.py (411) + _codex_probes.py (375) + _codex_explorer_projection.py (599)
  • claude.py (1234) → claude.py (931) + _claude_parse.py (231) + _claude_session_locator.py (117)
  • _codex_session_storage.py (1600) → _codex_session_storage.py (1408) + _codex_fs_atomic.py (225)
  • poster.py (997) → poster.py (854) + _poster_finalize.py (242)
  • ledger.py (868) → ledger.py (773) + _ledger_schema.py (137)
  • _headless_result.py (1030) → _headless_result.py (846) + _headless_adjudication.py (247)
  • process/__init__.py (942) → process/__init__.py (821) + _termination.py (177)
  • _managed_headless_session_lineage.py (911) → _managed_headless_session_lineage.py (611) + _records.py (167) + _runner.py (137) + _indexes.py (76)
  • _skill_session_contract_store.py (907) → _skill_session_contract_store.py (393) + _codec.py (571)
  • evidence_reader.py (1452) → deferred to Investigate evidence_reader.py decomposition (1452 lines, E25 still applies) #4674

Requirements

Issue #4664: Decompose execution backend authority files (10 files, 12,385 LOC).

Closes #4664

Implementation Plan

Plan file: /home/talon/projects/generic_automation_mcp/.autoskillit/temp/make-plan/issue-4664-decompose-execution-authority-files_plan_2026-08-17_000000.md

🤖 Generated with Claude Code via AutoSkillit

Trecek added 30 commits August 17, 2026 12:41
…ntory re-export

Extract ClaudeStreamParser + ClaudeResultParser to _claude_parse.py and
ClaudeSessionLocator to _claude_session_locator.py. Re-export from
claude.py so the public surface is unchanged.

Also re-add _validate_codex_mcp_inventory to the _codex_probes re-export
block (it was dropped by the linter since the import was not used
inside codex.py itself; evidence_reader imports it via the canonical
codex path).

Refs: #4664
…torage.py

Move stateless filesystem primitives (lexists/fsync/atomic_json/write_reconciliation_audit/
read_bounded/ensure_directory_chain/decode_mount_path/filesystem_mount_root/
filesystem_type/replace_symlink) to _codex_fs_atomic.py. The transaction-
boundary core (CodexSessionStore, CodexInteractiveSessionLease, _FileLease)
remains in _codex_session_storage.py.

Update DURABLE_ARTIFACT_WRITERS registry and AST-target guard in
test_durable_artifact_writers_guard.py to point at the new module
location. Narrow E13 exemption to 1500 lines to accommodate the
remaining core after extraction.

Refs: #4664
…_poster_finalize.py

Move post-attempt finalize/reconcile helpers out of poster.py (997 → 854
lines) into _poster_finalize.py as module-level functions. The mutation
authority remains in GitHubReviewLedger — these are pure shaping helpers
that operate on receipts/result tuples post-attempt. _EXPECTED_REMOTE_STATES
and _FINAL_STATES constants move with the helpers that consume them.

Refs: #4664
Move _SCHEMA_VERSION/_DIRECTORY_MODE/_DATABASE_MODE/_SCHEMA + dataclasses
ReviewOperationRecord/ReviewAttemptRecord/MutationSlot from ledger.py
(868 → 772 lines) into _ledger_schema.py. ledger.py imports them and
re-exports for sibling modules; existing test imports keep working.

Refs: #4664
…ation.py

Move _resolve_skill_session_id/_parse_stdout/_build_api_retry_outcome/
_make_terminated_result/_has_out_of_cwd_file_change/_apply_post_session_adjudication/
_validate_declared_artifact from _headless_result.py (1030 → 846 lines)
to _headless_adjudication.py. _build_skill_result remains as the
headless orchestration authority. Re-export the moved symbols so
existing imports keep working. Narrow E25 line-cap to acknowledge
the residual 846-line _headless_result.py.

Refs: #4664
Move decide_termination_action (pure decision function) and
execute_termination_action (sole authorized async kill executor) from
process/__init__.py (942 → 821 lines) to process/_termination.py. The
public process.__init__ facade re-exports both so existing callers
keep working; _EXPECTED_PROCESS_SYMBOLS test continues to pass.

Refs: #4664
Move per-record creation/projection/anchor helpers to _records.py,
runner observation helpers to _runner.py, and index path helpers
to _indexes.py. _managed_headless_session_lineage.py retains
the conflict/CAS mismatch errors and the DefaultManagedHeadlessSessionLineageStore
class plus the unaccounted _validate_anchor_identity/_require_cas/
_read_bounded helpers. Existing canonical-path imports keep working
via re-export facade.

Refs: #4664
…codec.py

Move validation, source/exploration-vector/execution-identity serialization,
contract to/from dict, manifest construction, and _digest_json helper from
_skill_session_contract_store.py (907 → 393 lines) into
_skill_session_contract_codec.py. The parent re-exports _digest_json and
all other moved symbols so existing imports keep working — notably
tests/server/test_run_skill_resume.py:640 and tests/execution/test_skill_session_contract_store.py
which import _digest_json from the canonical path.

Refs: #4664
Final pass to make pre-commit (ruff, mypy, contract checks) pass on
all decomposed files. Adds missing imports for _skill_session_contract_codec
extracted types (SkillSource, RepositoryProfileId, ChildExecutionIdentity,
etc.) and re-adds _validate_codex_mcp_inventory to codex.py re-export
block after linter removed it.

Refs: #4664
Per review findings #1 and #2 in PR #4676: annotate gateway,
ledger, wall_clock, and response_class on the module-level
reconcile_payload and finalize functions so they match the
types those parameters carried when they were methods of
DefaultGitHubReviewPoster.
Per review finding #3 in PR #4676: the post-decomposition module
was re-exporting adjudication helpers that had moved to
_headless_adjudication.py only so stale import paths would still
resolve. AGENTS.md §3.1 forbids backward-compatibility hacks, so
delete the shim, replace it with a direct sibling import inside
_headless_result, and update the public headless/__init__.py
re-export surface plus the two test files that imported the moved
names from the canonical _headless_result path.
Per review findings #5 (warning) and #15 (info) in PR #4676:
replace bare `except Exception` with the failure set the call site
actually produces (`OSError`, `UnicodeDecodeError`,
`zstandard.ZstdError`), and parameterize the empty `list[dict]`
return annotation as `list[dict[str, Any]]` since the entries
parsed from each JSONL line are string-keyed dictionaries.
…port

Per review finding #6 (warning) and concurrent failure discovered in
tests/contracts/test_skill_semantic_authenticity.py:130 (the test
imports CODEX_SPAWNABLE_BUILT_IN_AGENT_NAMES from the canonical
`autoskillit.execution.backends.codex` path; the constant was
moved out during decomposition and never re-exported on the new
path). Delete the dead alias and the now-unused
`_codex_config as _codex_cfg` import in
_explorer_projection.py, and add a single explicit re-export of
CODEX_SPAWNABLE_BUILT_IN_AGENT_NAMES plus __all__ entry in codex.py
so the test can collect again.
…n for_each prompt

Per review finding #7 cohesion (DISCUSS, resolved as ACCEPT) and a
related consistency gap uncovered during DISCUSS investigation of
finding #6:

- Rename src/autoskillit/execution/github_review/_poster_finalize.py
  to _poster_post_attempt.py to match the file's actual ownership of
  both the reconcile_payload and finalize helpers (the docstring
  already calls them 'post-attempt helpers'). Update the three
  references in poster.py (import + two call sites).
- Update the for_each branch at codex.py:1109 to use the same
  'retain every returned child terminal result before parent
  synthesis' wording as the spawned-count branch and the wait_agent
  prompt at line 1122, so all three Codex prompt fragments agree.
Per adversarial validation sub-agent findings:

- Restore capability-flags dispatch in _headless_adjudication._parse_stdout
  (was reverted in 1022ea1 due to cascading mock-backend failures). Update
  tests/execution/conftest.py::_mock_backend to default
  supports_claude_format_stdout=True so the mock's capability set matches
  its hardcoded name="claude-code" value, and update the 5 inline
  Mock backend setups in tests/execution/test_headless_result.py to add
  supports_claude_format_stdout=True (and the one negative-test mock to
  set it to False). The arch constraint that motivated the original
  capability switch is a real one and the mock-fixture asymmetry was
  the only reason it cascaded.

- Remove redundant "Extracted from _headless_result.py" sentence from
  _headless_adjudication docstring (now matches the trimmed style
  established for _termination in 8499def).

- Rephrase the claude.py docstring to distinguish ownership of
  ClaudeEnvPolicy / _claude_host_attestation_env / ClaudeCodeBackend
  from re-export of ClaudeResultParser, ClaudeStreamParser,
  ClaudeSessionLocator, and the flag vocabulary. The decompose PR
  moved those symbols out, so the previous wording overstated what
  the module owns.

- Clean up unused imports in execution/process/__init__.py
  (assert_never, cast, KillReason, ProcessCleanupResult, TerminationAction,
  ProcessObservationSnapshot) left behind by the decompose PR removal of
  decide_termination_action and execute_termination_action. Move the
  _termination re-export shim from bottom-of-file (E402) up next to the
  other process module imports so it satisfies ruff/arch rules.

- Drop the unused 'import structlog' (TYPE_CHECKING block) from
  execution/process/__init__.py.

Ruff F401 violations now: 0 in process/__init__.py, _headless_adjudication.py,
claude.py. Remaining E402 violations are pre-existing in
session/_managed_headless_session_lineage.py and
session/_skill_session_contract_store.py and require the leaf-constants
extraction that the validate-review sub-agent recommended for the
arch pattern flagged by review findings #12/#21/#22.
Trecek and others added 22 commits August 17, 2026 17:07
… for _CODEX_PROBE_STREAM_LIMIT and _run_bounded_codex_probe
…mount_root from canonical _codex_fs_atomic module
…up patch probes consistently

Switch the module-attribute patches from codex.subprocess/codex.os to
probes.subprocess/probes.os so the test is coupled to the canonical
probe module after the #4664 decomposition. Other probe tests in this
file were already using the probes module; this single test still
referenced the parent module.

Refs: round 3 review finding (tests: test_codex_config_validation.py:126)
Co-Authored-By: Claude <noreply@anthropic.com>
…purpose, not refactor history

Drop the refactor-narrating sentences from the module docstrings added
during the #4664 decomposition. Each module now describes its functional
role and responsibilities instead of how it came to be split out.

Also unify CodexSessionLocator.read_session internal annotation
(result: list[dict[str, Any]] = []) with the public return type.

Refs: round 3 review findings (slop: 5 files; defense: type annotation consistency)
Co-Authored-By: Claude <noreply@anthropic.com>
…laude sibling modules

Each sibling module's docstring now states what it owns rather than
how it was extracted from the parent file during the #4664
decomposition. The claudeparse sibling also drops the
exposes-them-via-its-public-surface sentence that narrated consumer
relationship instead of the module's purpose.

Refs: round 3 review findings (slop: 5 backend modules)
Co-Authored-By: Claude <noreply@anthropic.com>
…ocstrings and comments

Replace the refactor-narrating comment blocks above the post-class
import sections with concise statements of why the imports are
deferred. The codec module's docstring now states which parent-module
constants it imports instead of describing the import-order workaround.

Refs: round 3 review findings (slop: 3 session modules)
Co-Authored-By: Claude <noreply@anthropic.com>
…and conftest fixture

Drop the change-log narrative from the codex.py line-limit exemption
rationale; the exemption now states only what the backend class owns
and why the cap was lowered. Trim the _mock_backend docstring to
describe the fixture's contract without narrating the test-failure
mode it prevents.

Refs: round 3 review findings (slop: 2 test files)
Co-Authored-By: Claude <noreply@anthropic.com>
Adversarial validation of the previous docstring rewrite flagged that
the new sentence claimed the module owns 'file lease, atomic write,
and macOS quarantine helpers' — but _FileLease lives in
_codex_session_storage.py and there is no quarantine helper here.
The module actually exposes atomic-write, bounded-read,
directory-sync, mount/filesystem-type detection, symlink replacement,
and directory-chain helpers.

Refs: round 3 review finding (slop: _codex_fs_atomic.py:3) +
      adversarial re-validation pass
Co-Authored-By: Claude <noreply@anthropic.com>
Resolves conflicts between the execution-authority file decomposition and
develop's #4641/#4644 cleanup-evidence, #4575 join-contract, and
#4657 MCP-timeout fixes. Develop's fixes are preserved verbatim and
relocated into the modules the decomposition moved their code to:

- _codex_probes.py receives settle_evidence(), the cleanup_incomplete
  field, and the _validate_mcp_probe diagnostic warning.
- _headless_adjudication.py receives _should_flag_cleanup_incomplete and
  the _make_terminated_result infra= wiring; _headless_result.py imports it.
- process/_termination.py receives the settle_evidence() swap and the
  int | None return type; _coalesce_returncode stays in process/__init__.py
  where run_managed_async/run_managed_sync consume it.
- Two develop-added probe tests retargeted from codex to _codex_probes so
  monkeypatching reaches the module that owns the symbol.

Line-limit exemption entries keep the narrowed caps (codex.py 1300,
_headless_result.py 900) with counts updated for develop's additions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merged tree failed 36 tests that the decomposition had broken by moving
symbols without following their consumers. Each fix points the consumer at the
module that now owns the symbol:

- test_claude_result_parser / test_claude_session_locator patched
  parse_session_result and claude_code_project_dir on claude.py, but the
  consumers resolve them in _claude_parse and _claude_session_locator. A
  re-export would have silenced the AttributeError while leaving the patch
  inert, so the patch targets move instead.
- _poster_post_attempt.py imported ReviewResponseClass from
  core.types._type_github_review, violating REQ-IMP-001/002; it is exported
  from autoskillit.core.
- The fcntl allowlist and the _has_active_child_processes AST caller scan
  follow the lock context and execute_termination_action to their new modules.
- _headless_adjudication.py is registered in EXPECTED_CROSS_LAYER_GUARDS.
- The plugin mutation ratchet gates on plugin-lifecycle symbol references, so
  _codex_explorer_projection.py fell out of scan scope when it was split from
  codex.py. It is named explicitly to keep its staged session-root swaps
  classified rather than silently losing that coverage.

task test-check: 39860 passed, 23 failed — the 23 are identical to the
upstream/develop baseline in this environment (ambient
CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT and cli probes-fixture paths), so the branch
introduces no regression.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Trecek
Trecek added this pull request to the merge queue Aug 18, 2026
Merged via the queue into develop with commit db852ec Aug 18, 2026
4 checks passed
@Trecek
Trecek deleted the impl-decompose-execution-authority-files-20260817-123010 branch August 18, 2026 22:59
Trecek added a commit that referenced this pull request Aug 19, 2026
…er develop merge

Merging origin/develop (#4676, #4679 decomposed execution/backends and
server/_lifespan.py into directory packages) shifted the file:line
locations this registry hardcodes for known forwarding sites and
unresolved dynamic reads, without changing the sites themselves.

- server/_lifespan.py:747 -> server/_lifespan/_session_boots.py:455
  (file split, same EVIDENCE_READER_ENV_FORWARD_VARS dict comprehension)
- claude.py/codex.py _HEADLESS_EXCLUSIVE_VARS / _INTERACTIVE_ENV_EXCLUSIONS
  forwarding sites shifted within their files; codex.py's maintenance/
  version-probe dict(os.environ) site moved into the new
  execution/backends/_codex_probes.py module

Verified each new site against the live source before updating its
justification; all four ambient-env-surface pincer tests pass locally
after the merge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant