Repository navigation
Milestone A: unified assembly and verified execution boundaries - #26
Conversation
…n slow runners On devel fb18f7d, run 40986148907 failed once on backend 3.13 in test_shared_bounded_queue_duplicates_and_both_cancellation_paths. The signals were ['soft_stop', 'terminate'] where ['soft_stop'] was expected. The cooperatively cancelled child had not exited within the test helper's short default soft-stop grace on a loaded runner, so the supervisor correctly escalated. A rerun passed. The test now waits for the cooperative child's durable ready boundary instead of assuming it is ready. It gives this test's supervisor a startup-safe grace and wall budget; the uncooperative-worker test still exercises the short escalation timers. The exact ['soft_stop']-only assertion is kept, and the test also proves that the cooperative child itself acknowledged the stop: - same pid as the ready record; - exit code 0; - the child's durable terminal record carries the result token, state cancelled and reason cooperative_stop; - the store reaches cancelled. Authored by Engineering (ExecutionCancellation.patch 134d66cb): 50/50 repetitions under four CPU busy loops and 459 backend selections. Root validation at 29b329c: backend runtime 377 passed and 10/10 repeats. Constraint: Test-only. Production supervisor timeouts are unchanged; the signal assertion is not loosened Rejected: Accept ['soft_stop', 'terminate'] | would stop proving that cooperative workers honour soft stop Confidence: high Scope-risk: narrow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ard against new ones Milestone A M4. Main's legacy-boundary audit (archive MilestoneA AcceptanceV2 a9f1f6c3, UnregisteredLegacyConsumersV1 31c7bbbf) found 129 registered rows and 16 direct legacy consumers with no registration. They are now registered in RefactorTransitionBoundariesV1.json in three groups: - legacy-notebooks: 4 retained .ipynb workflows. Owner WS5; exit P06-06, P09-02, P09-03. - retained-research-benchmark-tools: 10 scripts/benchmarks tools. Owner RS; research-retained. Exit P09-03, only after an explicit RS consumer-exit review. - legacy-browser-wire: 2 browser clients. Existing rows and the historical planning record are unchanged. No downstream hash pin of this file needed replacing. A solver-free guard (tests/refactor/test_legacy_consumer_registration.py, about 1.5 s) re-runs the audit scan over non-test Python files, notebooks and browser transport files. It fails when a direct legacy consumer outside the W03 facade and the legacy package is unregistered, and when a registered path disappears without being marked historical. LREG-R1 from the first review is closed. The guard now covers every refinement lane and executor that targets legacy, whatever its module prefix, so retargeting a lane to backend.main:run_jv, or adding a new backend lane, fails. The old perovskite-sim application remains the disclosed, registered legacy product surface. It stays read-only until P06-06 (PlanV2 section 5.5, D19). The new packages reach legacy only through W03. Reviews: Performance LegacyRegistrationReviewV1 4fb998ac rejected the first version on LREG-R1; LegacyRegistrationReviewV2 accepted RegistrationV3 (defa25be). Constraint: M-class; registrations only, with existing rows and history untouched Confidence: high Scope-risk: narrow Directive: Any new direct legacy consumer must be registered with an owner and exit gate, or routed through W03; the guard enforces this Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cap each slow node Run 2 of the scheduled suite (40986149015) exposed three defects in the planner and runner. 1. Node identity drift. tests/unit/discretization/test_stable_bernoulli_flux.py parametrizes over np.geomspace values. Their decimal reprs in pytest node IDs differed between the planning runner and the execution runner, so the strict selection-drift check refused the shard (exit 4). Planning now verifies by test-function identity with an exact per-function node count. Each worker then selects its locally collected parameter cases. The drift check stays strict for everything else, and the test is unchanged. 2. Two shards hit the 110-minute job timeout. They are split using measured durations from both runs: coverage is split per integration file, and slow shards by function. 3. A stalled solve could consume a whole shard. Scheduled slow runs now go through a subprocess-per-node watchdog. A node over its measured wall cap is recorded as an explicit "timeout" failure, never a skip or xfail. The runner continues with the next node and writes incremental named receipts, so failures and the active test survive an outer job timeout. pytest-timeout is not in the lock, so no dependency was added. Motivating case: the legacy CIGS notch slow cases took 273 s in run 1, timed out in run 2, and took 210-220 s locally. Main's investigation (results/ScheduledSuite/CigsNotchStabilityV1) finds uncapped zero-bias Robin preconditioning of up to 250,136 RHS calls. It is probable legacy numerical stagnation, not yet attributed on Linux. Two-run classification (CiCoverageV3.json acd8a899): - the 51 earlier unknowns are now 6 mitigated environment cases, 33 passing but environment-sensitive cases and 12 repeated failures; - 0 confirmed refactor regressions. Per-push ci.yml, every test, threshold and marker are unchanged. Constraint: Scheduled-only; a timeout is a failure outcome, and no test or threshold changed Confidence: medium Scope-risk: narrow Not-tested: native Linux timing of the new shard plan (next scheduled run) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er by default Milestone A M1. These production entry points now build their systems through the unified layout/term/assembly layer by default: - steady DC, operating-point admission, DC backward-Euler transients and DC AC; - ionic steady, ionic initialization, ionic J-V, ionic transients (Core's assembled steppers) and ionic AC. The selection lives in solarlab.equations.implementation. The previous equation classes stay available through the explicit opt-out equation_implementation(LEGACY) or NEWCORE_EQUATIONS=legacy for one release. Invalid values fail closed and nothing falls back silently. 2D steady is unswitched in this commit and binds legacy explicitly; its switch is a separate, separately reviewed patch. Conditions closed by this landing: - PW-C1, provenance. Every production admission site (13 in scope) binds the resolved implementation, the actual Assembler identity and an analysis digest before any equation is evaluated, including nested initialization. Legacy carries an explicit legacy binding. Assembled-produced and legacy-produced artifacts are distinguishable. - PW-C2, worker selection. The selection is captured and forwarded with the job/environment binding before engine imports. Parent and worker agree under both selections, including spawned and queued execution. - PW-C3, reference paths. Core's parity tests run their reference side under LEGACY. A one-ulp assembled mutation fails 9/9. - PW-C4, rebase. The rebase is pinned and uses Q6's admitted-source mechanism. - PW-C5, evidence. The example audits derive the exact expected bindings independently and check them before any masking. A permanent negative control rejects resealed wrong identities. Reviews: - Science ProductionWiringReviewV1 30bc958d (accepted_with_conditions); - Performance ProductionWiringDeltaReviewV1 133d8f79 plus Addendum bc311676 (PW-C1..C5 closed). Patch ProductionWiringV2 c0c48147, 33 files. All per-file pins verified. Evidence: - Architecture: every ci.yml job green under default and NEWCORE_EQUATIONS=legacy; test_all 7008/7008; examples 50/50 (1D) and 139/139 (2D). - Root, at 8a0bf11: core 4699 (legacy 4699), packages 1772 (legacy 1772), static 90, scan 26, frontend 47+4; mypy, ruff and imports clean. Backend runtime 382 passed. One failure is the pre-existing ps sampler defect on the base (RuntimeControl fix pending), which this landing does not touch. Constraint: M-class, bitwise; no gate, tolerance or physics change Rejected: Delete the duplicated legacy Problem/Step/SmallSignal classes now | the deletion plan waits one release, with frozen-baseline digests as the post-deletion proof Confidence: high Scope-risk: broad Directive: New production entry points must bind provenance through solarlab.equations.implementation before evaluation; BDF stays original-only until migrated with its own parity proof Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…without solvers CND3 for the 2026-11-08 BDF development-mainline flip. Publication: experiments/bdf_results.py records - the requested and effective method; - the requested and effective BDF options, including clipped caps and the remaining wall budget; - physical controls, drive knots and both sides of every discontinuity; - the event/restart policy; - accepted, rejected, auxiliary and sample work; - the requested sample clocks; - the last accepted time and the termination; - the equation-implementation binding; - source, runtime, device and initial hashes; - qualification=false, labelled experimental and unqualified. The requested end, the last accepted time, optional samples and a failed candidate state stay distinct, so a failed run can never present a reached requested end. Readback: the solver-free reader uses only the saved historical options and arrays. It never reconstructs a missing accepted end, and it refuses tampered and mixed-method records. The restart, sample and work-history invariants (CND3-R1) are enforced at capture, at publication and at readback. BDF capability: BDF is original-equation-only until it is migrated onto the assembler with its own parity proof. Two original-only BDF operator labels are registered in the wiring's provenance helper and bind assembled=false. Initial capture and admission inside BDF run under the explicit legacy scope, and BE still follows the selector. Forcing an assembled BDF run is refused rather than silently run as original. Reviews (Performance): - BdfPublicationReviewV1 43ab4a74 rejected CND3-R1; - V2 9895b90d accepted it, conditional on the post-wiring hook rebase; - V3 accepted the hook rebase and the capability set (patch 9e0fdcbf). Engineering evidence: test_all passes 7124 in both modes, and all 40 Readiness histories read back (16 no-history cases are refused). Root validation at 0585f53 with this commit, Activation V2 and RuntimeControl V3 stacked, CI-equivalent: - core 4752 (legacy 4752); packages 1872 (legacy 1872); - static 90, backend 383 + control 56, scan 26, frontend 47 + 4; - mypy, ruff and import contracts clean. Constraint: BE publication bytes unchanged; no physics, budget or default change; qualification=false Rejected: Let BDF follow the equation selector now | BDF readiness and the frozen allowlist were proven only on original equations Confidence: high Scope-risk: moderate Directive: Do not flip any BDF default until CND2 and a new freeze are done (CommanderBdfDefaultFreezeV1); readers must stay solver-free Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dark-to-light steps E_dark_to_light_step failed for every integrator (BE, BE-h/4 and BDF). Core diagnosed a numerical initialization/globalization defect in the near-empty minority-hole log-coordinate basin. There, whole-direction damping near 3.19e-19 trips the original stagnation guard. A full-transient density seed converges the shared BE/BDF1 target in two Newton iterations at both h and h/4, with physical and Newton controls unchanged. This commit adds the seed (solvers/time/carrier_seed.py) and its bounded trigger. The seed is DEFAULT-DISABLED and unwired: no production caller uses it yet. The trigger was frozen before any evidence run (TriggerNote 16904671). It fires only after the existing seed's Newton attempt fails with the recorded basin signature, at most once per affected step. Seed and retry work are debited separately and stay visible in the accounting. ACT-C1 is fixed: the deadline type BE derives from RuntimeError is now labelled correctly in the ledger. Evidence (ReadinessV5, via test-only injection): - 73 protected histories are numerically bitwise, with zero activations; - BE completes at 4/0/1 and BE-h/4 at 16/0/1, with one recovery each; - adaptive BDF still fails at 2/13/16 and stays ineligible. Reviews: Science DarkToLightReviewV1 b639d916, ActivationReviewV1 b55cdf03 and its Addendum accepted it as default-disabled source (ACT-C1 closed). Patch b3076ae2. Constraint: Default-disabled; no production caller, gate, profile, budget or allowlist change Confidence: high Scope-risk: narrow Directive: BE dark-to-light becomes supported only after reviewed wiring through HookPoints.md, publication/parity validation and a new scope freeze; BDF dark-to-light stays ineligible Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lure The defect, reproduced on fb18f7d: `ps -axo` in worker._process_info took longer than its 0.5 s cap and raised TimeoutExpired. The supervisor then killed and reaped the child with supervisor_control_failure, but left the store attempt `running`. HTTP/SSE consumers waited forever for a terminal event. What changes: - Every supervisor control-failure path, including the claim and audit paths, now drives the run to exactly one terminal store state with one terminal event. - RSS sampling retries a timed-out sample a bounded number of times and then fails closed. The budget is never left unenforced. - When storage cannot commit, a fenced recovery intent is kept. - Recovery isolation (RTC-B1): if one intent's audit or publication fails, for example because of a stale receipt.json.part, the other intents are still reconciled. Publication is idempotent and safe against stale .part files. - Fallback intent discovery (RTC-B2): on reopen, every attempt that has a nonterminal failure receipt or control evidence is discovered and terminalized, even if control-failure.json was never published. Resubmitting never returns a stale nonterminal receipt. The 0.5 s sampler cap and the 768 MiB pool budget are unchanged. Test isolation. Under load (load average 8-12), the new supervisor tests failed intermittently. They were legitimate shared_rss_limit stops before child readiness, because the pool budget counts the whole pytest process family (CI lesson class 2). So tests/runtime/test_supervisor_control_failure.py now runs as its own isolated CI group (supervisor_control_failure), like scan_execution and run_client. scripts/test_all.py and the scheduled suite keep that isolation. The tests arm faults only after durable child readiness, and they allow 30 s observation windows. No assertion or limit was weakened. TestChanges.md lists every changed test and the reason. Reviews: Science RuntimeControlReviewV1 f1ba9f11 rejected V1 (RTC-B1/B2). Performance RuntimeControlReviewV2 4bff94ea accepted V2. V3 (patch 4c82a53e) keeps the reviewed V2 production bytes exactly (sha256-verified) and changes only tests and CI isolation. Evidence: - Engineering: 30/30 loaded repetitions (1680 cases) and the 521-case backend, under 6 busy loops. - Root: an independent load check (4 busy loops, load average 15-20) ran the isolated file 10x at 56/56, and the backend at 383 passed. On the full stack, static 90, solvers/time 270 and control 56 all pass. Constraint: Sampler cap and RSS budget unchanged; fail closed rather than run unmeasured Rejected: Raise the ps timeout or the pool budget | would hide, not fix, the missing terminal transition and the test-family RSS coupling Confidence: high Scope-risk: moderate Directive: Any new supervisor exception path must end in exactly one terminal state and event; RSS-budgeted runtime tests belong in an isolated CI group Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re failure On ed97ac3 (run 41044476030), backend 3.11.16 failed its first attempt. All five tests that share the real ionic J-V HTTP module fixture errored in setup: observe_http_case found result["artifacts"] == [] where a completed run should have 15. The rerun passed, and so did 3.13 and 0585f53. Engineering ruled out the shared 768 MiB pool hypothesis: the fixture runs on a separate 3 GiB service pool with a 1536 MiB worker cap. Loaded macOS runs on 3.13 and 3.11 all produced 15 artifacts. The runner tmp holding the raw response is gone, so the cause remains unresolved. This commit adds no fix, only the means to get native evidence: - On failure, the unchanged 15-artifact assertion now records the raw result, the run and attempt store state, the terminal reason and the receipt, both in the message and in a saved evidence file. No behaviour or assertion change. - A workflow_dispatch-only ci.yml job (run_ionic_jv_diagnostics, default 20 repetitions) runs the module on Linux 3.11.16 and 3.13.15 and uploads the per-attempt evidence directories. Per-push jobs are unchanged. Authored by Engineering (IonicJvDiagnostics.patch b9049f5a). Root checks: tests/ci 97 passed; ruff clean; ci.yml parses. Constraint: Diagnostic only; the 15-artifact assertion and all production budgets are unchanged Rejected: Isolate the module now | no native evidence ties the failure to RSS coupling (the fixture has its own 3 GiB pool) Confidence: medium Scope-risk: narrow Not-tested: the Linux diagnostic job itself (dispatched right after this push) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sembly layer by default Completes milestone A M1 for 2D. solve_steady_state_2d, the 2D J-V entry points and the 2D process/HTTP steady worker now build their systems through Core's assembled DC2D binding by default. The explicit LEGACY opt-out works as in the 1D landing (0585f53). Invalid selections fail closed, and nothing falls back silently. Conditions carried over from the 1D landing: - PW-C1: provenance binds the actual implementation, the Assembler identity and the analysis before execution. - PW-C2: workers receive the selection explicitly. - PW-C5: the 2D example audit derives exact expected bindings in the producing clone and checks them before any masking. A negative control rejects resealed wrong identities (3/3). PW2-C1 (from Science's 2D review) is fixed. Explicit J-V continuation now only parses and hash-checks its prefix before the job, without evaluating any equation. The unchanged numerical readmission of the original initial state and every prefix row then runs inside the tail job, immediately after the selected Q6 binding is admitted. Evidence: - The new two-mode ordering tests pass on V2 and fail on V1 at event 0. - Science's probe sees 0 callbacks before admission (V1: 2). - An archived bc3->e5->endpoint continuation replay keeps all 52 diagnostics and 183 evaluations under the exact binding, with 57/57 arrays byte-identical. - 2D examples: 139/139 files, 1754 checks, 0 failures. Reviews: Science ProductionWiring2DReviewV1 9388c7fb (accepted_with_conditions) plus Addendum (PW2-C1 closed). Patch ProductionWiring2DV2 2a4be8bc, 10 files. It is composed onto c0f06bd. The equations/implementation.py change set is identical to the patch hunk, merged with CND3's BDF capability labels. Root validation at 340553c, together with the public-methods patch and the PM-R1 guard: - core 4766 (legacy 4766), packages 1879 (legacy 1879); - static 97, backend 386, scan 26, control 56, frontend 47+4; - mypy, ruff and import-linter clean. Constraint: M-class, bitwise; the 2D 20-state / 500-readout / ordered-port parity is unchanged Confidence: high Scope-risk: moderate Directive: The duplicated original 2D classes stay behind LEGACY for one release; delete them only with the frozen-baseline digests as proof Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd guard their originals Milestone A follow-up from AssemblyMigrationInventoryV1. Part B (ae7a21f) promoted 14 private helpers to public names. Three cross-module private METHOD dependencies were left outside that count. They are now public, and every cross-module caller uses the public name: - CompiledMultipleIonicDevice1D._values -> values - IonicSmallSignal1D._moving_voltage_partial -> moving_voltage_partial - DC1DProblem._contact_exchange -> contact_exchange Two protected runtime callers keep thin private aliases that only delegate, until their own migration. An AST guard in tests/refactor/test_numerical_public_imports.py forbids new cross-module calls of these private methods. PM-R1 is closed. The parity tests' no-original-evaluation guards (ORIGINAL_EVALUATIONS in test_assembly_ionic_drivers.py and test_production_wiring.py) now also cover the public contact_exchange. Before this change, the substituted context blocked the alias but still allowed entry into the new public body. A witness and a negative control prove that refusal now happens. Authors: - Main: PublicMethods.patch 7e468e2d. - Architecture: PM-R1 guard, PublicMethodsGuardV1.diff 381d7458. Reviews: Performance PublicMethodsReviewV1 d03d51cf (accepted_with_conditions) plus Addendum (PM-R1 accepted). Evidence: 40 factories / 160 states and the ionic/AC digests are bitwise unchanged. Root validation is in the previous commit's stack: core 4766 in both modes, packages 1879, static 97 and the full backend. Constraint: M-class, bitwise; aliases are pure delegation Confidence: high Scope-risk: narrow Directive: Remove the two private aliases when their protected runtime callers migrate; the AST guard keeps new private cross-module calls out Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Close P03-01 gap 2. solarlab.solvers no longer imports solarlab.experiments: experiment admission, event/history orchestration and the four BDF entry functions move to solarlab.experiments.bdf, while storage, stepping, seeds and accounting stay in solarlab.solvers.time. A new forbidden import-linter contract keeps the edge from coming back. Moved symbols (old solarlab.solvers.time.* -> solarlab.experiments.bdf.*): bdf_1d._events -> _events bdf_1d._adapter_step_budget -> _adapter_step_budget bdf_1d._generation_density_context -> _generation_density_context bdf_1d.solve_electronic_bdf -> solve_electronic_bdf bdf_1d.solve_ionic_bdf -> solve_ionic_bdf assembled_ionic_bdf._events -> _assembled_events assembled_ionic_bdf._solve -> _solve_assembled assembled_ionic_bdf.solve_assembled_ionic_bdf -> solve_assembled_ionic_bdf assembled_ionic_bdf.solve_assembled_multiple_ionic_bdf -> solve_assembled_multiple_ionic_bdf Lower interfaces added: bdf_1d.BDFProtocol and bdf_1d.bdf_seed. PublicAPI.json is byte-identical and public transient entry points keep their paths; the four direct backend paths carried no external compatibility promise, so no upward re-export is retained. New publication provenance names experiments.bdf; historical module identifiers stay readable unchanged. Evidence (archive Stage03/P03CloseoutV1/Gap2V1): 40 factories / 160 states identical in both equation modes, BDF 74/74 pairs bitwise, historical pins 1297/1297, CND3 readback 76 packets / 365 arrays unchanged; test_all 7239/0 in both modes at ed97ac3. On 3ffd21f every CI selection passes (core 4770, packages 1884, backend, frontend, mypy, ruff, imports 10/10, legacy mode); the only conflict was the test module docstring, kept with both sentences. Science accepted the implementation proof with one condition (Gap2ImplementationReviewV1, dea242ce): G2I-C1, the pre-existing runtime leak through package initialization, closes separately in gap 2b before P03-01 promotion or milestone A closure. Constraint: M-class, bitwise; Architecture-owned 2D files untouched Rejected: lazy upward imports or backend re-exports | they would keep the solvers -> experiments edge alive Confidence: high Scope-risk: moderate Directive: Keep experiment orchestration in solarlab.experiments; the forbidden contract fails on any new solvers -> experiments import Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g tests CI run 41057960755 (backend, Python 3.13.15) failed test_bounded_missing_samples_retry_before_launch_and_recover[1]: the test injected one sampler timeout and then called the real _process_info, so a second, natural miss on a loaded runner raised missing_samples_total to 2. Production accounting was correct; the test's count assumption was not. After the injected misses, the affected policy tests now feed controlled successful samples, so every counted miss is one the test injected. The production supervisor loop, sample bookkeeping, launch gating, child process, readiness barrier, store transitions, termination and reaping all still run. New regression cases exercise a natural sampler timeout explicitly. Test-only, one file. Production code, CI selections, the 0.5 s ps cap, the three-consecutive-miss / two-second sampling bound and the 768 MiB shared RSS budget are unchanged. Engineering's 30/30 loaded receipt; Main's independent review accepted it with no conditions (IonicJvWallAndSamplerReviewV1, 4b5ba4a9; production hunks 0). On cbec478 the isolated group passes 58/58 three times. Constraint: test-only; no production limit or selection change Confidence: high Scope-risk: narrow Not-tested: native Linux repeat under load (next push CI is the first) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s below experiments
Close P03-01 gap 2b (Science condition G2I-C1). Importing a solver module
no longer loads solarlab.experiments at runtime. The leak was pre-existing:
solvers.nonlinear.{ionic_1d, multiple_ionic_1d, steady_1d, steady_2d}
-> equations.implementation -> solarlab.reproducibility.__init__ -> registry
-> sources -> config.identity -> behavior -> legacy_defaults -> device.defaults
-> experiments.jv.inputs. import-linter cannot see this edge, because
package __init__ execution is not part of its graph.
solarlab/reproducibility/__init__.py now resolves its six public exports
lazily (PEP 562), keeping __all__, the defining objects and TYPE_CHECKING
exports intact. A fresh-process guard in the ordinary core selection imports
every solarlab.solvers module as the first requested module and fails if any
solarlab.experiments module is loaded. An injected-import negative control
and export-identity coverage come with it.
No numerical code, equations selector, frozen fixture or public registry
changes. Evidence (archive Stage03/P03CloseoutV1/Gap2bV1): test_all 7273
passed / 0 failed in both equation modes, 320/320 frozen states identical,
0 leaks in 60 fresh solver imports and in all five forbidden contracts, 85
declared-export lists equal __all__, and the release pin is verified. Performance's
independent review accepted it without conditions (Gap2bImplementationReviewV1,
cad97560); the raw dir() namespace delta (TYPE_CHECKING visible, undeclared
submodule attributes deferred) is disclosed as informational.
Constraint: M-class; public names, __all__ and PublicAPI.json unchanged
Rejected: moving device input declarations down now | larger change; the device -> experiments inversion is reported separately
Confidence: high
Scope-risk: narrow
Directive: Keep package __init__ modules free of eager imports that cross layers; the runtime guard catches what import-linter cannot
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d ionic J-V example The installed research_1d_shared ionic J-V v1 example needs more than its 30 s whole-job budget on Linux Python 3.11 runners. Native diagnostic run 41062040632 failed 2 of 20 HTTP attempts, with wall_limit and the supervisor correctly discarding the result. Success-only owned wall was p95 29.97 s on 3.11 against 21.4 s on 3.13. Solver counts are identical, and the extra time is in the curve. The 30 s value is both a production admission ceiling and the example's own request, so a test-only change could not fix it. A new optional field, wall_profile_sha256, binds one IonicJVRegistration to the content of the approved prepared device (schema solarlab.ionic-jv.wall-profile.v1, approved digest b051cb0c) and to the exact v1 recipe. Only that registration may admit up to 128 s. Catalog construction, HTTP preparation and registered child admission each enforce the same content authorization before any numerical work. ExecutionCatalog ceilings are unchanged, so composing catalogs in any order, or calling the direct constructor, cannot widen or narrow another route. Direct IonicJVJob.submit and the raw worker binding keep 30 s. Numerical, RSS, output, counter and sampler limits, and the 15-artifact assertion, are unchanged. The manual diagnostic workflow now also measures uncensored child timing on both Pythons. It fails if the allowance is below ceil(max(128, 4 x p95, 2 x max)). The value stays provisional until that native run exists. This replaces V2 (1264aa73). Main rejected V2 for a catalog-composition hole and an over-broad device opt-in (IonicJvWallAndSamplerReviewV1, 4b5ba4a9). Main authored this version. Science reviewed it independently and accepted it with no pre-landing conditions (IonicJvWallV3ReviewV1, d2675157): zero unauthorized changes over 1120 composition cells, content binding reproduced across clones at different paths, and every negative refused at the child boundary. Author evidence (patch 2c92bd2b): 720-case baseline comparison, Python 3.11 runs 20/20, backend, scan and control groups on both Pythons (1022 tests), and the refactor, API, CI and static gates. On c815a07: static 111, the module 66, packages 1884 non-editable, mypy clean. Constraint: every non-authorized request must match the base outcome exactly Rejected: catalog-level default wall clamp (V2) | composition order changed other routes' ceilings Rejected: raising the fixture's request only | 30 s is also the production admission ceiling Confidence: high Scope-risk: moderate Directive: 128 s is provisional; confirm or revise it from the native uncensored diagnostic before calling it validated Not-tested: native Linux uncensored timing (dispatch the diagnostic workflow after this push) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es by readiness, not wall time Five root backend tests failed in scheduled run 41008968887 under subprocess coverage, and every failure was a race. Engineering's local reproductions in the exact CI coverage mode show the mechanisms: - test_execution malformed-terminal [not_mapping] and [missing_name]: the serial cold start of two workers let the independent worker cross its 3 s cap (wall_limit at 3.139 s, with a succeeded child discarded). - test_transient_worker failed-payload guards [missing-counter], [publication-error] and [publication-wall]: process startup used up the 2 s guard window, so wall_limit pre-empted the intended branch. A now starts both cold interpreters together, waits for both ready boundaries, confirms the independent worker is running and the following worker is queued, then releases the malformed writer. B's failure emitter moved into the lightweight worker fixture module, so a spawned child no longer imports pytest and the transient graph. A readiness/release handshake orders cancellation and each failed return. The existing 2 s guard window starts at the readiness boundary, and fixture-clock.json records the excluded startup and any intentional publication offset. The 3 s and 2 s caps, the 2 s readiness deadline, the 8 s wait bound and every assertion are unchanged: exact reasons, store states, terminal isolation, artifact references and reaping. A new diagnostics helper preserves receipts, terminals, signals, samples, logs and store events on failure. pyproject.toml adds tests/runtime/runtime_evidence.py to the sdist list. Evidence: each case passed 30/30 under CI subprocess coverage with four CPU hogs and 30/30 uncovered. The backend, scan and control groups passed 468/468, and ruff, mypy and 10 import contracts passed. Performance accepted it with condition RRFR-C1 (RootRuntimeFixReviewV1, 53dcd5bf): the next complete scheduled replay must show all five targets and the cancellation case natively, with full receipts. On b4463aa: backend 429, the six target cases passed 5 times, refactor packages green. Constraint: no production code, default or numeric limit changes Rejected: raising the 3 s and 2 s fixture caps | censored timings cannot justify a larger limit Confidence: medium Scope-risk: narrow Directive: The historical native A reason remains unproven; RRFR-C1 requires the scheduled replay's native evidence before A6 can close Not-tested: native Linux covered replay (next scheduled run) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… fail loudly on gaps Scheduled run 41008968887 aborted one coverage shard with pytest INTERNAL_ERROR (exit 3). Its recorder hook called the shared json.dumps, and test_r1_streaming_digest deliberately replaces that function until teardown. That left 1,021 of the shard's 3,231 planned nodes unexecuted, while tests.json still said status=completed. The regression came in with 8a0bf11; fb18f7d was fine. The recorder now binds every callable it uses inside pytest to private names at import: json encode/decode and string quoting, time.monotonic, os.getcwd, open, os.replace, os.fsync, os.write. A failure inside the recorder becomes an explicit recorder_error event. The run goes on with exit 1, KeyboardInterrupt and SystemExit still propagate, and a collection that cannot be verified is refused (UsageError, exit 4) before any test body runs. No status can claim completion when planned nodes are missing: - tests.json reports incomplete with the nodes that never started; - a coverage shard now checks its planned-versus-executed nodes, so os._exit(0) fails and names them; - a failed slow preflight no longer writes completed; - slow-node recorder errors are labelled as such. Selection, sharding, caps, node identities and the journal start/report rows are unchanged. Evidence (archive CiCoverageV4RecorderFix V1-V3, author Performance): the 1,022 previously unexecuted cases now run under the real recorder with coverage. That gives 3,066 complete phase receipts, 984 passed, and the 38 failures are exactly the Main-attributed legacy set. tests/ci passes 160, ruff and mypy are clean, and 51/51 one-line mutants are killed. The negative control runs the real 8a0bf11 recorder. An independent reviewer (RecorderFixReviewV1, 1be0d914, plus its addendum a79b1617) accepted it once finding 1 restored the collection refusal, which V3 does. This push starts the integrated scheduled replay for milestone A (A6), which covers the root runtime replay condition RRFR-C1. Constraint: no change to selection, sharding, caps or planned node identities Confidence: high Scope-risk: narrow Directive: Recorder code must never resolve shared stdlib attributes while tests run; extend the private bindings when adding hooks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efault Close milestone-A blocker A3, BDF equation-adapter closure. Gap 2 (cbec478) and gap 2b (c815a07) closed the import boundary. Until now, the public BDF routes still executed only the original equation classes, and they refused an explicit assembled selection. Admitted electronic public BDF now runs AssembledDC1DProblem through ElectronicStorage1D, on the landed assembled storage facade. Single positive-ion public BDF runs AssembledIonic1DProblem through IonicStorage1D, with the matching assembled BE seed and accounting adapter. The original classes stay reachable through the one-release opt-out, equation_implementation(LEGACY) or NEWCORE_EQUATIONS=legacy, under the existing refusal and no-silent-fallback rules. Dual/shared public BDF stays refused, and multiple-domain public stays BE-only. In equations/implementation.py only the two BDFStep capability rows change. experiments/bdf_results.py keeps the historical original reads and the process-bound manifest refusal, and records true assembled identities. Evidence (archive AdaptiveBDFV1/AssemblyV2, author Core): - 114/114 pairs are bitwise identical between assembled and original: the 74 production pairs, the 20 FreezeV2 envelopes and the 20 ReadinessV2 helpers. That covers 2,782,858 array entries and 41,450 scalars, with zero differing words and zero signed-zero exceptions. - 40/40 frozen histories and 40/160 in both modes are unchanged, and CND3 readback (76 packets, 365 arrays) is unchanged. - test_all passes 7300/0 in both modes. Science accepted the implementation proof with no conditions (A3AssembledBdfReviewV1, 09000912): 45/45 comparisons over 15 configurations, 12/12 publication byte comparisons, and the 8 historical signed-zero words explained and reproduced by a negative control. Because the November 8 BDF evidence was produced with the original equations, the FreezeV2 envelopes are bitwise identical under both implementations. Constraint: M-class, bitwise; selector change limited to the two BDFStep rows Rejected: keeping BDF original-only | milestone A requires existing new-core capabilities on the unified layer Confidence: high Scope-risk: moderate Directive: CND2 rebases onto this commit; the time-method axis and the equation-implementation axis stay separate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed curve phase The native diagnostic run 41190084645 left the A-C4 validation of the 128 s ionic J-V wall allowance censored on Python 3.11. One uncensored sample was cut by the unchanged 25 s numerical curve cap: 25.001 s, 50 of 51 points. To decide the shipping curve cap with evidence, the manual diagnostic workflow gains an input, ionic_jv_measure_curve_64s, which is off by default. When it is set, scripts/ci/ionic_jv_timing.py runs its direct diagnostic children with only the curve-phase ceiling raised to 64 s. The 30 s point cap, the 25 s initialization cap, every iteration, evaluation and tolerance control, and the exact device, grid and voltage sequence are unchanged. The output is labelled a measurement, not a limit recommendation. The override exists only inside the diagnostic harness, through an isolated source-bound adapter. No production module imports it. The HTTP route, the registered child admission and the raw worker binding refuse it, and the shipping recipe and its wall-profile hash are unchanged. No push or pull_request job changes behaviour. Main's independent review accepted it (IonicJvCurveMeasurementReviewV1, 86107c2f): a 37-field comparison of diagnostic versus shipping child policy, and 15 child-boundary plus 4 HTTP refusal probes. Condition ICM-C1 (a combined-mypy FunctionType boundary) is fixed with a Callable boundary and runtime narrowing, without casts or ignores. My pre-landing check found ICM-C2: the static job installs only the dev extra, so the new tests/ci module failed to collect without fastapi. The 15 service-dependent cases moved unchanged to tests/runtime, which the backend job runs, and 5 import-safe cases stay in tests/ci. On 88339c4: dev-only static 167, the moved runtime module 15, backend 444 + scan 26 + control 58, combined mypy on 255 files, and 10 import contracts. Constraint: measurement only; no production, shipping-recipe or numerical-cap change Confidence: high Scope-risk: narrow Directive: Dispatch with run_ionic_jv_diagnostics=true and ionic_jv_measure_curve_64s=true; any shipping cap change needs its own reviewed decision and a new wall profile Not-tested: native Linux measurement (dispatch after this push) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e to json internals Integrated scheduled run 41201981031 (devel 31f43bd) failed shard coverage-root-static even though all 160 tests passed. The outer recorder logged recorder_error at stage journal: "shared string quoting was called". The cause: test_error_serializer_keeps_bound_string_quoting patches json.encoder.encode_basestring_ascii process-wide, and the recorder's privately bound json.dumps still looks up json.encoder module globals (the string encoders, c_make_encoder, _make_iterencode) at call time. Only the error-line serializer was immune. The recorder did fail loudly as designed, but receipts must not depend on what a test patches. Every serialization the recorder performs while tests run (journal rows, tests.json snapshots and error lines) now goes through one private encoder. It is built at import from privately bound primitives and produces output byte-identical to the previous json.dumps calls. Selection, sharding, caps, node identities and journal row bytes are unchanged. Evidence (archive CiCoverageV4RecorderFix/V4, author Performance): plain pytest and the real scheduled coverage-root-static command both pass all 166 tests with zero outer recorder errors, 62/62 mutants are killed, and 664 actual journal rows match V3 bytes. The independent re-check accepted it (RecorderFixReviewV1Addendum2, 25785b90): - a module-scoped probe patching 40 json, json.encoder, json.decoder and json.scanner attributes gives 0 recorder errors on V4 and 31 journal errors on V3; - 61,692 byte comparisons over 10,282 values show 0 mismatches; - an independently built shard plan passes with receipt_complete and an empty error sink. On cf2abf6, with dev-only static: tests/ci 173 passed and ruff is clean. Constraint: byte-identical recorder output; no plan, shard or cap change Confidence: high Scope-risk: narrow Directive: Recorder serialization must never route through the shared json module while tests run; read() still uses the shared decoder and must stay out of test-time paths Not-tested: native scheduled replay with V4 (this push triggers it) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…overage shard The landed root-runtime fixture fix (c20eb58) carries review condition RRFR-C1. The next complete scheduled replay must retain native worker evidence for the five fixed targets and the cancellation case, including successful attempts. Scheduled run 41201981031 passed all six natively, but uploaded no worker evidence. The workflow never set SOLARLAB_ROOT_RUNTIME_EVIDENCE, so tests/runtime/runtime_evidence.py wrote its bundles to pytest's temporary directory, outside the uploaded coverage-result directory (CiCoverageV5, 16ee2e52). The coverage shard step now sets SOLARLAB_ROOT_RUNTIME_EVIDENCE to a per-shard directory inside the uploaded path: coverage-result/runtime-evidence/<shard id>. Two tests/ci guards cover it: - the workflow keeps the destination inside the upload; - evidence files never count as node receipts or coverage data in the run, combine and receipt logic. The helper already retains successful attempts, so no test code changed. Engineering authored it. Locally: dev-only static 175 passed, and the six target cases under subprocess coverage wrote 6 bundles, 10 worker receipts and terminals, and 4 fixture clocks. Performance reviewed it independently and accepted it with no conditions (RootRuntimeEvidenceUploadReviewV1, 2ca35bf4). On 528da42 the dev-only static selection passed 175. This push starts the scheduled replay that should close A6 together with the recorder V4 check and RRFR-C1. Constraint: no change to test assertions, caps, selection or node accounting Confidence: high Scope-risk: narrow Not-tested: native upload contents (this push's scheduled replay) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…AB_ override namespace 91a9323 set SOLARLAB_ROOT_RUNTIME_EVIDENCE for every coverage shard. The legacy research capability gate in perovskite_sim/solver/mol.py rejects any non-empty SOLARLAB_* variable as a product override. So scheduled run 41227286086 failed six root-core legacy-parity tests, and failed the legacy unit physics, models and validation shards, with "environment overrides=SOLARLAB_ROOT_RUNTIME_EVIDENCE". That run was cancelled. The harness variable is now SCHEDULED_RUNTIME_EVIDENCE_DIR. It is non-empty only for coverage-root-backend* shards, the only ones that run the six root-runtime targets; every other shard sees "". tests/runtime/runtime_evidence.py reads the new name. A new tests/ci guard (test_workflow_environment.py) fails if any workflow step sets a non-empty SOLARLAB_* variable. It catches step env, inline assignments, export, GITHUB_ENV writes and os.environ, and allows only the justified command-local SOLARLAB_PYTHON in the frontend Vitest step. The legacy gate and all production code are unchanged. Engineering authored it. The old name reproduces the six failures. With the fix and a non-empty value, the three parity files pass 290/290 and the six runtime targets still write six evidence bundles, successes included. Dev-only static passes 197. Performance reviewed it independently and accepted it with no conditions (RuntimeEvidenceEnvironmentReviewV1, 86be6d2a). On 91a9323, dev-only static passes 197, and the parity files pass 290 with the new variable set non-empty in an all-extras non-editable install. Constraint: perovskite-sim legacy code is read-only; never touch its override gate Rejected: exempting the variable inside the legacy gate | modifies retained legacy product code Confidence: high Scope-risk: narrow Directive: Harness and CI variables must not use the SOLARLAB_ prefix; that namespace is product overrides Not-tested: native scheduled replay (this push triggers it) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tcome The single measured test_all at 4c8617b failed one case, test_real_worker_rejects_internally_wrong_registry_schema_or_facade[job_schema_sha256]. The worker ended failed/worker_exception as expected, but its detail was "Command ['ps', '-p', ..., '-o', 'pid=,ppid=,lstart=,rss='] timed out after 0.5 seconds" instead of the registry refusal. _child_main probes its own startup identity with one ps call before writing started.json and before registry validation. That call sits outside the supervisor's tolerant RSS sampler, and the broad child exception handler turned its TimeoutExpired into worker_exception. On a loaded machine, one slow ps could therefore fail a user's job, or relabel its failure, before the job's real outcome was reached. The child now retries its startup identity within its own private bounds, which mirror the supervisor RSS-sampler bounds: 3 misses within 2 s, with the 0.5 s ps cap unchanged. A test keeps the two pairs equal. If every attempt fails, it records an unavailable startup identity and continues, so the worker reaches its real outcome. The supervisor carries a degraded observation into the final ExecutionReceipt and the persisted RunStore result as child_start_identity_observation, scoped apart from its own sampled parent identity. The field appears only in the degraded case, so normal receipt and result bytes are unchanged. It never replaces the worker's reason or detail. Reconciliation stays uncertain on a missing identity. No signal or reaping decision trusts an unverified identity. JobProvenance, RSS budgets and the sampler policy are unchanged. Engineering authored the fix. Core wrote the two follow-up amendments while Engineering's model was suspended: 90 s caller waits, 7x the measured loaded maximum, and restoring execution.py's local RSS constants so that no new private cross-module import is added. - Performance reviewed it: accepted with condition PSID-C1 (PsStartupIdentityReviewV1, 363005e6). Core's independent recheck closed PSID-C1 (Addendum1, e0db2ec1) with 3/3 normal byte comparisons and 10 safety controls. - My full CI-equivalent check on 4c8617b caught the private-import guard in the packages job. The final bytes pass every selection in both equation modes: core 4776, packages 1906, static 197, backend 467, scan 26, control 58, frontend 47 + 4, mypy 254, ruff, and imports 10/10. Constraint: the 0.5 s ps cap, miss policy, RSS budgets and assertions are unchanged; no JobProvenance schema change Rejected: widening the supervisor sampler to cover startup | the startup probe runs in the child before any fence exists Confidence: high Scope-risk: moderate Directive: Measurement failures must never overwrite a worker's own terminal reason or detail Not-tested: native Linux loaded repetition (push CI is the first) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er node Scheduled coverage runs 41239577644 and 41266788155 both stopped at the 110-minute step limit inside coverage-legacy-integration-interface_flux_balance. The first node started and never reported setup, so three planned nodes ended without outcomes and A6(b) (complete receipts) could not close. The stall comes from the file's module-scoped ladder fixture on ubuntu-24.04. The slow sibling already exhausts the 1200 s slow-shard cap with the same fixture. Locally on macOS the three nodes pass in about 10 s each. LEGACY_COVERAGE_NODE_SECONDS now designates only perovskite-sim/tests/integration/test_interface_flux_balance.py, at 1200 s per node. That shard reuses the slow-shard runner. A bounded collection preflight runs first. Then each node runs in its own process group under `coverage run --parallel-mode`, with the unchanged patch = subprocess config. A node that overruns is killed and recorded as a timeout. It fails the shard in tests.json, the journal and JUnit, and later nodes still run. Completed coverage is kept, and each data file stays at the artifact root, where combine() already looks. The worst case is 80 minutes inside the 110-minute step. The planner refuses a mixed-file designation, and any growth past a 90-minute collection-plus-node budget. The other 269 shard plans are byte-identical on the native 41239577644 inventory, and no node ID changes. A designated node also dumps every thread's stack at 1140 s, just before its kill, using pytest's faulthandler_timeout. A stalled fixture then leaves its location in the node log. A new receipt check certifies the shard only when: - every planned node has a start row and one outcome row; - each timeout carries exactly one timeout label, a JUnit timeout failure and a nonzero exit; - every other node passes the ordinary child receipt; - the aggregate JUnit equals the sum of the child JUnit testcases plus one per labelled node. A failed call plus a teardown error writes two testcases for one node. Performance authored the watchdog. Review CoverageWatchdogReviewV1 (92446a17) accepted it with one blocking finding: the synthetic test's 5 s cap left too little margin on contended runners. I wrote R1 while gpt-6-astra was suspended: - the synthetic test cap is now 30 s; - the JUnit count is exact per node (N2); - the stack dump (N4); - a 17-case receipt matrix plus an escalation test (N1). 15 of 16 single-check mutants are killed; the survivor is the redundant final gate. The reviewer accepted R1 in CoverageWatchdogReviewV1Addendum1 (652af36c). It confirmed the survivor is equivalent and found 24 of 27 of its own mutants killed. I then added the two guards it suggested, as test-only R2: a missing or unparseable child JUnit never counts as a testcase, and slow shards get no stack dump. Addendum2 (3524086a) accepted R2; production bytes are unchanged from R1. Validation: - tests/ci in the exact static-job environment: 222 passed. - The real designated shard at the production cap: 3/3 passed, with complete receipts and 3 coverage files. - An end-to-end call-failure-plus-teardown-error probe: 2 child testcases, receipt complete, shard failed. This bounds and labels the legacy stall; it does not fix it. Expect three disclosed timeouts on ubuntu-24.04. Constraint: perovskite-sim legacy code is read-only; a timeout is never a pass or skip Rejected: raising the 110-minute step limit | the stall never reported setup, so a longer limit only delays the same gap Rejected: excluding the file from coverage | hides planned nodes instead of giving them outcomes Confidence: high Scope-risk: narrow Directive: Designate a legacy coverage file only when it has its own shard and fits the 90-minute budget Not-tested: the native ubuntu stall itself (the push-triggered scheduled run is the first) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On 2026-11-01 the user replaced the commanded Codex worker sessions (Main, Science, Performance, Engineering, Core) with claude-opus-5-5 at effort max and had the original sessions closed. That day gpt-6-astra was suspended for the account twice (403 ModelAccessTemporarilySuspended), stalling both active workers. The old rule that existing workers keep gpt-6-astra now named only closed sessions. CLAUDE.md now states the current policy. The retired Codex sessions' resume ids are kept in the archive execution state (revision 168). The deputy commander and every other governance rule are unchanged. No test or CI step reads the root CLAUDE.md. Constraint: user decision, quoted in ExecutionStateV2 user_records (revision 167) Confidence: high Scope-risk: narrow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…commander On 2026-11-01 the user replaced the Codex deputy (session 01a180c3, terminal term_3c65f4db) with a Claude Code session: d6a3ed06, claude-opus-5-5 at effort max, terminal term_ac765e70. CLAUDE.md now names the new deputy. The old session has been closed; its codex resume id is kept in the archive execution state. The deputy's rules are unchanged: it makes no Git mutations, dispatches or state writes, and it takes over only after the commander has been unreachable for more than 2 hours. The new deputy runs a read-only liveness watchdog on the commander's transcript. It must also get no reply to an acknowledgement request before it may take over. No test or CI step reads the root CLAUDE.md. Constraint: user decision in the commander session, 2026-11-01 Confidence: high Scope-risk: narrow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Supplementary evidence after this PR opened: independent audit CiCoverageV6 Addendum 2. The full push-triggered scheduled run 41286004950 at 20a5dc4 has finished. That commit has the same code as this PR's head 3c88c4c; the only difference is CLAUDE.md. The run had 272 jobs: 257 passed and 15 failed. All 11 root jobs passed, and all 15 failures are legacy shards, which are outside the green claim. The independent auditor reviewed the run (CiCoverageV6Addendum2, kept in the project archive). The A6 verdict stands. This single run meets A6 (a)–(d) by itself:
Updated disclosures
No code changes. Only the user merges. 🤖 Generated with Claude Code |
devel → main at 3c88c4c. This is ready for the user's review. Milestone A's engineering criteria M1–M4 are met within their stated scope, and A6 closes under the agreed CI composition with conditions C1–C4. No scientific qualification is claimed; G9P remains open. Only the user merges.
The branch introduces solarlab, solarlab_server and solarlab_research alongside the retained legacy application. A shared layout/term/assembly layer now serves these routes by default: the admitted 1D steady, backward-Euler and AC routes, connected 2D steady/J-V, and the public electronic and single-positive-ion BDF equation adapters. Typed configuration, source identity, registry contracts, versioned artifacts and job-owned provenance support these execution boundaries.
Gap 2/2b removes the solver-to-experiment dependency, and PM-R1 guards the promoted public methods. The branch also adds:
The startup-identity correction (2af0467) keeps an unavailable optional
psobservation from replacing a worker's real outcome.The scheduled-suite watchdog (20a5dc4) runs the one stalled legacy coverage file node by node under a 1200 s cap. A stall now ends as a labelled timeout with complete receipts instead of missing outcomes.
The last two commits (6da1a07, 3c88c4c) change only the root CLAUDE.md. The density recovery seed remains default-disabled and unwired.
How verified
One-command root suite.
python scripts/test_all.pyran on a fresh clean clone with editable frozen all extras, npm ci, CPython 3.13.15 and BLAS threads at 1. MeasuredRun3 at 6da1a07 passed: 7,509 passed, 0 failed, 0 errors, 198 skipped, 7 xfailed (static 222, core 4,776, packages 1,909, backend 551, frontend 51). 6da1a07 → 3c88c4c changes only CLAUDE.md, which no test or CI code reads. Earlier runs stay on record: MeasuredRun1 at 4c8617b failed (7,460 passed, 1 failed, 0 errors, 198 skipped, 7 xfailed), and MeasuredRun2 at 2af0467 passed (7,484 passed, 0 failed, 0 errors, 198 skipped, 7 xfailed).Push CI (Refactor checks). All nine ordinary jobs passed at 3c88c4c (41291759093), 6da1a07 (41289807919) and 20a5dc4 (41286004910). Each static job ran 222
tests/citests. The conditional slow, coverage and ionic-jv-diagnostics jobs were skipped.Scheduled coverage (A6). Criteria (a)–(d) pass under the agreed composition, per the independent audit CiCoverageV6Addendum1:
The legacy tree and both locks are byte-identical across those commits. No node among the 14,701 planned nodes in 270 shards lacks an outcome.
Reviews. CoverageWatchdogReviewV1 accepted the watchdog with one blocking test-margin finding, and Addenda 1–2 accepted R1 and the test-only R2. The landed bytes equal the reviewed R2 patch. Earlier landings keep their recorded review chains and evidence levels.
Disclosures
Flux-balance stall. The legacy file
tests/integration/test_interface_flux_balance.pyhit the 110-minute stall 2/2 under the old single-process harness at 4c8617b.Run 41286004950 is supplementary and was still in progress. Its 11 root jobs passed. Its failed jobs so far are legacy shards: they are outside the green claim and await the auditor's Addendum2 classification.
C2. The 25 new
tests/cinodes have count-level push evidence. Per-node receipts exist only as supplementary samples: the MeasuredRun3 JUnit, and the 41286004950 coverage-root-static receipt (222/222 passed).C4. These are disclosed, not repaired:
No tolerance, cap, pin, skip or xfail changed. CiCoverageV5's separate historical 99 is a different set and is not added.
Evidence-variable regression. The 91a9323 regression was caught by cancelled run 41227286086, before any acceptance claim, and fixed in 4c8617b.
Ionic J-V allowance. The whole-job allowance remains 128 s, provisional under A-C4.
Legacy suite. The legacy perovskite-sim suite is not part of the green claim.
Not in this PR
Run the supported root tests
The runner derives separate root pytest job and isolation selections and pins BLAS threads to one. The legacy suites, the standalone web tests and build, and the non-pytest CI checks keep their own evidence scope.
The acceptance pack lives in the project archive, outside the repository:
test/ArchitectureRefactor/MilestoneA/AcceptanceV5.mdandAcceptanceV5.json, with every receipt and a verifying hash manifest. Only the user merges.🤖 Generated with Claude Code