fix: lock all-INFRA contract, idempotency tests, tuple units (algo #6)
This commit is contained in:
+10
-3
@@ -809,7 +809,8 @@ class GateSpec:
|
|||||||
target_file: 解析后生效 skill 文件名(候选物化写此文件)。
|
target_file: 解析后生效 skill 文件名(候选物化写此文件)。
|
||||||
candidate_content: 候选 skill 全文。
|
candidate_content: 候选 skill 全文。
|
||||||
base_skill_content: 基线侧生效 skill 全文(skill_hash 作缓存键)。
|
base_skill_content: 基线侧生效 skill 全文(skill_hash 作缓存键)。
|
||||||
units: 阶梯序单元列表(已排除案例单元、截断 gate_n_max)。
|
units: 阶梯序单元元组(已排除案例单元、截断 gate_n_max);元组,装配后
|
||||||
|
不可变,防 spec.units 与 run.slots 漂移。
|
||||||
gate_run_prefix: run_id 前缀,必须含 "_gate_"(防泄露过滤依赖)。
|
gate_run_prefix: run_id 前缀,必须含 "_gate_"(防泄露过滤依赖)。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -817,7 +818,7 @@ class GateSpec:
|
|||||||
target_file: str
|
target_file: str
|
||||||
candidate_content: str
|
candidate_content: str
|
||||||
base_skill_content: str
|
base_skill_content: str
|
||||||
units: list[QuestionUnit]
|
units: tuple[QuestionUnit, ...]
|
||||||
gate_run_prefix: str
|
gate_run_prefix: str
|
||||||
|
|
||||||
|
|
||||||
@@ -884,6 +885,10 @@ def _advance_prefix(run: _GateRun, params: GateParams) -> None:
|
|||||||
瞬间返回、cand 臂必新鲜跑,两臂延迟不对称,若 cand 延迟与对错相关,早到翻转
|
瞬间返回、cand 臂必新鲜跑,两臂延迟不对称,若 cand 延迟与对错相关,早到翻转
|
||||||
对系统性偏向 W 型 → e-值虚高假接受。前缀消费把判定顺序钉回预声明阶梯序,
|
对系统性偏向 W 型 → e-值虚高假接受。前缀消费把判定顺序钉回预声明阶梯序,
|
||||||
anytime-valid 无条件成立;INFRA 单元视为"已解决(剔除)"不阻塞前缀。
|
anytime-valid 无条件成立;INFRA 单元视为"已解决(剔除)"不阻塞前缀。
|
||||||
|
|
||||||
|
契约:全部单元被剔除时 verdict 保持 None、frozen 保持 False,由调度编排层
|
||||||
|
(Task 3 的 validate_skills_concurrent)检测 verdict None 并 raise
|
||||||
|
RuntimeError;本函数不负责该终态。
|
||||||
"""
|
"""
|
||||||
while not run.frozen and run.prefix_ptr < len(run.slots):
|
while not run.frozen and run.prefix_ptr < len(run.slots):
|
||||||
slot = run.slots[run.prefix_ptr]
|
slot = run.slots[run.prefix_ptr]
|
||||||
@@ -903,7 +908,9 @@ def _advance_prefix(run: _GateRun, params: GateParams) -> None:
|
|||||||
run.frozen = True
|
run.frozen = True
|
||||||
continue
|
continue
|
||||||
uid = slot.unit.unit_id
|
uid = slot.unit.unit_id
|
||||||
assert slot.base is not None and slot.cand_per_q is not None
|
assert slot.base is not None and slot.cand_per_q is not None, (
|
||||||
|
f"slot 未配齐即被消费: unit={slot.unit.unit_id}"
|
||||||
|
)
|
||||||
c_units = unit_correctness_view([slot.unit], slot.cand_per_q)
|
c_units = unit_correctness_view([slot.unit], slot.cand_per_q)
|
||||||
pair_result = pair_block({uid: slot.base}, c_units, [uid])
|
pair_result = pair_block({uid: slot.base}, c_units, [uid])
|
||||||
run.candidate_per_q.update(slot.cand_per_q)
|
run.candidate_per_q.update(slot.cand_per_q)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def _mk_run(n_units: int) -> _GateRun:
|
|||||||
target_file="action-reasoning.md",
|
target_file="action-reasoning.md",
|
||||||
candidate_content="cand",
|
candidate_content="cand",
|
||||||
base_skill_content="base",
|
base_skill_content="base",
|
||||||
units=[_mk_unit(f"q{i}") for i in range(n_units)],
|
units=tuple(_mk_unit(f"q{i}") for i in range(n_units)),
|
||||||
gate_run_prefix="r_e1_s0_gate_action-reasoning",
|
gate_run_prefix="r_e1_s0_gate_action-reasoning",
|
||||||
)
|
)
|
||||||
return _GateRun.from_spec(spec)
|
return _GateRun.from_spec(spec)
|
||||||
@@ -119,6 +119,48 @@ def test_infra_unit_skipped_not_counted() -> None:
|
|||||||
assert run.verdict is not None and run.verdict.decision == "accept_provisional"
|
assert run.verdict is not None and run.verdict.decision == "accept_provisional"
|
||||||
|
|
||||||
|
|
||||||
|
def test_all_infra_leaves_verdict_none() -> None:
|
||||||
|
"""全部单元被剔除:verdict 保持 None、frozen 保持 False——终态由调度编排层
|
||||||
|
(Task 3 的 validate_skills_concurrent)检测 verdict None 并 raise,本函数不管。"""
|
||||||
|
run = _mk_run(3)
|
||||||
|
for i in range(3):
|
||||||
|
run.slots[i].base_infra = True
|
||||||
|
run.slots[i].cand_per_q = {f"q{i}": True}
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
assert run.verdict is None and not run.frozen and run.n_excluded == 3 and run.prefix_ptr == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_repeated_calls_are_idempotent() -> None:
|
||||||
|
"""部分前缀消费后重复调用不改变状态;补齐剩余单元后再调用正常推进。"""
|
||||||
|
run = _mk_run(4)
|
||||||
|
for i in (0, 1):
|
||||||
|
run.slots[i].base = False
|
||||||
|
run.slots[i].cand_per_q = {f"q{i}": True}
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
snapshot = (run.n_used, run.w, run.l, run.prefix_ptr, len(run.evidence_rows))
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
assert (run.n_used, run.w, run.l, run.prefix_ptr, len(run.evidence_rows)) == snapshot
|
||||||
|
for i in (2, 3):
|
||||||
|
run.slots[i].base = False
|
||||||
|
run.slots[i].cand_per_q = {f"q{i}": True}
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
assert run.n_used > snapshot[0] and run.prefix_ptr > snapshot[3]
|
||||||
|
|
||||||
|
|
||||||
|
def test_frozen_run_call_is_noop() -> None:
|
||||||
|
"""frozen 后再调用是 no-op:不再消费已配齐单元、证据不再追加。"""
|
||||||
|
run = _mk_run(12)
|
||||||
|
for i in range(12):
|
||||||
|
run.slots[i].base = False
|
||||||
|
run.slots[i].cand_per_q = {f"q{i}": True}
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
assert run.frozen and run.n_used == 7
|
||||||
|
n_evidence = len(run.evidence_rows)
|
||||||
|
_advance_prefix(run, _PARAMS)
|
||||||
|
assert run.n_used == 7 and len(run.evidence_rows) == n_evidence
|
||||||
|
|
||||||
|
|
||||||
def test_ties_hit_futility_early_and_freeze() -> None:
|
def test_ties_hit_futility_early_and_freeze() -> None:
|
||||||
"""全打平(无翻转对)时 futility 出口尽早触发并冻结——早停语义(数值:W=L=0
|
"""全打平(无翻转对)时 futility 出口尽早触发并冻结——早停语义(数值:W=L=0
|
||||||
时乐观 E = E(n_remaining, 0),n 小易 <e_provisional=3)。"""
|
时乐观 E = E(n_remaining, 0),n 小易 <e_provisional=3)。"""
|
||||||
|
|||||||
Reference in New Issue
Block a user