Repository navigation
Conversation
Pins today's behavior before these executors move into runtime.core: the key event and form submit, the modifier string, the refusal of a key step with no key, and a WAIT that sends no input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…an import contract runtime.core carries out steps through a backend; governance reaches it only as gates. KEY, HOTKEY and WAIT now run there: ActionContext.authorize runs delivery authorization and then the qualification-environment check (Replayer._authorize_delivery), exactly the order the replayer used, and deliver is the replayer's own delivery bookkeeping. Behavior is unchanged: the characterization tests from the previous commit and the keyboard, guarded-keyboard and refusal suites pass. packages/flow/.importlinter, run by lint-imports in flow core lint: - runtime.core imports no runner, hosted, approval_mode, jit, replayer or qualification* module (kept with no exceptions); - the same through every import chain, except 45 listed edges by which the schema, effects, policy, repair, fleet and durable modules reach governance today. New governance imports in the core's dependencies fail. tests/test_import_boundaries.py keeps the forbidden list complete and proves every ActionKind has exactly one executor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Section 2.4 measures the boundary (a core module reaches 249 of 562 modules through 45 governance edges). Section 10 inventories duplicate implementations (strength scales, outcomes, admissions, certify paths, canonical JSON, runtimes) with the canonical one and the PR for each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(move only) Seven Replayer methods move verbatim into runtime/core/render.py as the RenderScaleAndLayoutDrift mixin; scripts/verify_moved_code.py finds 7 moved units and no new logic. scripts/extract_mixin.py, added here, does such moves: it cuts methods verbatim, imports what they use from where the replayer got it, and writes a host class that declares for mypy only what the code reads from self, with the replayer's own annotations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…me/replay (move only) SurfacesAndOverlay (10 methods) and AssetsHealingAndEvidence (3) move verbatim; verify_moved_code finds 13 moved units and no new logic. They read governed-run state, so they go to runtime/replay, not runtime.core. The extractor now copies only signature decorators onto host stubs and ignores docstrings when it looks for referenced names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s stay put Records why a mixin's host is a plain class (protocol attributes without a value are abstract and get re-inferred; implicitly abstract protocol stubs can shadow real methods), the rule for what goes into runtime.core, and two invariants: resolver.py and program_predicates.py source bytes are part of the predicate evaluator contract, and the CLI split keeps command identities. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/replay/_shared (move only) 49 constants, helpers and small types move verbatim, adjacent definitions as one block so shared comments stay with them; replayer.py re-exports every name, so imports such as _DURABLE_RESUME_AUTHORITY, secret_env_var and _ProgramHalt keep working (same objects). verify_moved_code: 58 units. The extractor no longer adds module imports for names a moved body imports itself: ruff would otherwise delete the body's own import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e only) Qualification fault injection, consequential delivery, effect checks and read-back, the API tier, acting by kind, settling and confirming move verbatim into mixins (107 units; verify_moved_code finds no edits). replayer.py goes from 18,300 to 10,926 lines. Left in place because open pull requests change them: identity checks (#1518, #1504), _resolve_step (#1504), postconditions and step evidence (#1516, #1514), the starting-state settle and qualification boundary (#1449), and the run and step drivers. Two methods that name Replayer stay too. Two tests patched module globals the moved code now reads from its new module, and patch them there as well: resolve (scroll readiness is in replay.acting, interstitials in replay.settling) and _DIALOG_WAIT_S (replay.acting). Checked: strict mypy on runtime/core, runtime/replay and replayer.py; package-wide mypy; both import contracts; 3,149 tests across the 146 files that import the replayer plus the real-browser e2e files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…replay The moved replayer code keeps the strict type check and safety-path review it had in replayer.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Summary
PR 2 of the decomposition plan (
packages/flow/docs/design/DECOMPOSITION.md, section 4.3): the replayer's step pipeline and strategies. Draft; it grows by chunk.replayer.pyis down from 18,300 to 10,926 lines, with behavior unchanged.1.
runtime.core, behind import contractsruntime/core/actions.py:ActionContextcarries the step, the backend, and two gates.authorize()runs delivery authorization, then the qualification-environment check, in the replayer's original order.deliver()is the replayer's own delivery bookkeeping.runtime/core/render.py: render scale and layout drift. Moved; its host reads no governance state.packages/flow/.importlinter, checked bylint-importsin flow core lint:runner,hosted,approval_mode,jit, replayer orqualification*module. Kept with no exceptions.ir,backendand the resolver reaches 249 of 562 modules today (section 2.4).tests/test_import_boundaries.pykeeps the forbidden list complete and proves everyActionKindhas exactly one executor.2. Move-only extraction into
runtime/replay/(mixins)_shared.py: the 49 module-level constants, helpers and small types.replayer.pyre-exports them, so imports such as_DURABLE_RESUME_AUTHORITYand_ProgramHaltget the same objects.scripts/extract_mixin.py. Method bodies are cut verbatim. Each mixin gets a host class that declares, for mypy only, what the code reads fromself, using the replayer's own types.Protocol, and why, is in DECOMPOSITION.md section 4.1.scripts/verify_moved_code.py(from test: safety net for decomposing the largest files (PR 1) #1510) checks every move commit: 185 units moved, no edits, no new logic.Not moved yet, because open PRs change them
_resolve_step: fix(flow): halt when another shown row has the same identity #1504.Replayer.Test changes
Two tests patched module globals that moved code now reads from its new module. They now patch them there as well:
resolve, inreplay.actingandreplay.settling;_DIALOG_WAIT_S, inreplay.acting.The verifier now flags that case (#1510).
Checks (local, at most 2 workers)
tests/test_core_key_actions.py, committed before the first move: real Playwright; key event and submit, HOTKEY modifiers, refusal of a key step with no key, WAIT sends nothing.test_record_compile_replay,test_perturbationandtest_replayer(144).test_layout_drift.py::test_real_render[font_size_changed]fails identically on unmodified code (font rendering).runtime/core,runtime/replayandreplayer.py; package-wide mypy (575 files); both import contracts; ruff.main.🤖 Generated with Claude Code