Conversation
Publish the review records of two stacked PRs through a narrow task_memory ignore exception: - task_2026-09-21_oversized_module_split: cleanup-first and functional split plan for the four modules above the 2,000-line gate that the Issue 26 fixes must touch, grep-backed structural surveys, and the Step 0 baseline report (84 passed / 10 pre-existing or environmental failures; co-location and PDD dense dummy smokes pass). - task_2026-09-21_issue26_correctness_pr: the execution specification landed verbatim (its Markdown hard line breaks keep their trailing double spaces) with an Amendments table recording the verified facts and the decisions from the planning interview. No source code changes.
fwyc0573
force-pushed
the
refactor/oversized-module-split
branch
from
September 21, 2026 14:06
41dabfb to
56efdf7
Compare
…amily `frontier/config/config.py` was 5,720 lines, far above the 2,000-line maintainability gate in AGENTS.md, and the Issue 26 correctness fixes have to edit it. Splitting it first keeps that PR's diff readable. Cleanup first, verified unreferenced by grep at edit time: - removed `BaseExecutionTimePredictorConfig.validate_linear_op_input`, which had no caller anywhere in the repository - replaced the two parallel CC-backend dispatch ladders with one ordered (type key, config class, creator) table, preserving evaluation order - replaced four copies of a field-resolution closure with one reader, and three identical hasattr-guarded field triplets with one helper - dropped a method-local re-import of `dataclasses.replace` Then the split, twelve modules with the largest at 1,888 lines. The two groups extracted from `ClusterConfig` are mixins it inherits rather than free functions, so the move leaves method bodies, method names and every call site untouched, including the four methods other modules and tests call directly. Every module carries the imports its own annotations need, because the flat CLI generator resolves annotation strings in the defining module's namespace. Also adds `tests/e2e/refactor_fidelity/`, the 67-case matrix that gates this branch. It runs each case through a checked-in example wrapper on both this branch and a read-only checkout of the base commit and requires exact equality of every artifact, plus of the predictor cache file names. Gates for this commit: - generated CLI flag set identical, 753 flags - all 36 externally imported names resolve from both import paths - ClusterConfig keeps its 181 dataclass fields - configuration unit selection, 62 files: 10 failed / 671 passed on both sides with byte-identical failure identities, all pre-existing drift - fidelity matrix: 67 identical, 0 mismatched, 0 cache name differences
Two cases were reported as failures in one matrix run, passed when re-run individually, and by then their logs had been overwritten by the re-run, so the cause could not be established from the record. - A case that exits zero but appears to have written nothing is now retried for up to ten seconds before being recorded as a failure. A directory listing can lag the child process, and a spurious failure is worse than waiting. A genuinely empty run still fails, only later. - The last twenty lines of a failing case's log are stored in results.jsonl and printed by compare, so a failure record survives a later re-run of the same case. Also records an independent validation of the config split: commit 99922d2 was measured again from a read-only detached checkout of itself rather than from the shared working tree, giving 67 of 67 cases identical, zero mismatches, and zero predictor cache name differences.
…ility `vllm_v1_engine_replica_scheduler.py` was 5,138 lines. It is one of the four modules above the 2,000-line gate that the Issue 26 correctness fixes must edit, so it is split here rather than in that pull request. Cleanup first: removed `_attach_afd_metadata_if_needed`, 65 lines with no caller anywhere in the repository, duplicating `frontier/scheduler/utils/afd_metadata.py`. Eight modules, largest 1,386 lines: - the scheduler keeps construction, batch creation, `on_batch_end`, both scheduling phases, the two-phase entry point and the public overrides - vllm_v1_mtp_wait: output-wait policy for target-embedded MTP - vllm_v1_role_schedules: the single-role entry points a disaggregated cluster drives - vllm_v1_kv_allocation: token accounting, block allocation, preemption - vllm_v1_iteration_policy: policy, fast lanes, CUDA graph sizing, speculative-decoding batch metadata, decision-log emission - vllm_v1_prefix_cache: admission and the identity ledger - vllm_v1_decode_attn_cohort: PD-AF decode-attention cohort state - vllm_v1_decision_log: the optional JSONL decision log Each group is a mixin the scheduler inherits, placed before `BaseReplicaScheduler`, so the bodies, the names and the call sites are unchanged and the private methods four subclasses override keep resolving to the subclass. That was verified for every overridden name. One test changed: test_prefix_cache_identity_ledger patched the decision-log function on the scheduler module, and the prefix-cache methods now resolve it in their own module, so the patch target moved with them. Gates: - CLI flag set identical - unit selection of 73 files: 13 failed / 1570 passed / 19 skipped / 5 errors on both sides, with identical failure identities - fidelity matrix: 67 identical, 0 mismatched, 0 cache name differences
A cache name difference has two very different causes and the report could not tell them apart. An artifact that keeps its model name but changes hash means a training identity or cache key changed, which a behavior-preserving refactor must not do. An artifact present on one side only means a model started or stopped being trained. The comparison now splits every cache name into model name, hash and suffix, classifies each difference as rekeyed, baseline-only or candidate-only, and prints the affected names with their hashes instead of a bare count. The classification is also written to comparison.json. This matters for the next split: the execution-time predictor manager owns the cache keys, so a difference there would be a real finding rather than the input-path artifact that produced a false alarm earlier.
Running the matrix against a shared working tree measures whatever happens to be there at that moment. When two people work on one branch that is a mixture, and a mixture is not a verdict: one such run reported 66 failures that belonged to somebody else's half-finished edit. measure_commit.py takes a SHA, checks it out into its own detached worktree, runs the matrix there under a label named after the commit, and compares against the captured baseline. It runs the harness committed at that revision, so the measurement and the measured code always agree. The checkout is kept by default, because a validated earlier split is the reference you want when a later split differs and you need to attribute the difference. The predictor cache comparison is now skipped, and said to be skipped, unless both sides ran the complete case set. The cache is populated by the cases that ran, so a filtered run would otherwise report every model the other side trained as a difference. Cases present on only one side still fail the verdict: a partial comparison is not a pass. Cache findings are capped at twenty per kind in the printed summary; comparison.json keeps all of them.
`shared_prediction_model_manager.py` was 4,614 lines, the third of the four modules above the 2,000-line gate that the Issue 26 correctness fixes edit. Cleanup first, all three verified definition-only across the repository: `get_required_capabilities`, `get_training_context` and `_get_moe_df_with_derived_features`. Six modules, largest 1,700 lines: - the manager keeps construction, cluster requirement analysis, measurement family and input file selection, training orchestration and the public API - prediction_family_trainers: one method per operator family plus the shared fitting routine - profiling_dataframe_loaders: one loader per profiling CSV with its column validation and derived features - prediction_model_registry: cache keys, trained-model identity, the in-memory registries and the persistent cache - layer_contract_resolution: typed layer contract and TP/EP key resolution, the FFN contract signature, the MoE dataset contract - prediction_model_identity: stateless identity helpers, in a leaf module so every part can import them without importing the manager The boundary is chosen so the routing-identity fix planned for the stacked PR edits the signature and the registry and not the loaders, which is where that defect lives. Eight tests needed their patch target moved with the code they exercise: a test that patches a module-level name no longer intercepts once the method reading it resolves that name in another module. One of the eight is a governance gate rather than a test binding. The allowlist of functions permitted to resolve a raw model architecture profile changed by exactly one line: same function name, same kind, same expected call count of 1, and still 11 entries; only the owning file moves from shared_prediction_model_manager.py to prediction_model_identity.py. No entry was added, removed or given a higher count, so the property the gate protects is unchanged. Gates: - no name loaded at runtime without a runtime binding, checked mechanically across every module in the package - unit selection widened to the predictor tests, 2300-odd tests per side: identical results with identical failure identities
`sklearn_moe_execution_time_predictor.py` was 3,539 lines, the last of the four modules above the 2,000-line gate that the Issue 26 correctness fixes edit. With this commit every module the stacked PR touches is under the gate. Cleanup first: removed `_is_grouped_gemm_on_demand_mode`, 14 lines with no reference anywhere in the repository. Six modules, largest 1,557 lines: - the predictor keeps dummy-mode timing, layer classification, the attention query cache, layer and stage orchestration, and the public entry points - moe_operator_times: gating, routing top-k, shuffling, the grouped expert GEMM, the expert-parallel collective, and their token-count resolution - moe_routing_workload: the expert-load distribution and the per-lane routed workload it produces - moe_dataset_training: dataset admission, per-operator training, and the load-imbalance feature list - moe_mtp_replay: MoE time for speculative-decoding replay rows - moe_predictor_helpers: the module-level helpers, in a leaf module so the mixins can use them without importing the predictor Three tests gained a line rather than changing one. `MOE_FAMILY` was a single binding before the split and is now imported by two modules, both of which the path under test reads, so a fake operator family has to be installed in both. The assertions are unchanged. Gates: - no name loaded at runtime without a runtime binding, checked mechanically - CLI flag set identical - unit selection widened to 183 files: identical on both sides at 28 failed, 3016 passed, 55 skipped, 12 errors, with identical failure identities The first attempt at that selection aborted at collection because seven modules import torch, which the minimal CPU environment excludes, so both sides trivially agreed on nothing. That run was discarded and rerun with --continue-on-collection-errors; the lesson is recorded in the task notes.
Two sides that both ran nothing agree trivially, and two sides that both ran the same three cases agree on three cases. Neither is evidence about the branch, but both exited zero and printed no mismatches. The comparison now reports how many cases it compared out of the case table, and refuses to pass unless both sides ran the complete table. A deliberate subset is still possible with --allow-partial, which makes the narrowing explicit instead of silent. Prompted by the same failure mode appearing in the unit gate: a widened pytest selection pulled in modules that import torch, pytest stopped at collection, and two empty result sets were reported as identical. A parity check has to assert that the work ran, not only that the sides agree about it.
The previous commit added the second MOE_FAMILY patch twice in two of the three tests. The edit applied a literal replacement and then a regular expression that inspected only the setattr block, not the line following it, so the two blocks the literal pass had already handled were extended again. The duplicates were inert: the second call set the same attribute to the same value, and the test file passed either way. They are removed because this branch's claim is a reviewed pure move, and a copy-paste artifact in the diff gives a reviewer reason to wonder what else was pasted. Three mirror patches remain, one per test, which is the intended shape.
Sixty-six of the sixty-seven cases run one attention DP lane, where lane placement cannot vary, so a change to how requests are placed on lanes had almost no blast radius in this matrix. Four dense co-location cases now cover it: two lanes and four lanes with online arrivals, two lanes over two replicas offline, and two lanes over two replicas online. Arrivals spread over many scheduling calls are what matters, because placement that restarts each call only differs from placement that persists when the stream is divided. Measured on the base commit, three of the four sit in the defective regime and the ledger records which lane served each batch, so they can see a fix rather than merely run. dp_dense_online_lanes2_replicas2 is the sharpest: replicas split 28/28 while every record is lane 0, which is one rotation working and the other not, in one run. The prefill role reaches the same placement path but no shipped recipe can give it more than one lane, and the group docstring records why: a dense model is rejected outright in a disaggregated architecture, and the MoE wrappers require ATTN_TP == MOE_TP * MOE_EP while the runtime requires attn_tp * attn_dp == moe_tp * moe_ep, which have no common solution above one lane. That role needs unit tests, not this matrix.
fwyc0573
marked this pull request as ready for review
September 21, 2026 17:23
fwyc0573
marked this pull request as draft
September 21, 2026 18:25
compare_labels skipped a case whose baseline run failed, and baseline_failures was not in the failure predicate. Completeness tested whether both result tables held every case id, which a table of failure records satisfies. So a matrix in which all 71 cases failed on both sides compared nothing, returned 0, and measure_commit printed VERDICT: IDENTICAL. The same branch hid a new candidate failure whenever the baseline had failed on the same case, and list_artifacts returning [] for an absent directory made two deleted artifact directories compare equal. Completeness now means compared rather than present, every reason a case was not compared is itself a failure, and an unexplained gap fails too so a later code path cannot drop a case silently. An absent artifact directory is reported, and a successful record is checked against the files still on disk before its contents are compared. For provenance, each case record now carries source_revision, source_dirty, harness_revision and a digest of the case definition. A continuation whose retained records disagree is refused before any case runs, a retained id that has left the case table is dropped and reported, and measure_commit refuses a reused checkout with local modifications instead of cleaning it. Cache file names are compared only when both sides ran the whole table from a clean cache. Four false successes reproduce against the pre-fix harness and none against this one, with the healthy-identical and healthy-mismatch controls unchanged. Every label captured before this change now fails for want of provenance, which is intended: both sides are recaptured in the next step. No file under frontier/ is touched.
These ran as task-local commands during the split, which means they happened once. The matrix cannot see what they see: a move-heavy refactor fails at import or construction time, before any simulation runs. Covered: every config dataclass still resolves its own annotations in its defining module, which is the constraint flat_dataclass imposes and the one a twelve-way split can quietly break; the flat CLI can still be generated; every name another module imports from frontier.config or frontier.config.config resolves from the entry point it actually names; the mixin order of the three split classes, including that BaseReplicaScheduler stays behind every extracted mixin; SGLang still reaches the decision-log helper that moved into the iteration policy; ClusterConfig's two mixins contribute no fields; a cached estimator written by one registry loads into a fresh one; every new module imports on its own; and the TYPE_CHECKING-only import that caused I1 stays a runtime import. Two findings while writing them. ClusterConfig's annotations do not resolve, because cc_backend_config is annotated BaseCCBackendConfig and the module does not import it at runtime. That predates the split -- the same lookup fails on 1f694f7 -- so it is pinned as a known exception and a new one fails the test, rather than being asserted away. And PrecisionType, get_quantization_manager and global_vars live on the package and never on config.py, so each import site is checked against the module it names.
The review asked whether existing receipts already covered the final tree. They do not. db15e64..5ef96b5 touches only cases.py, so the production tree was unchanged at the tip, but the candidate label held 67 records and no DP-placement case, and the baseline label was an assembled partial run: its last execution was a filtered dp_ run of four cases merged onto 67 without a cache clean, which is why it reported 72 cases over 71 result lines. Both sides were recaptured as single clean full 71-case runs from detached checkouts, driven by the harness committed at bb582a4. 71 of 71 compared, 71 identical, nothing failed, no missing evidence, no provenance findings, and the predictor cache names compared and matched across all 426 files. This is the first run where the count means what it says, because the gate that produces it was corrected first. Whole tests/unit: 84 failed, 3679 passed, 49 skipped, against 84, 3644, 49 on 1f694f7, with the failure identities byte-identical and the 35 extra passes being exactly the tests added in these two checkpoints. Ten modules do not collect on either side for want of torch or matplotlib and are listed rather than counted as passing. All 142 estimators in the baseline-produced cache unpickle and run under the split code, which cache-name equality could not have shown. The status header said Step 0 and Step 7 NOT_STARTED while Steps 3 to 6 had landed; it is now correct, and the summary distinguishes the historical 67-case runs from the current 71.
fwyc0573
marked this pull request as ready for review
September 22, 2026 00:44
The predictor-cache comparison accepted `cache_clean_before_run` plus an empty `case_filter` as proof of one clean full run. `--start` and `--limit` narrow the executed selection without writing a filter, so a clean partial run merged onto full retained results passed the predicate with a partially populated cache. The manifest already records `cases_executed_in_last_run`; require it to equal the full case table on both sides, and treat a manifest without the field as ineligible. Gate tests cover the `--limit` and `--start` shapes, symmetric partial caches, and the missing field; the healthy case now asserts `predictor_cache_compared`. The Checkpoint B verdict was re-derived with the corrected rule on the retained outputs and is unchanged (71 of 71, 0 cache differences). External review finding C34-01.
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.
Problem
AGENTS.mdsets a 2,000-line soft limit on any critical module. Four modules that the Issue 26 correctness fixes have to edit were far above it. Editing them in the same pull request as the behavior fixes would have made that diff unreadable, because a reviewer could not tell a moved line from a changed one.This PR brings those four under the gate through a cleanup-first pass and a functional split, with no behavior change and no numeric change. The correctness work is stacked on top as #35.
Result
frontier/config/config.pyfrontier/scheduler/replica_scheduler/vllm_v1_engine_replica_scheduler.pyfrontier/execution_time_predictor/shared_prediction_model_manager.pyfrontier/execution_time_predictor/sklearn_moe_execution_time_predictor.pyFive modules remain above 2,000 lines:
sklearn_execution_time_predictor.py8263,metrics_store.py5586,sklearn_disaggregation_execution_time_predictor.py2985,profiling/attention/main.py2157,entities/request.py2125. These were named as out of scope inplan.mdsection 1, in commit 56efdf7, before any source change was made, not justified afterwards. The gate applies to modules a change touches, and the Issue 26 fixes touch none of these five. They are a documented boundary, not an oversight.How the no-change claim is verified
A 71-case matrix runs each case through a checked-in example wrapper on both this branch and a read-only checkout of the base commit, then compares every artifact the run produces. The gate is exact equality, not a tolerance: a behavior-preserving refactor has no reason to change a simulated number.
Compared per case:
request_metrics.csv,system_metrics.json,frontier_stage_batch_ledger.jsonl,op_precision_metadata.csvandconfig.json, plus the set of files produced. Only three run-specific absolute paths are normalized; the metrics artifacts carry no timestamps, wall-clock durations or hostnames, so nothing else needs to be.Coverage: co-location, sequential PDD and sequential PD-AF; dense and MoE; offline and online; the dummy predictor and the checked-in profiling CSVs; 4 to 64 requests, prompts from 128 to 3584 tokens, three arrival rates; chunked prefill, all three decode CUDA graph modes, prefix caching, speculative decoding, thinking mode; TP, PP, attention DP and EP variations; the Sarathi and SGLang schedulers.
Each measurement is taken from a read-only detached checkout of its own commit, never from a shared working tree, so no result can be a mixture of unrelated edits. The harness lives in
tests/e2e/refactor_fidelity/;measure_commit.pyperforms one such measurement end to end.The predictor cache comparison
The matrix also compares the names of the predictor cache files each side produces. This matters because output equality alone would not catch a changed training identity: retraining from the same CSV reproduces the same numbers, so a re-keyed estimator is invisible in the metrics.
It is the load-bearing check for the manager split, which moves
_get_model_hash,_get_hash_relevant_config, the trained-model registry and the FFN contract signature into different modules. All 426 cache file names matched, and the comparison classifies any difference as re-keyed, baseline-only or candidate-only so that a hash change cannot be mistaken for a new model.Correction after external review (2026-09-22, C34-01). The eligibility rule for this comparison accepted
cache_clean_before_runplus an emptycase_filter, which a--start/--limitcontinuation also satisfies, so a clean partial run merged onto full retained results could have compared a partially populated cache.2310417requires each manifest'scases_executed_in_last_runto equal the full case table and adds gate tests for--limit,--start, symmetric partial caches and a manifest without the field. The acceptance verdict above was re-derived on the retained outputs with the corrected rule and is unchanged: both labels executed all 71 cases,predictor_cache_compared: true, 0 cache differences (report).Review round 1: the gate was corrected before it was trusted
Maintainer review found that the harness producing the evidence above could report success without having compared anything. That was addressed first, because a passing result from an unsound gate is worth less than no result.
The gate could pass while comparing nothing (R34-01).
baseline_failureswas missing from the failure predicate, and completeness tested only whether both result tables contained every case id — which a table of failure records satisfies. Separately,list_artifactsreturns[]for a path that does not exist, so two deleted artifact directories compared as identical. Completeness now means compared, not present;missing_evidence_forchecks that a successful record still names a directory that exists and whose files match the recorded inventory; and anunexplainedbucket lists any case that was neither compared nor reported under a specific finding, so a future code path cannot drop a case silently and still pass.One label could describe several measurements (R34-02). Continuation runs merged retained records and then rewrote the label manifest with the current run's revision and cache listing, and a reused checkout was accepted after comparing
HEADalone. Every case record now carriessource_revision,source_dirtyandharness_revision; a continuation whose retained records disagree is refused before anything runs;case_countcounts lines actually written; cache names are compared only when both sides' manifests show one clean, unfiltered run whose executed case list equals the whole table, so--start/--limitcontinuations are excluded as well (tightened after external review finding C34-01,2310417); and a reused checkout with modifications is reported and refused rather than cleaned.Both were reproduced before and after against the pre-fix harness with identical synthetic inputs. Four scenarios that returned exit 0 before now fail; both healthy controls are unchanged:
Committed as
tests/unit/test_refactor_fidelity_gate.py, 22 tests. One of them asserts that the dirty-checkout check leaves the modified file untouched, so the check cannot start repairing a checkout in order to pass itself.Review round 2: the final record, taken with the corrected gate
The earlier chronological runs were genuine per step, but none of them could serve as the acceptance record (R34-03).
candidate_db15e64held 67 records and no DP-placement case, so the four cases that took the table from 67 to 71 had never run against the refactor tip; and thebaselinelabel's last execution was a filtered four-case run merged onto 67 without a cache clean, which is why it reported acase_countof 72 over 71 result lines. The provenance stamp added above also invalidates every label captured before it, by design.Both sides were therefore recaptured as single clean full runs, driven by the harness at one revision:
1f694f7bb582a4bb582a4bb582a471 of 71 compared, 71 identical, 0 mismatched, no failures on either side, no missing evidence, no differing case definitions, no cases uncompared, no provenance findings, and 0 predictor-cache differences. Comparison exit code 0.
Whole
tests/unit: 84 failed / 3679 passed / 49 skipped here, against 84 / 3644 / 49 on1f694f7. The 84 failure identities are byte-identical —diffof the sortedFAILEDlists is empty — and the 35 extra passes are exactly the tests added in these two rounds. Ten modules do not collect on either side for want oftorchormatplotlib; they are listed in the report rather than counted as passing. This is a regression comparison, not a claim that those 84 pass.The checks that specifically protect a move-heavy refactor are now committed rather than run once (R34-04):
tests/unit/test_module_split_boundaries.py, 13 tests covering annotation resolution in each defining module, generation of the flat CLI, the public re-export surface from both entry points, mixin order for each split class,ClusterConfigfield ownership, loading a cached estimator into a fresh registry, and importing every split module in a fresh interpreter.Two results from that work are worth stating on their own:
n_features_in_also predict. A pickle records the module path of the class it holds, and moving code is what invalidates that; matching cache names could not have shown it.typing.get_type_hints(ClusterConfig)raisesNameError: BaseCCBackendConfig, becausecc_backend_configis annotated with a name the module does not import at runtime. The same lookup fails on1f694f7, so it predates the split and the generated CLI is unaffected. It is pinned inKNOWN_UNRESOLVED_CONFIG_ANNOTATIONSso a new unresolvable annotation fails, and so does fixing this one without dropping the pin.Test changes, in three kinds
Eight test files changed. No assertion, expected value or tolerance changed anywhere.
A patch target moved with the code. Most of them. A test patches or imports a module-level name and the code under test now resolves it in the module it moved to. A patch aimed at the wrong module does not fail loudly, it silently stops intercepting, so these were found by comparing failure identities between the two sides rather than failure counts. Counts alone would have hidden them, because several of the same files already have pre-existing failures.
A governance allowlist entry.
test_raw_model_profile_resolution_callsites_are_allowlistedis an exhaustive, commented list of every (file, function, kind) permitted to resolve a raw model architecture profile, with how many times each may do so. The diff there is one line, and it is a file path: same function_resolve_model_architecture_profile, same kindhelper, same expected count of 1, still 11 entries. The property the gate protects is intact; only the module hosting one entry moved. An entry added, removed, or given a higher count would be the change worth scrutiny, and none of those happened.A test that gained a patch, as evidence rather than cost.
MOE_FAMILYwas one binding in one module and is now imported by two modules that the path under test both read, so three tests install the fake family in both. The assertion is unchanged, and the second patch is needed precisely because the production code genuinely reads the name in both places.What a reviewer should check
1f694f7and the tip. Every name other modules import fromfrontier.configstill resolves, from whichever of the two entry points imports it.Review material
Tracked under
task_memory/task_2026-09-21_oversized_module_split/:plan.md(scope, boundaries, sequencing, matrix design, acceptance),module_survey.md(grep-backed survey of all four modules),issues.md(every regression with cause and resolution),progress.md,summary.md, and the test reports — including the base-commit baseline, the failures that already exist onmain, and the two rounds above:test_report_2026-09-22_checkpoint_a_fidelity_gate.md— the gate defects, the corrections, and the before/after reproductiontest_report_2026-09-22_checkpoint_b_final_evidence.md— the final 71-case record, the unit-suite comparison, the retained checks, and the limits of eachStatus
Open for review (no longer a draft on GitHub). Do not merge before review.