fix(control-plane): bind semantic replans to turn guards - #3840
Conversation
Signed-off-by: huangruiteng <[email protected]>
e90c15c to
4d61b72
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
No unresolved findings on the refined head 4d61b7251.
Resolved during review:
- The original implementation correctly identified the causal race between Turn admission and later replan derivation, but it treated a legacy receipt with no semantic-replan field the same as a new guard that explicitly selected no replan. That could weaken an in-flight legacy Turn's strict gate.
- Receipt interpretation now lives in the existing TypeScript settlement-readback boundary as a typed three-state contract: legacy/unscoped, guard-scoped with no selected replan, or guard-scoped to one exact obligation id. Python consumes that projection; malformed persisted ids fail closed.
- Added end-to-end coverage for both sides of the race: an obligation created after admission is deferred, while a guard-selected obligation remains strict. Added legacy-receipt and TS contract coverage as well.
Product/architecture judgment: the change is valuable and appropriately scoped. A Turn should be judged against the semantic obligations admitted by its own guard, not against a newer projection computed during writeback. This prevents valid work from being retroactively rejected without allowing old or malformed receipts to bypass current safety. The TS companion refactor is at the existing receipt-decoding owner and avoids a new leaf runtime handler or a second Python source of truth.
Provenance: the PR had no linked issue, review discussion, or originating task/thread, so I did not rely on its original validation narrative. I rebased it onto current main and independently reconstructed the invariant from the live settlement and replan paths.
Validation:
- focused Python control-plane suite: 215 passed;
- complete TypeScript control-plane suite: 453 passed, 1 PostgreSQL integration skipped because
LOOPX_TEST_POSTGRES_URLis not configured; - TypeScript typecheck, changed Python compile, Ruff, and diff hygiene: passed;
- risk-based premerge: 17/17 selected canaries passed, with no failures, warnings, or manual holds;
- GitHub checks: pytest, Windows PowerShell, DCO, dependency review, build, and both Sonar checks passed;
- public/private boundary scan: clean.
Merge decision: self-merge is justified on this exact head under the owner's explicit authorization.
Summary
Refined design
The original change fixed a real causal race, but did not distinguish an old receipt with no semantic-replan field from a new guard that explicitly selected no replan. Treating both as “no replan” would silently weaken the gate for an in-flight legacy Turn.
The refined implementation makes receipt interpretation a TypeScript-owned three-state contract:
legacy_unscoped: the receipt predates the field, so current strict behavior is preserved;turn_guardwith no selected obligation: a replan created after admission is deferred to the next Turn;turn_guardwith an exact obligation id: that generation remains strict.Python now consumes this typed readback instead of reinterpreting raw receipt shape. Malformed persisted ids fail closed.
Independent review / provenance
No linked issue, review discussion, or originating task/thread was present on the PR. The branch and commit are owner-authored, but the exact generation context could not be reconstructed. The change was therefore rebased onto current
mainand reviewed from the current control-plane invariants rather than relying on its original description or local validation claims.Validation
LOOPX_PYTHON="$PWD/.venv/bin/python" .venv/bin/python -m pytest -q tests/capabilities/test_benchmark_toolkit.py tests/control_plane/test_refresh_state_replan_gate.py tests/control_plane/test_quota_settlement.py tests/control_plane/test_quota_settlement_cli.py— 215 passednpm run test:control-plane— 453 passed, 1 skipped (LOOPX_TEST_POSTGRES_URLnot configured; unrelated PostgreSQL integration)npm run typecheck:control-plane— passedloopx canary premerge --from-git-diff --git-diff-base origin/main --format json --no-progress— passed; 17/17 selected canaries, no failures, warnings, or manual holdsMerge decision
The final diff is cohesive, current-main based, backward-compatible, and covered across the causal race, exact-generation strictness, legacy receipts, malformed state, replay, and the broader quota/settlement surface. Owner authorization for self-review/refine/merge was provided in the current task.