feat(s33): wave-boundary goal-stop semantics + single-loop and one-writer guards
Documents the wave-model reading of the existing Step-8 goal semantics (a
reading, not a redesign — Session 1 already moved the checks to wave assembly
because the executor required it) and pins it with three tests.
Goal checks run at WAVE ASSEMBLY, per member: a HARD per-project goal removes
the pid before the wave starts, so an excluded project is never STARTED and
leaves no verdict behind; a HARD portfolio goal stops the pass with the
assembled wave still crossing its barrier; SOFT flags and continues.
Detach points measured:
per-project goal keyed per WAVE, not per member -> RED (membership diverges
at k=3, unaffected at k=1)
import threading under src/ -> RED (AST guard)
promote_verdict called on the run path -> RED (tripwire)
Corrects one claim the plan and my first docstring both implied: checking the
goal MID-WAVE does NOT make membership completion-order dependent.
_goal_limit_if_reached reads only the ledger, contract and baseline, all
invariant during a pass, so a later check reaches the same decision.
Determinism of membership is the ONE-WRITER rule's (C3, now a tripwire test),
not the check's placement; placement buys the never-started property. The
docstrings say this rather than claiming a detach point that does not exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vYbqW4MppACRvPhEMDpvF
This commit is contained in:
parent
dd15e33556
commit
796f8d3af0
2 changed files with 198 additions and 1 deletions
|
|
@ -690,7 +690,19 @@ async def run_portfolio(
|
|||
every ``k``, including ``k=1``. The policy is carried by ``return_exceptions=True`` on the
|
||||
wave's ``gather``; ``asyncio.TaskGroup`` would cancel the siblings and is rejected for that
|
||||
reason. Note the pass still raises for errors that are NOT one project's failure — an unknown
|
||||
``project_id`` and a non-positive ``concurrency`` are caller mistakes and fail fast."""
|
||||
``project_id`` and a non-positive ``concurrency`` are caller mistakes and fail fast.
|
||||
|
||||
Goal-stop under waves (S3.3 reading of the Step-8 semantics above — a reading, NOT a redesign):
|
||||
the checks run at WAVE ASSEMBLY, per member, before the wave starts. A HARD per-project goal
|
||||
removes that pid from its wave, so an excluded project is never STARTED — it costs no model
|
||||
round-trip and leaves no verdict behind, which a post-hoc filter over completed runs could not
|
||||
achieve. A HARD portfolio goal stops the pass, and the wave already assembled still crosses its
|
||||
merge barrier before the loop exits, so a stop never strands verdicts outside the store. A SOFT
|
||||
goal flags ``stop_reason`` and continues. Membership is identical at every ``concurrency`` — but
|
||||
the reason is that the ledger is STATIC during a pass (C3: no realization happens on the run
|
||||
path, which ``test_concurrent_pass_does_not_write_on_the_run_path`` pins), so every check reads
|
||||
the same accumulated sum regardless of when it runs. Wave-assembly placement buys the
|
||||
never-started property, not determinism; determinism is the one-writer rule's."""
|
||||
if concurrency < 1:
|
||||
raise ValueError(
|
||||
f"concurrency must be >= 1, got {concurrency}: a non-positive wave size would run no "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue