feat(wire): T27-first partial flip — specs/wire.t27 becomes SSOT - #33
Conversation
Constants (VERSION, KIND_HELLO, KIND_DATA, HEADER_LEN) and pure predicates (frame_kind_valid, header_byte, parse_accepts) are now auto-generated into gen/rust/wire.rs via t27c gen-rust. src/wire.rs re-exports them and keeps only the ergonomic Rust wrappers (Header struct, FrameKind enum, to_bytes, parse) on top. Partial rather than full flip: t27c-0.1.0 has a bit-shift parser bug that emits 'return ();' for expressions like ((w >> 24) & 255) as u8, so be_byte and u32_be stay hand-written inside gen/rust/wire.rs (below a documented banner) instead of coming from the compiler. Bug reproduces on gen-rust and gen (Zig) both — it is in the shared frontend, not the emitter. To be filed against gHashTag/t27. build.rs invokes t27c only when T27C_REGENERATE=1 is set, so CI without t27c installed still builds. gen/rust/ is committed as a deterministic build output. Guardrails added: - t27_gen_constants_match_hand_written — pins auto-gen constant values. - t27_gen_predicates_match_semantics — exercises the generated predicates. Verified green: - cargo test --lib: 101/0 - cargo test --test m2_routing_pure_logic: 25/0 - cargo fmt --all -- --check: clean Docs: - docs/T27_PORT_STATUS.md — wire.rs row flipped to 'T27-FIRST (partial)'. - docs/T27_FIRST_MIGRATION.md — new; documents the flip, the bootstrap limitation, the build story, and the regeneration recipe. Anchor: phi^2 + phi^-2 = 3
…hift Post-merge triage on the t27c bootstrap isolated the real trigger. The bug is not in bit-shift parsing; it is missing ExprCast lowering in the Rust, Zig, and C text emitters. The frontend builds the AST node correctly, but expr_to_rust and its Zig/C peers have no arm for NodeKind::ExprCast and fall through their default arm to the unit value '()'. gen-verilog is the only backend that implements ExprCast. Isolation table (all against current master t27c): w >> 24 -> OK (w >> 24) & 255 -> OK (w >> 24) as u8 -> () <-- cast triggers it (w & 255) as u8 -> () <-- no shift, same failure 42 as u32 -> () x as u32 -> () ((a as u32) << 8) | ... -> ((() << 8) | ()) Filed as gHashTag/t27#1314 with the isolation matrix and the compiler.rs line reference. Practical impact on this PR: unchanged. be_byte and u32_be still cannot come from t27c because both use 'as u8' / 'as u32'. Hand-written stubs stay beneath the banner in gen/rust/wire.rs until t27c ships an ExprCast arm in expr_to_rust. This commit only fixes the wording in the banner and the migration doc so the tracking issue is accurate. Anchor: phi^2 + phi^-2 = 3
|
Post-merge triage note before human-merge: upstream root cause is precisely identified and filed as gHashTag/t27#1314. The bug is missing Follow-up commit 880954e updates the banner in
Once t27c#1314 lands, follow-up PR: strip the hand-stub band from Anchor: phi^2 + phi^-2 = 3 |
|
Upstream unblock landed: gHashTag/t27#1320 — Merge order once approved:
Zig + C emitters remain follow-up under t27#1314 (Rust arm in #1320 is the template). tri-net does not consume them today so this does not block the flip. Anchor: phi^2 + phi^-2 = 3 |
…20 + gen/ untouchable rule - Two remaining 'bit-shift' references (build.rs note, Next flips) corrected to ExprCast lowering (t27#1314 → fixed by t27#1320). The Bootstrap-limitation section was already corrected in 880954e; these two were missed. - Regeneration recipe updated to reflect pre-validated reality: post-#1320 regen overwrites the whole gen file (~49 removed / ~40 added), NOT just the stub band. Tests stay 101/0 + 25/0. Bigger diff is expected/correct (removes obsolete ExprCast banner + hand-stubs, normalises cosmetic rendering). - New 'Rule: gen/ is untouchable raw output' section: gen/<lang>/* is never hand-edited; explanatory banners/comments go in the consumer (src/) or this doc. If t27c emits wrong, fix t27c upstream, don't patch gen/. Captures the diff-shape lesson so future flips (routing, gf16) don't repeat the trap.
…f '()' (Closes #1314) Add Expr::Cast arm in expr_to_rust (bootstrap/src/compiler.rs), mirroring the gen-verilog arm at compiler.rs:4941. Before this fix, ExprCast fell through to the default '_ => "()".to_string()' branch and generated empty-tuple stubs in Rust output, silently corrupting any T27 spec using bit-width casts. Also update docs/NOW.md with an exprcast-rust-emitter entry (required by the NOW Sync Gate). Closes #1314 (gen-rust half; gen-c and gen-zig follow-ups tracked separately). Unblocks gHashTag/tri-net#33 T27-first wire flip. Anchor: phi^2 + phi^-2 = 3
…f '()' (Closes #1314) (#1320) Add Expr::Cast arm in expr_to_rust (bootstrap/src/compiler.rs), mirroring the gen-verilog arm at compiler.rs:4941. Before this fix, ExprCast fell through to the default '_ => "()".to_string()' branch and generated empty-tuple stubs in Rust output, silently corrupting any T27 spec using bit-width casts. Also update docs/NOW.md with an exprcast-rust-emitter entry (required by the NOW Sync Gate). Closes #1314 (gen-rust half; gen-c and gen-zig follow-ups tracked separately). Unblocks gHashTag/tri-net#33 T27-first wire flip. Anchor: phi^2 + phi^-2 = 3 Co-authored-by: Perplexity Computer <agent@perplexity.ai>
Regenerated via t27c (release, built from t27 master c4dc8ee) which now includes the Expr::Cast arm in expr_to_rust that emits '(operand as target)' instead of the empty-tuple stub. This closes the T27-first partial flip that a6bb0b0 opened: gen/rust/wire.rs is now the full canonical output of specs/wire.t27, not a hand-patched shim. Diff shape: +40 / -49 (banner shrinks to the raw t27c header, be_byte / u32_be / header_byte bodies swap their return-() stubs for real 'as u8' / 'as u32' casts). Tests: cargo test --lib 101/0, cargo test --test m2_routing_pure_logic 25/0. Anchor: phi^2 + phi^-2 = 3 Refs #33
Real t27c gen-rust emits idiomatic-T27 output (literal return + explicit parens) which fails 'cargo clippy -- -D warnings' with needless_return, unnecessary_parens, and unused_parens. Since gen/ is untouchable (see docs/T27_FIRST_MIGRATION.md), scope the allow to the wrapping mod in src/wire.rs rather than hand-editing the generated file. Cleaner Rust rendering (drop trailing 'return', drop wrapping parens on whole-expression returns and on if-condition wrappers) is upstream work on gHashTag/t27 in expr_to_rust; when that lands, the allow can shrink or disappear on the next regeneration. cargo build 101/0 lib, m2_routing_pure_logic 25/0, cargo fmt clean. Anchor: phi^2 + phi^-2 = 3 Refs #33
Real t27c gen-rust emits idiomatic-T27 output (literal return + explicit parens) which fails 'cargo clippy -- -D warnings' with needless_return, unnecessary_parens, and unused_parens. Since gen/ is untouchable (see docs/T27_FIRST_MIGRATION.md), scope the allow to the wrapping mod in src/wire.rs rather than hand-editing the generated file. Cleaner Rust rendering (drop trailing 'return', drop wrapping parens on whole-expression returns and on if-condition wrappers) is upstream work on gHashTag/t27 in expr_to_rust; when that lands, the allow can shrink or disappear on the next regeneration. cargo build 101/0 lib, m2_routing_pure_logic 25/0, cargo fmt clean. Anchor: phi^2 + phi^-2 = 3 Refs #33
7f0db07 to
45d84ce
Compare
Replace `u32::from_be_bytes(b[2..6].try_into().ok()?)` (and the dst-slot equivalent) with `u32_be(b[2], b[3], b[4], b[5])` — the auto-generated function produced by t27c from specs/wire.t27. Byte-order equivalence: - `u32::from_be_bytes([b0,b1,b2,b3])` = (b0<<24)|(b1<<16)|(b2<<8)|b3. - t27c-generated `u32_be(b0,b1,b2,b3)` = same expression (see gen/rust/wire.rs). - `try_into().ok()?` on a 4-byte slice never returns None post the length check on line 82; removing it eliminates a dead error path. Consequence: the parse-side arithmetic now lives under the SSOT contract. spec-drift-guard CI (workflow spec-drift-guard.yml, merged in #35) is the enforcement mechanism — any drift between specs/wire.t27 and gen/rust/wire.rs now covers this reassembly path as well. Closes weak-spot audit finding #5 (post-#33 loop): src/wire.rs still used std::from_be_bytes rather than delegating to the auto-gen equivalent, so the parse-path was outside the SSOT umbrella even though the serialize-path (header_byte + be_byte) was inside. Also re-exports `be_byte` and `u32_be` from the `gen` module for symmetry with the other spec-driven helpers. Verification (local): - `cargo build`: clean. - `cargo test --lib`: 101 passed / 0 failed. In particular `wire::tests::header_roundtrips` still passes, confirming byte-order equivalence end-to-end. Anchor: phi^2 + phi^-2 = 3. Co-authored-by: Perplexity Computer <agent@perplexity.ai>
…dit-trail primitive Bounded expansion (not full v1). The skeleton previously proposed the auditability primitive abstractly (Sections 1-4); this commit adds Section 5 reporting that the primitive is now empirically realized end-to-end at tri-net main dc1bebb + t27 master 3c912d9. Section 5 subsections: - 5.1 What is materialized -- one spec (specs/wire.t27), three generated backends (gen/{rust,zig,c}/wire.*), one workflow (spec-drift-guard.yml), consumer path under the same umbrella (src/wire.rs -> u32_be). - 5.2 Audit-trail tuple (concrete instance) -- (tri-net@dc1bebb, t27@3c912d9, spec-drift-guard run) is the minimal fetch set for a third party to independently verify byte-identity. - 5.3 Merge chain table -- 5 tri-net PRs (#33/#34/#35/#37/#38) + 2 t27 PRs (#1320/#1337) with merge SHAs. - 5.4 Empirical checks at HEAD -- three diff -u results (all empty), zero 'unsupported: ExprCast' in Zig/C output, 101 lib tests green including header_roundtrips as empirical byte-order-equivalence proof. - 5.5 What this reference impl does NOT show -- byte-identity is not functional correctness; only one spec is covered so far; no silicon; trust in t27c itself is not eliminated (moved, per Carrone 2026). Sync updates across the doc: - Header cross-link: 91a5b63 -> dc1bebb (post-#38). - Section 3.1: describes all three gen targets, not just Rust. - Section 3.2: drift-guard covers Rust + Zig + C; links to #35 + #38. - Section 3.3: ExprCast marked resolved on all four backends (Rust #1320, Zig+C #1337, Verilog pre-#1320). - Section 6 (Limits): removed obsolete 'Rust-only SSOT until #1333' bullet; added trust-surface-of-t27c and no-HDL-target-yet bullets. - Section 7 (Future work): struck through completed Zig+C drift-guard item; added protocol-stack expansion, t27c deterministic-build items. - Appendix A: three-target reproducibility recipe (8 steps), pins tri-net@dc1bebb + t27@3c912d9. Draft only -- not for external circulation. Anchor: phi^2 + phi^-2 = 3.
Second + third T27-first flips (after wire.t27 #33). Both specs produce CLEAN gen-rust output (0 broken casts, 0 unsupported) — no dynamic arrays needed, ExprCast lowering (t27@3c912d9) handles all 'as u8'/'as u32'. Generated (6 files, 3 targets × 2 specs): gen/rust/hello.rs (67L) — MAX_HEARD, hello_byte, reports_hearing, etc. gen/rust/etx.rs (68L) — OPTIMISTIC, DEAD_EPS, ONE_FP, calc_etx, etc. gen/zig/hello.zig — @as/@intcast form gen/zig/etx.zig gen/c/hello.c — ((uint8_t)(...)) form gen/c/etx.c Guardrail tests (tests/t27_gen_hello_guardrail.rs, 4 tests): - hello constants (MAX_HEARD=3, HEADER_LEN=13) - hello byte-layout (src field BE, n at idx 8) - etx constants (OPTIMISTIC, DEAD_EPS, ONE_FP in Q8.8) Gate: cargo test --all = 141 passed, 0 failed (was 137 + 4 new). Drift-guard extension (to cover hello + etx) follows in next commit.
…-end measurement Single-process loopback confounded every metric and repeatedly produced broken-ruler results: sent and recv were the SAME node's counters (delivery ratios >100% from timing offsets, waves #26/#33/#37), jitter==0 meant "no stream" not "clean link" (#24), and a cross-thread crash-race hid until it fired (#33). There was no way to verify the UI or a real MITM either. Fix: run TWO independent TriNetMonitor instances that dial each other over real UDP. Three dev-only env hooks (no-ops in a shipping run): TRINET_LOG=<path> per-instance log (LogBus) so each process's counters read apart TRINET_LISTEN=<port> this instance's UDP listen port TRINET_AUTOCALL=host:port auto-dial a 1-1 call on launch (INVITE bypassed) CallManager.startCall now honors a distinct listen port so two locals don't collide. smoke/two_endpoint_rig.sh orchestrates A(:8000) <-> B(:8100) and reports the CROSS-process delivery A->B = (B received)/(A sent) — the honest number a single process cannot give. Verified live: two real windowed processes establish a bidirectional call (macOS shares the camera, so both send AND receive). Clean link: A->B 98.7%, B->A 93.4%. With 25% induced packet loss on B: A->B still 96.7% -- i.e. the layered loss-recovery stack (grouped FEC + whole-NAL NACK + per-fragment NACK from #31-33) recovers 25% loss to ~97% frame delivery, now proven over two genuine processes instead of a self-loopback. Mac-only (the rig runs two Mac instances); the env hooks are inert dev tooling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Flips
src/wire.rsto consume auto-generated code fromspecs/wire.t27viat27c gen-rust. The spec is now the source of truth for constants and pure predicates; the Rust module only adds ergonomic wrappers on top.What changed
gen/rust/wire.rs(new, deterministic build output): auto-generated constants + predicates, plus documented hand-written stubs forbe_byte/u32_be(see limitation below).src/wire.rs: re-exports auto-gen symbols, keepsHeader/FrameKind/to_bytes/parseon top. No local re-declaration ofVERSIONorHEADER_LENanymore.build.rs(new): optionalt27c gen-rustinvocation underT27C_REGENERATE=1. Silent on missing t27c, so CI without the compiler still builds.docs/T27_PORT_STATUS.md: wire.rs row updated to 'T27-FIRST (partial)'.docs/T27_FIRST_MIGRATION.md(new): rationale, bootstrap limitation, regeneration recipe.Why partial
t27c-0.1.0bootstrap has a parser bug on bit-shift expressions — bothgen-rustandgen(Zig) drop((w >> N) & 255) as u8and emitreturn ();. Sobe_byte/u32_becannot come from the compiler yet. They live insidegen/rust/wire.rsbeneath a documented banner. Once the bit-shift bug is fixed upstream ingHashTag/t27, delete the stubs and letgen-rustemit them. Filing a repro issue against gHashTag/t27 as a follow-up.Verification
cargo test --lib— 101/0 (includes two new guardrails: constants + predicates).cargo test --test m2_routing_pure_logic— 25/0.cargo fmt --all -- --check— clean.No hardware runs, no fabricated metrics, no changes to crypto / modem / routing.
Anchor: phi^2 + phi^-2 = 3