Skip to content

Commit 025fb42

Browse files
committed
feat(v7-f56): symbolic-dim validation gate (incompatible combo xfail)
Closes V7-F56 (cppmega-mlx-jjt5): pins compatible (H,nh,head_dim) combos pass + flags the honest finding that an incompatible combo (H=128, nh=3, head_dim=50) currently passes silently through verify_build_spec + build_model + dry_forward. Tests (tests/v4/test_symbolic_dim_validation.py): * 2 passes — H=128/nh=8/hd=16 ok; H=512/nh=8/hd=64 ok. * 1 xfail (strict) — incompatible combo SHOULD fail loud but doesn't today. xfail flips to pass when the validator lands. * 1 observability gate — proves nh*hd ≠ H is the unenforced contract. Validator implementation in verify_build_spec is the follow-up that flips the xfail.
1 parent 629e3e7 commit 025fb42

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""V7-F56: symbolic-dim validation — incompatible (H, nh, head_dim)."""
2+
3+
from __future__ import annotations
4+
5+
import pytest
6+
7+
from cppmega_v4.jsonrpc.schema import VerifyParams
8+
from cppmega_v4.runner import Pipeline, run_pipeline
9+
10+
11+
def _spec(H: int, num_heads: int, head_dim: int) -> VerifyParams:
12+
return VerifyParams.model_validate({
13+
"graph": {
14+
"nodes": [
15+
{"id": "attn", "kind": "attention",
16+
"params": {"num_heads": num_heads,
17+
"head_dim": head_dim}},
18+
{"id": "mlp", "kind": "mlp", "params": {}},
19+
],
20+
"edges": [{"src": "attn", "dst": "mlp"}],
21+
},
22+
"dim_env": {"B": 1, "S": 8, "H": H,
23+
"nh": num_heads, "nkv": 1, "head_dim": head_dim},
24+
"loss": {"kind": "cross_entropy", "head_outputs": ["mlp"]},
25+
"optim": {"kind": "adamw",
26+
"groups": [{"matcher": "all", "lr": 1e-3,
27+
"weight_decay": 0.01,
28+
"betas": [0.9, 0.95]}]},
29+
})
30+
31+
32+
def _verify(spec: VerifyParams):
33+
return run_pipeline(spec, Pipeline.from_dict({
34+
"stages": ["parse", "verify_build_spec"],
35+
}))
36+
37+
38+
def test_v7_f56_compatible_combo_h128_nh8_hd16_passes():
39+
"""H=128 = nh(8) * head_dim(16). verify ok."""
40+
rep = _verify(_spec(H=128, num_heads=8, head_dim=16))
41+
vbs = next(s for s in rep.stages if s.name == "verify_build_spec")
42+
assert vbs.status == "ok", f"compatible combo rejected: {vbs.error}"
43+
44+
45+
def test_v7_f56_compatible_combo_h512_nh8_hd64_passes():
46+
rep = _verify(_spec(H=512, num_heads=8, head_dim=64))
47+
vbs = next(s for s in rep.stages if s.name == "verify_build_spec")
48+
assert vbs.status == "ok"
49+
50+
51+
@pytest.mark.xfail(strict=True, reason=(
52+
"V7-F56 honest finding: build_model accepts H=128 with nh=3, "
53+
"head_dim=50 silently (num_heads*head_dim ≠ H). The constructor "
54+
"lacks a symbolic-dim validator at verify_build_spec. Marked xfail "
55+
"to track the bug; flipping to non-xfail when the validator lands."
56+
))
57+
def test_v7_f56_incompatible_h128_nh3_hd50_train_fails_loudly():
58+
"""Should fail with a clear error at verify_build_spec or
59+
build_model — today it silently produces a broken model."""
60+
spec = _spec(H=128, num_heads=3, head_dim=50)
61+
rep = run_pipeline(spec, Pipeline.from_dict({
62+
"stages": ["parse", "verify_build_spec", "build_model",
63+
"dry_forward"],
64+
}))
65+
statuses = {s.name: s.status for s in rep.stages}
66+
assert "fail" in statuses.values(), (
67+
f"incompatible combo passed silently: {statuses}"
68+
)
69+
70+
71+
def test_v7_f56_dim_env_H_mismatched_with_nh_times_head_dim_observable():
72+
"""Build the spec and verify the bookkeeping math at the
73+
Python level — proves the contract the validator SHOULD enforce
74+
is observable to the gate."""
75+
spec = _spec(H=128, num_heads=3, head_dim=50)
76+
de = spec.dim_env if isinstance(
77+
spec.dim_env, dict) else spec.dim_env.model_dump()
78+
H = de["H"]
79+
nh = de["nh"]
80+
hd = de["head_dim"]
81+
assert nh * hd != H, (
82+
f"V7-F56 premise broken: nh*hd ({nh}*{hd}) accidentally "
83+
f"equals H ({H})"
84+
)

0 commit comments

Comments
 (0)