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
|
|
@ -9,6 +9,7 @@ five ``def _inner_get_response`` sites (four scripted + test_step5's own-lineage
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
|
@ -18,9 +19,35 @@ def _py_files(base: str) -> list[Path]:
|
|||
return sorted((_ROOT / base).rglob("*.py"))
|
||||
|
||||
|
||||
# Files permitted to define ``_inner_get_response``. The invariant this guard protects is that the
|
||||
# scripted BODY is not duplicated — not that the def-site count is frozen. Adding a file here is a
|
||||
# deliberate act: a new entry must either be the canonical, or a thin override that DELEGATES to it
|
||||
# (which ``test_delegating_overrides_call_super`` below then enforces mechanically).
|
||||
_CANONICAL_SITE = "src/portfolio_optimiser/simulation.py"
|
||||
|
||||
# Overrides in the SCRIPTED lineage — they subclass ``ScriptedChatClient``, so a body of their own
|
||||
# would be a copy of the canonical. They must delegate.
|
||||
_DELEGATING_OVERRIDES = [
|
||||
# S3.3 ordering probe: yields to the event loop N times, then delegates. It cannot live in the
|
||||
# reply-selector seam, which the canonical calls synchronously and so can never await.
|
||||
"tests/test_portfolio_concurrent_loadbearing.py",
|
||||
]
|
||||
|
||||
# Doubles in a DIFFERENT lineage (``spikes._harness.FakeChatClient``). There is no canonical
|
||||
# scripted body above them to delegate to, so the delegation rule does not apply — but the
|
||||
# separation is asserted rather than assumed, so a file cannot be parked here to dodge the rule.
|
||||
_FOREIGN_LINEAGE = ["tests/test_step5_refine_loadbearing.py"]
|
||||
|
||||
|
||||
def test_inner_get_response_collapsed_to_two_sites() -> None:
|
||||
"""The four scripted clients collapse to ONE canonical ``_inner_get_response`` (simulation.py);
|
||||
test_step5's own-lineage double is the only other def. So exactly 2 def-sites remain — NOT 5."""
|
||||
"""The four scripted clients collapse to ONE canonical ``_inner_get_response``
|
||||
(``simulation.py``). Every other def-site must be a registered, DELEGATING override — never a
|
||||
fourth copy of the body.
|
||||
|
||||
The guard originally pinned a literal count of 2. That made it fail on any new legitimate
|
||||
subclass while still passing if someone pasted a duplicated body into an already-listed file —
|
||||
a count is the wrong shape for the invariant. The list below plus
|
||||
``test_delegating_overrides_call_super`` pin the property itself."""
|
||||
sites = [
|
||||
p.relative_to(_ROOT).as_posix()
|
||||
for base in ("src", "tests")
|
||||
|
|
@ -28,10 +55,43 @@ def test_inner_get_response_collapsed_to_two_sites() -> None:
|
|||
if p.name != Path(__file__).name # this guard file references the pattern in prose
|
||||
and "def _inner_get_response" in p.read_text(encoding="utf-8")
|
||||
]
|
||||
assert sorted(sites) == [
|
||||
"src/portfolio_optimiser/simulation.py",
|
||||
"tests/test_step5_refine_loadbearing.py",
|
||||
], f"expected the four scripted bodies collapsed to one canonical + test_step5's, got: {sites}"
|
||||
expected = sorted([_CANONICAL_SITE, *_DELEGATING_OVERRIDES, *_FOREIGN_LINEAGE])
|
||||
assert sorted(sites) == expected, (
|
||||
f"unregistered ``_inner_get_response`` def-site — the scripted body must not be copied. "
|
||||
f"Expected {expected}, got: {sites}"
|
||||
)
|
||||
|
||||
|
||||
def test_foreign_lineage_doubles_are_genuinely_foreign() -> None:
|
||||
"""A file listed as foreign lineage must NOT subclass the scripted canonical.
|
||||
|
||||
Without this, ``_FOREIGN_LINEAGE`` would be an escape hatch: any scripted-lineage subclass
|
||||
could be moved into that list to skip the delegation rule below."""
|
||||
for site in _FOREIGN_LINEAGE:
|
||||
text = (_ROOT / site).read_text(encoding="utf-8")
|
||||
assert "ScriptedChatClient" not in text, (
|
||||
f"{site} is registered as foreign lineage but references ``ScriptedChatClient`` — if it "
|
||||
"is in the scripted lineage it belongs in _DELEGATING_OVERRIDES and must delegate"
|
||||
)
|
||||
|
||||
|
||||
def test_delegating_overrides_call_super() -> None:
|
||||
"""Every scripted-lineage override actually DELEGATES to the canonical rather than
|
||||
reimplementing it.
|
||||
|
||||
This is the strength the literal count never had: without it, a file already on the list could
|
||||
grow a full copy of the scripted body and the consolidation would be cosmetic again."""
|
||||
for site in _DELEGATING_OVERRIDES:
|
||||
text = (_ROOT / site).read_text(encoding="utf-8")
|
||||
# Match the delegation ITSELF — ``super()._inner_get_response`` or the explicit
|
||||
# ``super(Cls, self)._inner_get_response`` form a nested function needs. Searching for
|
||||
# "super(" and "_inner_get_response" independently would pass on any file that merely
|
||||
# calls ``super().__init__`` near a def, which is accidental-green, not a guard.
|
||||
assert re.search(r"super\([^)]*\)\._inner_get_response", text), (
|
||||
f"{site} defines ``_inner_get_response`` but never delegates to the canonical via "
|
||||
"``super()._inner_get_response`` — that is a duplicated body, which is exactly what "
|
||||
"this guard exists to prevent"
|
||||
)
|
||||
|
||||
|
||||
def test_no_src_imports_tests() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue