feat(s33): wave executor with per-project snapshot and deterministic merge barrier
Replaces run_portfolio's sequential loop with a wave loop over _waves(ids, k):
each wave takes a per-project snapshot of the shared store, runs the wave under
one asyncio.gather in a single event loop, then crosses a merge barrier that
folds each project's NEW verdicts back in wave-submission order. Step 2's
contract goes GREEN; runs stays in project_ids order because gather resolves in
argument order, not completion order.
TWO PLAN CORRECTIONS, both found by the RED-first test rather than by reading:
1. The plan specified sorting the merged verdicts on `project_id`. Measured, that
produces a deterministic order which is the WRONG one: lexicographic gives
BRU/FV42/RV13 while the sequential pass gives FV42/RV13/BRU. It satisfies
"deterministic" while breaking "identical to concurrency=1" — and the second is
the actual contract. The merge preserves submission order instead.
2. The plan named the barrier's sort as the load-bearing seam. It is not — with
per-project snapshots the wave list is never reordered by completion, so a
sorted() there would re-sort an already-ordered list and read as a guard while
guarding nothing. The SNAPSHOT is the half that carries the load. Rather than
ship a decorative sort, both halves were measured (scratchpad-restore, never
git checkout):
detach _wave_snapshot -> RED (store lands in completion order)
detach merge ordering -> RED (reversed wave order diverges)
Both restored byte-identical (sha 42b01d46).
The snapshot carries `retriever` across deliberately: dropping it would silently
downgrade a caller-owned store's S3.1 semantic-retrieval opt-in mid-pass.
Also strengthens the scripted-client consolidation guard, which the probe broke by
being a legitimate third _inner_get_response def-site. It pinned a literal count
of 2 — the wrong shape: it failed on any new legitimate subclass while still
passing if someone pasted a duplicated body into an already-listed file. It now
pins the property (registered sites, scripted-lineage overrides must delegate via
super(), foreign-lineage doubles must genuinely be foreign). Verified load-bearing:
removing both delegation sites turns it RED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQapztREtC2mkr5oU811pr
This commit is contained in:
parent
5d0bb4c981
commit
756b1d5b5c
3 changed files with 207 additions and 51 deletions
|
|
@ -283,3 +283,32 @@ async def test_concurrent_pass_is_byte_identical_to_sequential() -> None:
|
|||
"SC2 (see docstring); a divergence here means something broader than ordering broke"
|
||||
)
|
||||
assert concurrent_ids == sequential_ids
|
||||
|
||||
|
||||
async def test_runs_follow_project_ids_not_completion_order() -> None:
|
||||
"""``PortfolioResult.runs`` is ordered by ``project_ids``, never by which project finished
|
||||
first — so a caller indexing ``runs[0]`` gets the project they listed first, at every ``k``.
|
||||
|
||||
This holds because ``asyncio.gather`` resolves in ARGUMENT order rather than completion order,
|
||||
and waves are built in caller order. It is asserted separately from the store-content contract
|
||||
because it survives a different set of mistakes: a barrier bug reorders the STORE while leaving
|
||||
``runs`` correct, and an executor that appended results as they completed would reorder ``runs``
|
||||
while leaving the store correct. The probe is active, so completion order is confirmed to
|
||||
differ from submission order — without that this assertion would be untested at k>1."""
|
||||
probe = _Recorder()
|
||||
_, result = await _pass(3, probe)
|
||||
|
||||
assert probe.max_in_flight > 1, "the pass ran sequentially — runs ordering is untested at k>1"
|
||||
assert probe.completion_order() != _PORTFOLIO_IDS, (
|
||||
"completion order matched submission order, so this assertion would hold trivially"
|
||||
)
|
||||
# ``claimed_saving_nok`` is the per-project discriminator in REPLIES (200k / 130k / 210k) —
|
||||
# ``measure_type`` is NOT, since FV42 and BRU deliberately share "Reduce scope".
|
||||
assert [r.verdict.proposal_features.claimed_saving_nok for r in result.runs] == [
|
||||
200_000.0,
|
||||
130_000.0,
|
||||
210_000.0,
|
||||
], (
|
||||
f"runs came back in {probe.completion_order()} (completion) rather than "
|
||||
f"{_PORTFOLIO_IDS} (submission) order"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue