fix(rectification): close round A/2 tail gaps in tests, CI, and stale-score reuse #47
Reference in New Issue
Block a user
Delete Branch "codex/rectification-tails-20260901"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Round A/2 tail work from
TASK-rectification-tails-20260901.md, based onorigin/staging@26ea3f06(task brief on top of8743dcb1).Hard red lines kept: no change to
deliveryCapability/canAdopt/canConfirmExactMinute/ engine gate semantics; CI checks only added, never removed or relaxed; skill version not bumped.Task A — window_scan
from_sign/to_signtests/test_rectification_diagnostics_clusters.py::test_window_scan_reports_d9_diversity_with_sign_namesexpected{layer, at, user_meaning}only.window_scanalready emitsfrom_sign/to_sign(金牛座→天蝎座); that contract is used by frontend varga contrast. The assertion was updated to include those fields. The engine output was not stripped.Before (
unitteston this test):After:
Full discover after the fix:
PYTHONPATH="$PWD:$PWD/scripts" python -m unittest discover -s tests -p "test_*rectification*.py"→ 79 tests, 0 fail.Task B — why the red test was invisible in CI
.gitea/workflows/backend-quality-gate.ymlpush: branches: [staging](auto)python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime.gitea/workflows/test.ymlworkflow_dispatchonlypython -m pytest.gitea/workflows/ci.ymlworkflow_dispatchonlyThe auto staging gate’s pytest list is
CORE_PYTEST_TARGETSinscripts/run_quality_gate.py. That list hadtests/test_candidate_discriminator_contract.py(BUG-393) but nottests/test_*rectification*.py. The diagnostics assertion could stay red onorigin/stagingindefinitely because the only pipelines that would have seen it never run on push.Fix (add-only): append
"tests/test_rectification_*.py"toCORE_PYTEST_TARGETS. No workflow trigger conditions were changed; no existing target was removed. A pin test (test_staging_quick_gate_runs_rectification_python_suite) keeps the glob in that list.python -m pytest tests/test_rectification_*.pylocally: 137 collected, 0 fail. This PR does not claim a green Gitea run untilbackend-quality-gateactually executes on this SHA, and does not claim staging deployed.Task C — human smoke sheet (not executed)
No staging/production credentials in this session. Do not treat this as pass or fail. Leave the sheet for someone who can open
https://staging.jyotisha.chatafter this SHA is on staging.Executor: unassigned
Environment: not run
Time: not run
display_date_labelcurrent_questionis non-empty or case is adoptable (non-terminal exit)can_adopt=true,can_confirm_exact_minute=falsecandidate_accepted, writesaccepted(not confirmed), entersverify_adopted_time(at most two items)prospective_probesnarrated if presentf83d9b42(if still reachable): GET refresh is adoptable; adopt succeedsalgorithm_version=rectification-v5-matrix-scoring-7,policy_version=rectification-candidate-policy-v3, audit row 「换运贴近度」Any mismatch → new BUG record, no drive-by code change.
Task D — option 1 (chosen)
Choice: option 1. Stale stored
policy_version/algorithm_versionis treated as “must rescore” on the compare-candidates cache skip inscoreAndPersistCurrentEvidence.Why not 2: GET overlay
scoreableSnapshotCurrentFromDossiercopying stored policy onto both sides is a display-staleness signal. It does not force the compare path that actually writes a new result. Adopt is already allowed; the miss is transition-proximity rescoring, which lives on compare.Why not 3: Idle cases would keep v6/v2 scores until the next evidence write. That leaves “换运贴近度” off the card after a policy bump even when the user compares again with unchanged evidence.
Why 1: Smallest change that hits the real skip (
evidenceLedgerFingerprint+candidateRangeFingerprintonly). Live identity is not hardcoded asv3/matrix-scoring-7in TS. Order:RECTIFICATION_DECISION_POLICY_VERSION/RECTIFICATION_ALGORITHM_VERSIONRECTIFICATION_ENGINE_VERSIONonly when it is not the coarse product idrectification-v5GET /api/rectification/v5/versions, which returns the samealgorithm_version/decision_policy_versionfields as/api/rectification/v5/scorewithout scoringCache reuse still uses the existing persist idempotency keys. No silent batch rescore of all cases. Old results keep
invalidated_at/ superseded semantics. Matching fingerprints + matching live identity still skip the engine (existing test updated to mock GET versions).New tests:
matching fingerprints still rescore when stored policy lags the live enginecompare-candidates rescores when stored policy lags the live engineVerification
unittest discover -s tests -p "test_*rectification*.py": 79, fail=0python -m pytest tests/test_rectification_*.py: 137, fail=0cd frontend && ./node_modules/.bin/tsc --noEmit: exit 0tsx --test tests/rectification-*.test.ts tests/skill-registry.test.ts: 756 pass, fail=0 (rectification ≥738; skill-registry 16/16)Not done here: FF-push to
staging, Task C smoke, skill bump.Follow-up — expand pytest globs before argv (
901cdb96)run()usessubprocess.run(..., shell=False), so a literaltests/test_rectification_*.pyin argv is not expanded by a shell. A pin that only asserted the glob string existed could stay green while pytest never collected those files._expand_pytest_targets()now expands targets containing*?[viaROOT.glob, sorted, relative toROOT. Zero matches raiseSystemExit. Ordinary paths are kept as-is. The gate calls:The pin
test_staging_quick_gate_runs_rectification_python_suitenow also imports_expand_pytest_targetsand asserts the glob expands to ≥6 files (zero-match glob raisesSystemExit).CORE_PYTEST_TARGETS rerun (no shell glob)
Invoked as Python argv after
_expand_pytest_targets(CORE_PYTEST_TARGETS)— notpytest tests/test_rectification_*.pyvia zsh.Pull request closed