ouroboros/tests/test_worker_process_extraction.py
Ouroboros 7d2dca49d7 v7next F1: domain D08 quiet part - events/queue/workers/control split from tip bytes; cancel/custody organ hot-deferred
Module side. 22 D08 owners classified: 4 byte-identical across tip/reference
(schedule_contract, supervisor/__init__, active_activity, schedule_time);
6 pure upstream drift - tip bytes stand (promotion_source, tools/followup,
message_bus, queue_transitions, state, task_admission); 4 NEW post-cutoff
upstream modules with no ledger rows - untouched (cognitive_operations,
log_addressing, subagent_task_truth, task_dispatch); 4 monoliths split.

The split: 16 leaves, 142 moved spans, every span transplant-tool proof-green
against git show HEAD:<monolith> (ast=tokens=byte-roundtrip on every symbol,
leaf_invariants=[], unread_declared=[]). supervisor/events.py 4476->1947:
eight handler families (chat_delivery, subagent_admission, schedule_task,
project_routing, coop_checkpoint, budget, worker_reports, runtime_controls).
supervisor/queue.py 1587->1265: queue_schedules on the _queue handle.
supervisor/workers.py 4396->2808: worker_promotion/chat_lane/pool_lifecycle
on the _pool handle + worker_process (the child, no pool state).
tools/control.py 3225->2110 (SHARED D07/D08): only the D08 leaves
control_events/control_routing/control_runtime; D07 rows untouched.
Facades = tip parent - moved spans + re-export block, facade audit green
(every kept span byte-identical to tip, every moved name re-exported).
Drift-probe first per leaf: 4 reference leaves fully byte-true, 12 with
41 upstream-drifted spans re-emitted from tip bytes; no oracle semantics
replayed over drift.

HOT-DEFERRED with evidence (cancel/custody D09-class; upstream 65b5d19f
re-decomposed this ownership): events_task_done family, _handle_cancel_task,
_close_campaign_after_owner_stop, events_evolution_done, queue_snapshot,
queue_timeouts, queue_evolution (upstream's own evolution_lifecycle.py
supersedes), worker_assignment, worker_health. Deferred semantic-delta rows:
D06 events taxonomy (dispatch/EVENT_HANDLERS keep tip bytes), D04 retired
timeout knobs, six retired-name rows. Row 2016 (_handle_schedule_task)
deferred on a mechanism finding: its >300-line FUNCTION_DEBT entry is
(path, qualname)-keyed and this tree's transition validator has no D11
same-qualname relocation rule - the handler stays with its debt key.

Test side: identity suites test_events_extraction (5, pins the deferred
inventory as the F2 work order), test_control_extraction (5),
test_worker_process_extraction (5, reference-verbatim); LEAVES table in
test_module_handle_extraction gains six tool-derived declared-set rows.
Dead-patch class re-pointed to owner leaves mirroring reference adaptations
across 13 files (coop quiescence, schedules, worker_process trio, promote
admission seam -> control_events, pool-disabled probe -> control_routing,
evolution restart claims -> control_runtime alias, budget append_jsonl ->
events_budget, ephemeral-turn source scan -> worker_chat_lane, routing
producers AST scan -> control_routing, worker_main scan -> worker_process,
resource-leak source contracts reference-verbatim); all 15 touched test
files lossless (test-name multisets equal), no new ast-identical dup bodies.
HOT_CODE_PATHS mirrors the 12 hot leaves (D04-block precedent).

size-ratchet manifest regenerated with the official tool (queue.py enters
the 1001-1500 band with rationale; giants shrink in place); ratchet lane
5 passed. ruff check . --select F clean. CI-shape battery on this tree:
parallel 11965 passed rc=0, serial 609 passed rc=0. Import smoke fwd+rev
green; worker_main stays picklable from supervisor.worker_process.
docs/v7next/LEDGER_CORRECTIONS.md: D08 lane section (11 entries).

(cherry picked from commit 1426ea3ae2413580992575f5ab890497469a6657)
2026-08-30 20:41:22 +00:00

75 lines
2.7 KiB
Python

"""Structural contracts for the semantic-no-op worker child-process extraction."""
from __future__ import annotations
import ast
import pathlib
import pickle
from supervisor import worker_process, workers
REPO = pathlib.Path(__file__).parents[1]
_MOVED = (
"WORKER_LOG_SINK_SUPPRESSED_TYPES",
"_bind_worker_repo_root",
"_current_custody_session_id",
"_log_worker_crash",
"_prepare_worker_task_runtime",
"worker_main",
)
# What could NOT move: the pool's own state, and everything that reads it. The
# child process has none of it, which is exactly why the seam sits here.
_POOL_STATE = (
"REPO_DIR", "DRIVE_ROOT", "MAX_WORKERS", "WORKERS", "PENDING", "RUNNING",
"CRASH_TS", "QUEUE_SEQ_COUNTER_REF", "_CTX", "_LAST_SPAWN_TIME",
)
def test_the_child_process_module_never_imports_the_pool():
tree = ast.parse(pathlib.Path(worker_process.__file__).read_text(encoding="utf-8"))
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
assert node.module != "supervisor.workers"
if isinstance(node, ast.Import):
assert all(alias.name != "supervisor.workers" for alias in node.names)
def test_workers_facade_reexports_every_moved_identity():
for name in _MOVED:
assert getattr(workers, name) is getattr(worker_process, name), name
def test_worker_main_is_still_module_level_and_picklable_by_name():
"""Spawn platforms re-import the target by qualified name; a nested or
wrapped function would break every non-fork host."""
assert worker_process.worker_main.__module__ == "supervisor.worker_process"
assert worker_process.worker_main.__qualname__ == "worker_main"
assert pickle.loads(pickle.dumps(worker_process.worker_main)) is worker_process.worker_main
tree = ast.parse(pathlib.Path(worker_process.__file__).read_text(encoding="utf-8"))
assert any(
isinstance(node, ast.FunctionDef) and node.name == "worker_main"
for node in tree.body
)
def test_the_pool_kept_its_state_and_the_child_module_declares_none_of_it():
child = vars(worker_process)
for name in _POOL_STATE:
assert hasattr(workers, name), name
assert name not in child, name
def test_worker_process_extraction_size_bounds():
counts = {
module.__name__: len(
pathlib.Path(module.__file__).read_text(encoding="utf-8").splitlines()
)
for module in (workers, worker_process)
}
assert counts["supervisor.worker_process"] <= 1000
# The pool itself is NOT split by this commit: its remaining size is bound to
# the module-global state the spec defers to the QueueState step, so this
# bound records the honest current ceiling rather than claiming a win.
assert counts["supervisor.workers"] <= 2894