Skip to content

Commit f33e41d

Browse files
committed
D3.2 client driver + starter YAML configs (118/118 tests, +1 spec-drift guard)
Final Phase 3 scaffold deliverable — curl-driven lab iteration against the shipped /v1/shader/sweep endpoint. Files: configs/codec/README.md — inventory + DoS-ceiling note + anti-#219 stub:true flag explanation configs/codec/00_pr220_baseline.yaml - PR #220 baseline regression: 6 subspaces × 256 centroids × identity rotation. Expected ICC ≈ 0.195 mean when D2.2 lands real decode-and-compare. configs/codec/10_wider_codebook.yaml - PR #220 fix (a): centroids ∈ {256, 512, 1024}. Cardinality 3, three distinct kernel signatures → warm cache after one pass. configs/codec/12_hadamard_pre_rotation.yaml - PR #220 fix (c): Hadamard × centroids cross-product (2×2 = 4). Hadamard stays Tier-3 F32x16 per Rule C. scripts/codec_sweep.sh - yq YAML → JSON conversion - POST to ${SHADER_LAB_URL}/v1/shader/sweep (default localhost:3001) - jq-pretty request + response - Stub honesty check: prints results[0].stub flag → verifies Phase 0 returns true (machine-checkable anti-#219) - Requires: yq (mikefarah/yq ≥ v4), curl, jq wire.rs +1 test: sweep_request_yaml_shape_deserializes_via_serde_json - Inline JSON fixture mirroring the canonical YAML → JSON shape - If this test breaks, the YAML configs are stale relative to the Rust DTOs → scripts/codec_sweep.sh would fail at runtime - Caught a real drift during development: PascalCase "Identity" vs the DTO's rename_all="lowercase" (YAMLs correctly use lowercase; test fixture had the typo) Phase state: Phase 0 ✅ complete Phase 1 scaffold ✅ (D1.1 / D1.2 / D1.3 shipped; D1.1b queued) Phase 2 scaffold ✅ (D2.1 harness + D2.3 handler; D2.2 queued) Phase 3 scaffold ✅ — D3.1 batch handler + D3.2 client driver shipped ⏳ D3.1b real Lance append writer queued DoS-ceiling note: sweep handler rejects grids with cardinality > 10_000 before enumeration (PR #238 P1 fix). README documents the ceiling so config authors can budget axis lengths. Rule D honored: adding a new codec candidate = authoring a new YAML file in configs/codec/. Zero Rust changes. Zero rebuilds. Rules F honored at the client boundary: YAML → JSON → HTTP ingress. Single deserialisation at the shader-lab's handler; everything after is in-memory Rust (WireSweepRequest → CodecParams → grid enumerate() → per-candidate WireSweepResult). https://claude.ai/code/session_01SbYsmmbPf9YQuYbHZN52Zh
1 parent 8faf5a7 commit f33e41d

7 files changed

Lines changed: 199 additions & 1 deletion

File tree

.claude/board/STATUS_BOARD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ afterwards is a JIT kernel, not a rebuild. Plan path:
7979
| D-id | Title | Status | PR / Evidence |
8080
|---|---|---|---|
8181
| D3.1 | Server-side sweep handler + Lance fragment append | **In PR** | branch — `sweep_handler` batch mode: enumerates `WireSweepGrid::enumerate()`, validates each via TryFrom(CodecParams) at ingress, returns `WireSweepResponse { results: [WireSweepResult { kernel_hash, stub:true }], cardinality, elapsed_ms }`. SSE streaming + real calibrate/token-agreement per point deferred to D3.1b. Route: `POST /v1/shader/sweep`. |
82-
| D3.2 | Client-side driver + config files | **Queued** | target ~20 LOC + YAML configs |
82+
| D3.2 | Client-side driver + config files | **In PR** | branch — 3 starter YAML configs (`configs/codec/{00_pr220_baseline, 10_wider_codebook, 12_hadamard_pre_rotation}.yaml`), `scripts/codec_sweep.sh` curl wrapper, `configs/codec/README.md`, YAML-shape spec-drift guard test. 118/118 tests pass. |
8383

8484
### Phase 4 — Frontier analysis — Queued
8585

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Phase 0 baseline — single candidate with defaults.
2+
# Reproduces PR #220's setup (6 subspaces × 256 centroids × identity rotation)
3+
# for regression testing. Expected: ICC ≈ 0.195 mean when D2.2 lands real
4+
# decode-and-compare; Phase 0 stub returns top1_rate = 0.0 + stub:true.
5+
name: pr220_baseline
6+
tensor_path: models/qwen3-tts-0.6b/q_proj.safetensors
7+
grid:
8+
subspaces: [6]
9+
centroids: [256]
10+
residual_depths: [0]
11+
rotations:
12+
- { kind: identity }
13+
distances: [AdcU8]
14+
lane_widths: [F32x16]
15+
residual_centroids: 256
16+
calibration_rows: 2048
17+
measurement_rows: 512
18+
seed: 42
19+
measure:
20+
- reconstruction_icc_held_out
21+
- token_agreement_top1
22+
label: "pr220 baseline regression"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# PR #220 fix (a) — wider codebook (1024+ centroids per subspace).
2+
# Tests the first remedy from the PR #220 "What's Needed to Fix" list.
3+
# The JIT cache warms once per unique (subspaces, centroids, …) tuple;
4+
# this grid generates 1 × 3 × 1 × 1 × 1 × 1 = 3 distinct signatures.
5+
name: wider_codebook_sweep
6+
tensor_path: models/qwen3-tts-0.6b/gate_proj.safetensors
7+
grid:
8+
subspaces: [6]
9+
centroids: [256, 512, 1024]
10+
residual_depths: [0]
11+
rotations:
12+
- { kind: identity }
13+
distances: [AdcU8]
14+
lane_widths: [F32x16]
15+
residual_centroids: 256
16+
calibration_rows: 2048
17+
measurement_rows: 512
18+
seed: 42
19+
measure:
20+
- reconstruction_icc_held_out
21+
- token_agreement_top1
22+
- token_agreement_top5
23+
label: "PR #220 fix (a): wider codebook"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PR #220 fix (c) — Hadamard pre-rotation.
2+
# Hadamard stays at Tier-3 F32x16 per Rule C (add/sub butterfly, not matmul
3+
# → no AMX benefit). Sylvester construction requires dim power-of-two.
4+
# Combined with wider codebook to probe the compound effect.
5+
name: hadamard_pre_rotation
6+
tensor_path: models/qwen3-tts-0.6b/o_proj.safetensors
7+
grid:
8+
subspaces: [6]
9+
centroids: [256, 512]
10+
residual_depths: [0]
11+
rotations:
12+
- { kind: identity }
13+
- { kind: hadamard, dim: 4096 }
14+
distances: [AdcU8]
15+
lane_widths: [F32x16]
16+
residual_centroids: 256
17+
calibration_rows: 2048
18+
measurement_rows: 512
19+
seed: 42
20+
measure:
21+
- reconstruction_icc_held_out
22+
- token_agreement_top1
23+
- token_agreement_top5
24+
label: "PR #220 fix (c): Hadamard pre-rotation × centroids sweep"

configs/codec/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Codec Sweep YAML Configs
2+
3+
Request bodies for the lab REST surface (`/v1/shader/sweep`,
4+
`/v1/shader/calibrate`, `/v1/shader/token-agreement`). Consumed by
5+
`scripts/codec_sweep.sh` — see that script for the curl invocation.
6+
7+
**Rule D enforcement:** adding a new codec candidate is authoring a new
8+
YAML file in this directory. Zero Rust changes. Zero rebuilds. The
9+
running `shader-lab` binary JIT-compiles each unique `CodecParams`
10+
signature; overlapping signatures across YAMLs hit the kernel cache.
11+
12+
## Inventory
13+
14+
| File | Target | What it tests |
15+
|------|--------|---------------|
16+
| `00_pr220_baseline.yaml` | q_proj | PR #220 baseline regression (6×256, identity rotation) |
17+
| `10_wider_codebook.yaml` | gate_proj | PR #220 fix (a) — centroids ∈ {256, 512, 1024} |
18+
| `12_hadamard_pre_rotation.yaml` | o_proj | PR #220 fix (c) — Hadamard × centroids |
19+
20+
Additional configs from plan Appendix A (residual PQ, OPQ, composite,
21+
CartanCascade 4-tier) land as separate YAML files as Phase 1b Cranelift
22+
wiring makes the real decode paths runnable.
23+
24+
## Expected results under Phase 0/2 stubs
25+
26+
Every candidate returns `stub: true` + `backend: "stub"` in the response
27+
until D2.2 (real decode-and-compare) lands. Clients that trust these
28+
rates as real measurements hit the machine-checkable `stub` wall — that's
29+
the anti-#219 defense at the type level (see `EPIPHANIES.md` 2026-04-20
30+
"D0.2 stub flag is anti-#219 defense at the type level").
31+
32+
## DoS ceiling
33+
34+
The sweep handler rejects grids with cardinality > 10,000 before
35+
enumeration. Multiply axis lengths to budget — e.g., `3 × 3 × 3 × 3 = 81`
36+
is fine; `100 × 100 = 10,000` is the exact ceiling.

crates/cognitive-shader-driver/src/wire.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,38 @@ mod tests {
15841584
assert_eq!(decoded.label, "phase1_initial_cross_product");
15851585
}
15861586

1587+
#[test]
1588+
fn sweep_request_yaml_shape_deserializes_via_serde_json() {
1589+
// Documents the canonical JSON shape (after yq conversion from YAML)
1590+
// for `configs/codec/*.yaml`. If this test breaks, the checked-in
1591+
// YAML configs are stale relative to the Rust DTOs — the
1592+
// scripts/codec_sweep.sh pipeline will fail at runtime.
1593+
let body = r#"{
1594+
"tensor_path": "models/qwen3-tts-0.6b/q_proj.safetensors",
1595+
"grid": {
1596+
"subspaces": [6],
1597+
"centroids": [256, 512, 1024],
1598+
"residual_depths": [0],
1599+
"rotations": [
1600+
{ "kind": "identity" },
1601+
{ "kind": "hadamard", "dim": 4096 }
1602+
],
1603+
"distances": ["AdcU8"],
1604+
"lane_widths": ["F32x16"],
1605+
"residual_centroids": 256,
1606+
"calibration_rows": 2048,
1607+
"measurement_rows": 512,
1608+
"seed": 42
1609+
},
1610+
"measure": ["reconstruction_icc_held_out", "token_agreement_top1"],
1611+
"label": "spec-drift guard"
1612+
}"#;
1613+
let req: WireSweepRequest = serde_json::from_str(body).expect("YAML→JSON shape parses");
1614+
// 1 × 3 × 1 × 2 × 1 × 1 = 6 candidates.
1615+
assert_eq!(req.grid.cardinality(), 6);
1616+
assert_eq!(req.measure.len(), 2);
1617+
}
1618+
15871619
#[test]
15881620
fn sweep_measure_serializes_snake_case() {
15891621
let m = WireMeasure::ReconstructionIccHeldOut;

scripts/codec_sweep.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env bash
2+
#
3+
# codec_sweep.sh — curl-driven lab iteration for codec sweeps.
4+
#
5+
# Reads a YAML config under configs/codec/, converts to JSON via yq, POSTs
6+
# to the running shader-lab's /v1/shader/sweep endpoint, pretty-prints the
7+
# WireSweepResponse.
8+
#
9+
# Prereqs: yq (mikefarah/yq ≥ v4), curl, jq, a running shader-lab binary
10+
# on SHADER_LAB_URL (default http://localhost:3001).
11+
#
12+
# Usage:
13+
# scripts/codec_sweep.sh configs/codec/00_pr220_baseline.yaml
14+
# SHADER_LAB_URL=http://10.0.0.5:3001 scripts/codec_sweep.sh configs/codec/10_wider_codebook.yaml
15+
#
16+
# Output: JSON WireSweepResponse with per-grid-point stub results (until
17+
# D2.2 lands real decode-and-compare). Every result row has stub:true.
18+
19+
set -euo pipefail
20+
21+
SHADER_LAB_URL="${SHADER_LAB_URL:-http://localhost:3001}"
22+
ENDPOINT="${SHADER_LAB_URL}/v1/shader/sweep"
23+
24+
if [[ $# -lt 1 ]]; then
25+
echo "usage: $0 <yaml-config>"
26+
echo " example: $0 configs/codec/00_pr220_baseline.yaml"
27+
exit 2
28+
fi
29+
30+
CONFIG="$1"
31+
32+
if [[ ! -f "$CONFIG" ]]; then
33+
echo "config not found: $CONFIG" >&2
34+
exit 2
35+
fi
36+
37+
if ! command -v yq >/dev/null 2>&1; then
38+
echo "yq not installed — install mikefarah/yq (https://github.com/mikefarah/yq)" >&2
39+
exit 2
40+
fi
41+
42+
# Convert YAML → JSON; POST to endpoint; pretty-print response.
43+
json_body=$(yq -o=json '.' "$CONFIG")
44+
45+
echo "=== POST $ENDPOINT ==="
46+
echo "--- request ---"
47+
echo "$json_body" | jq '.'
48+
echo
49+
echo "--- response ---"
50+
51+
response=$(curl -sS -X POST "$ENDPOINT" \
52+
-H "Content-Type: application/json" \
53+
-d "$json_body")
54+
55+
echo "$response" | jq '.'
56+
57+
echo
58+
echo "=== Stub honesty check ==="
59+
stub_flag=$(echo "$response" | jq '.results[0].stub // "no results"')
60+
echo "results[0].stub = $stub_flag"
61+
echo "Expected: true (Phase 0 stub; D2.2 flips to false when real decode lands)."

0 commit comments

Comments
 (0)