Skip to content

Commit 04712d4

Browse files
committed
grpc: saturate wire rung before narrowing (codex P2 #626) + repair pre-existing lab-feature breakage
codex P2: DispatchRequest.rung is a proto uint32; 'as u8' truncation made 256 wrap to Surface (the shallowest depth) where the pre-dedup match saturated any out-of-range value to Transcendent. Fixed via rung_from_wire_u32 (u8::try_from().unwrap_or(u8::MAX) -> from_u8) + regression test covering the exact wrap cases (256, 257, u32::MAX). wire.rs is unaffected (its rung field is already u8). Verifying the fix required repairing pre-existing --features grpc breakage (broken on the merge-base too, verified by stash-check): - wire.rs + grpc.rs ShaderDispatch initializers missing the Pillar-7 merge_override / alpha_saturation_override fields -> None defaults (the wire DTO / proto do not carry them). - cypher_bridge.rs tests calling ContextChain::new(8) against the contract's zero-arg new(). Lab-feature suite now green: 186 lib tests incl. the new saturation test; default suite still 100/100. CodeRabbit (2 findings): plan 'Option C' label corrected to 'the dated ADDENDUM strengthening Option B' (the issue defines only A/B). The ISSUES.md rewrite request is declined on-thread: board files are append-only ledgers — corrections append as dated entries, never rewrite prior text. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c9588b2 commit 04712d4

4 files changed

Lines changed: 41 additions & 4 deletions

File tree

.claude/plans/v3-convergence-wiring-v1.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ Recorded in `ISS-Q2-CASCADE3-NIBBLE-ANCESTRY` (board).
110110
(contract 763+, core 925, planner 204, arigraph 124).
111111
- **osint `0x0700` reconciliation:** operator decision; the two-id-spaces
112112
reading (classid space vs concept-vocabulary space, aliasing in the lo u16)
113-
is on `ISS-OSINT-SYSTEM-ROOT-SLOT-VIOLATION` as Option C.
113+
is on `ISS-OSINT-SYSTEM-ROOT-SLOT-VIOLATION` as the dated ADDENDUM
114+
strengthening its Option B (the issue defines Options A/B; no separate
115+
option was minted).
114116
- **classid human-readable reorder (`0x07:01::1000`):** DEFERRED-by-design,
115117
hands off until post-V3, implemented as the one flippable split-order.
116118

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,15 @@ mod tests {
251251
/// TD-INT-6 — empty candidate list escalates.
252252
#[test]
253253
fn disambiguate_empty_candidates_escalates() {
254-
let chain = ContextChain::new(8);
254+
let chain = ContextChain::new();
255255
let result = disambiguate_parse_candidates(&chain, 0, Vec::new());
256256
assert!(result.is_err(), "empty candidates must escalate");
257257
}
258258

259259
/// TD-INT-6 — single candidate escalates (margin = 0).
260260
#[test]
261261
fn disambiguate_single_candidate_escalates() {
262-
let chain = ContextChain::new(8);
262+
let chain = ContextChain::new();
263263
let cand = CrystalFingerprint::Binary16K(Box::new([0u64; 256]));
264264
let result = disambiguate_parse_candidates(&chain, 0, vec![cand]);
265265
assert!(result.is_err(), "single candidate must escalate");

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ fn proto_to_dispatch(req: &pb::DispatchRequest) -> ShaderDispatch {
346346
})
347347
.unwrap_or(StyleSelector::Auto);
348348

349-
let rung = RungLevel::from_u8(req.rung as u8);
349+
let rung = rung_from_wire_u32(req.rung);
350350

351351
let emit = match pb::EmitMode::try_from(req.emit) {
352352
Ok(pb::EmitMode::Bundle) => EmitMode::Bundle,
@@ -376,6 +376,10 @@ fn proto_to_dispatch(req: &pb::DispatchRequest) -> ShaderDispatch {
376376
max_cycles: req.max_cycles as u16,
377377
entropy_floor: req.entropy_floor,
378378
emit,
379+
// Pillar-7 knobs: the proto does not carry them; keep the documented
380+
// defaults (no sink-stage merge override).
381+
merge_override: None,
382+
alpha_saturation_override: None,
379383
}
380384
}
381385

@@ -398,3 +402,30 @@ fn unified_styles_proto() -> Vec<pb::StyleInfo> {
398402
})
399403
.collect()
400404
}
405+
406+
/// Decode the proto `uint32` rung, saturating BEFORE narrowing: an
407+
/// out-of-range client value clamps to `Transcendent` (the pre-dedup match's
408+
/// `_ =>` arm), never wraps (`256 as u8 == 0` would silently run the request
409+
/// at `Surface`, the shallowest depth — codex P2 on #626).
410+
fn rung_from_wire_u32(v: u32) -> RungLevel {
411+
RungLevel::from_u8(u8::try_from(v).unwrap_or(u8::MAX))
412+
}
413+
414+
#[cfg(test)]
415+
mod rung_wire_tests {
416+
use super::*;
417+
418+
#[test]
419+
fn wire_rung_saturates_never_wraps() {
420+
// In-range ordinals round-trip.
421+
assert_eq!(rung_from_wire_u32(0), RungLevel::Surface);
422+
assert_eq!(rung_from_wire_u32(6), RungLevel::Counterfactual);
423+
assert_eq!(rung_from_wire_u32(9), RungLevel::Transcendent);
424+
// Out-of-range clamps to the SAFEST maximum depth — the codex-P2
425+
// wrap cases: 256 must NOT become Surface, 257 must NOT become Shallow.
426+
assert_eq!(rung_from_wire_u32(10), RungLevel::Transcendent);
427+
assert_eq!(rung_from_wire_u32(256), RungLevel::Transcendent);
428+
assert_eq!(rung_from_wire_u32(257), RungLevel::Transcendent);
429+
assert_eq!(rung_from_wire_u32(u32::MAX), RungLevel::Transcendent);
430+
}
431+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,10 @@ impl WireDispatch {
946946
max_cycles: self.max_cycles,
947947
entropy_floor: self.entropy_floor,
948948
emit,
949+
// Pillar-7 knobs: the wire DTO does not carry them; keep the
950+
// documented defaults (no sink-stage merge override).
951+
merge_override: None,
952+
alpha_saturation_override: None,
949953
}
950954
}
951955
}

0 commit comments

Comments
 (0)