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
120 lines
6 KiB
Python
120 lines
6 KiB
Python
"""S2.5 (Step 9) consolidation guard (T-2.5e): the four scripted ``_inner_get_response`` bodies
|
|
collapsed to ONE canonical client (``simulation.ScriptedChatClient``); conftest's three test doubles
|
|
now SUBCLASS it. These grep-guards lock that in — they go RED if a divergent ``_inner_get_response``
|
|
is re-added, if a ``src``→``tests`` import creeps in, or if a double stops subclassing the canonical.
|
|
|
|
The guard is a regression lock over an already-verified consolidation: before the collapse there were
|
|
five ``def _inner_get_response`` sites (four scripted + test_step5's own-lineage double); after, two.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
_ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
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``). 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")
|
|
for p in _py_files(base)
|
|
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")
|
|
]
|
|
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:
|
|
"""No ``src`` module imports from ``tests`` — the canonical lives in ``src/simulation.py`` so
|
|
``conftest`` imports ``src``, never the reverse (the forbidden src→tests direction)."""
|
|
offenders = [
|
|
p.relative_to(_ROOT).as_posix()
|
|
for p in _py_files("src")
|
|
if ("from tests" in (text := p.read_text(encoding="utf-8")) or "import tests" in text)
|
|
]
|
|
assert offenders == [], f"src must not import tests: {offenders}"
|
|
|
|
|
|
def test_conftest_doubles_subclass_canonical() -> None:
|
|
"""conftest's three test doubles genuinely SUBCLASS the canonical ``ScriptedChatClient``
|
|
(delegating the shared body) — so the consolidation is real, not cosmetic."""
|
|
from conftest import (
|
|
ScriptedChatClient,
|
|
SyntheticUsageChatClient,
|
|
_ProjectAwareUsageChatClient,
|
|
_RecordingChatClient,
|
|
)
|
|
|
|
assert issubclass(SyntheticUsageChatClient, ScriptedChatClient)
|
|
assert issubclass(_ProjectAwareUsageChatClient, ScriptedChatClient)
|
|
assert issubclass(_RecordingChatClient, ScriptedChatClient)
|