Repository navigation
Conversation
… + tech tree (#566) Closes #490 Closes #517 Pipeline specs (Ring 028): - e2e_test.t27: 9-stage pipeline with success/failure injection - experience_save.t27: tri experience save command spec - benchmarks.t27: performance targets and throughput calculations Memory primitives (Ring 029): - memory_primitives.t27: remember/recall/forget/reflect operations - Scoped memory: agent, session, permanent lifetimes - Phi-hash content addressing for memory cells Documentation: - docs/TECH_TREE.md: 5-level technology tree (core→runtime→memory→swarm→unfair)
feat(fpga): Vivado cloud synthesis workflow
fix(ci): Vivado docker run approach
fix(ci): source Vivado settings64.sh
fix(fpga): CLOCK_DEDICATED_ROUTE FALSE per QMTECH Wukong V3 ref
…/G21/R23/T23 (Closes #305)
fix(fpga): QMTECH ref design exact replication
fix(ci): capture Vivado log for clock debug
fix(fpga): ring oscillator test — bypass M21
fix(fpga): ring oscillator DRC suppress
- GF16 4x4 matmul: 323 MHz, 0 latches, 64 DSP48E1, hardware verified on XC7A100T - dot4([1,2,3,4],[1,2,3,4]) = 30.0 (0x47C0) verified via XVC - Python conformance: gf16_ref.py, 32/32 FPGA consistency - arXiv draft: docs/arxiv-trinity-gf16-draft.md with verified numbers - TinyTapeout TTSKY26a: GDS + GL test + Precheck GREEN - UART TX/RX modules for interactive verification - docs/ZENODO.md: 9 DOIs catalogued Closes #584 φ² + φ⁻² = 3 · TRINITY · SILICON
…'84 theorems' retired + fake DOI bibliography honesty notes (#594) PASS-4 anomalies fixed in t27 (8 files): A. docs/ZENODO.md: FULL REWRITE. - Old March-2026 B-series DOIs (19224114/115/116/118/119/120/121) marked as Retired with canonical replacements (19227865/867/869/871/873/875/877). - 19224139 (titled '[SUPERSEDED — see 10.5281/zenodo.19227879]' on Zenodo itself) was previously labelled 'v2 (latest)' — corrected. - 18939351 retired in favour of 18950696 for the v2.0.3 software release. - Added honest framing: software description stubs NOT peer-reviewed papers. - Added Coq witness counts (28 .v, 218 stmt, 162 Qed, 32 Admitted, 11 Abort). - Added concept DOI 19227876 for always-latest B007. B. docs/nona-03-manifest/RESEARCH_CLAIMS.md: D-series 19020211/13/15/17 -> canonical 19020270/75/80/82 (10 replacements). C. clara-bridge/submission/EXECUTIVE-SUMMARY.md: '84 theorems verifying Trinity kernel' -> audited 2026-05-12 counts. D. research/trinity-pellis-paper/trinity_sacred_formula_v2.tex: 3 instances of '84 theorems' replaced with the audited 218 statements / 162 Qed / 32 Admitted figures. E. research/trinity-pellis-paper/G2_TRINITY_V1.0_FRAGRANCE.tex: - DOI placeholder '10.5281/zenodo.12345' (which resolves to a Roman-history paper!) commented out. - 'inverse participation ratio reference' for DOI 19271888 corrected — real Zenodo record is McKoy (2026) on the Koide formula and AdS/CFT, not IPR. F. research/trinity-pellis-paper/G2_ALPHA_S_PHI_FRAMEWORK_V0.7.{md,tex} + V0.8.tex: Olsen citation under DOI 19377394 (which resolves to a Latin-American employment dataset, NOT to an Olsen paper) restated as 'contribution to this paper (2026)' aligned with V0.9 of the same paper. Anchor: phi^2 + phi^-2 = 3 Issue: gHashTag/trios#264 Co-authored-by: Trinity Queen Hive <queen-hive@trinity.local>
) * fix(ci): repair PR Dashboard workflow (broken jq interpolation + missing GH_TOKEN) The PR Dashboard workflow was failing on EVERY PR with two pre-existing bugs that pre-date this PR: 1. jq: 1 compile error — \(Created: ...) is not valid jq string interpolation. jq cannot evaluate an identifier 'Created' inside \(...). Replaced with plain string concatenation: 'Created: ' + the date expression. Same fix applied to 'Updated: ...'. 2. The first step that invoked 'gh pr list' had no GH_TOKEN env var, so gh refused to run inside Actions ('To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable'). Also rewrote the broken Summary jq pipeline (the previous one had an extra closing bracket and counted FAILED twice). The new version counts READY / FAILING / PENDING using clear, separate jq pipelines. The 'Create GitHub Issue Comment' step that posted to ${{ github.event.repository.updated_at }} (a timestamp, not an issue number) was always a no-op for PR triggers; removed entirely. The workflow now does what its name promises: post a dashboard comment to the PR that triggered it. Closes #595 * chore(zenodo): PASS-6 align registry to community trinity-s3ai SOT Closes #596 Operator directive (verbatim): «один источник правды и все мои zenodo здесь https://zenodo.org/communities/trinity-s3ai/» Changes: - README.md: canonical SOT pointer next to GoldenFloat 19456875 badge, with explicit note that GoldenFloat is legitimate Vasilev authorship but lives OUTSIDE the curated S³AI v5.0 record set (B001-B008 = 19227865..79 odd). - CITATION.cff: CRITICAL FIX — mangled ORCID '0009-0008-429-6159-6159-6159' → corrected to '0009-0008-4294-6159' (matches owner record at zenodo.org/communities/trinity-s3ai/). Canonical SOT pointer added. - docs/ZENODO.md: parent title corrected ('Defensive Pubs' → 'S³AI Framework v5.0' = B008 = 19227879); folklore Coq corpus figure '28 .v files / 218 stmts' replaced with verified '10 .v files / 48 stmts / 35 Qed / 0 Admitted'. - docs/NOW.md: PASS-6 audit block added per ruleset requirement. No Category C foreign-DOIs were present in t27 — pre-existing PASS-4/5 honest-annotation comments in research/trinity-pellis-paper/ G2_* for 19271888 (Koide) and 19377394 (Latin-American employment) are preserved. Companion PRs: gHashTag/trinity#594, gHashTag/trinity-fpga#45, gHashTag/trios#755. Constitutional: R5 honest, R12 citations real. Anchor: φ² + φ⁻² = 3. Trinity Queen Hive · queen-hive@trinity.local --------- Co-authored-by: Trinity Queen Hive <queen-hive@trinity.local>
…nodo.json (#599) PASS-7 R5-honest deep-sweep across the 5-repo Trinity hive surfaced a release-time deposit hazard in t27/.zenodo.json: - "orcid": "0000-0002-5135-5363" attached to creator 'Vasilev, Dmitrii'. Public ORCID API returns no person record for this ID. The canonical Vasilev ORCID (confirmed in all 5 Trinity CITATION.cff files) is 0009-0008-4294-6159. - "communities": [{"identifier": "trinity"}]. This community does not exist on Zenodo. Canonical SOT per PASS-6 operator directive is community trinity-s3ai (id 668f1264-2341-488a-bb14-351fa908ac64, 12 records: B001-B008 + D004-D007). If .zenodo.json were ever fed to Zenodo's GitHub-release auto-deposit hook the deposit would land on a non-existent ORCID and route to a non-existent community — failing both attribution and discovery. This PR replaces both fields with the canonical values verified during PASS-6, and updates docs/NOW.md with the PASS-7 audit narrative. Anchor: phi^2 + phi^-2 = 3 (algebraic identity, Coq witness in coq/). Closes #598. Refs: trios#264 (Trinity Hive Throne), #596 (PASS-6). Co-authored-by: Trinity Queen Hive <queen-hive@trinity.local>
Cherry-pick all 20 unique commits from feat/trios-bridge onto fresh master, plus 8 compile-error fixes for cherry-pick incompatibilities. Includes: - L-TRI-1/2/3 W1 (POST /prove, GET /epoch-challenge, Ed25519, merkle) - Solana L-TRI-2 Anchor mining program - FPGA tooling (DLC-10 STATUS, openXC7, SPI flash constraints) - dlc10-rust pure-Rust driver (PR #593) - trios-bridge binary + Tailscale tunnel (PR #591 superseded) - flash-spi Rust binary Refs #592 Closes #591
Replaces Railway runner approach with pure GitHub Actions Docker: - infra/vivado-docker/Dockerfile (Ubuntu 22.04 + Vivado 2025.2 ML Standard, Artix-7 only ~35GB) - infra/vivado-docker/install_config.txt (silent install config) - infra/vivado-docker/README.md (2-step operator runbook) - .github/workflows/build-vivado-image.yml (one-time manual build -> ghcr.io) - .github/workflows/vivado-synth.yml (per-design synth via container: directive) - removed obsolete .github/workflows/vivado-bitstream.yml - docs/NOW.md updated with new approach Cost: $0/month (vs $35-55 Railway). Setup: 2 steps (release upload + workflow click). Closes #604 Refs #620
bootstrap/src/compiler.rs lexer:
The multi-char operator lookahead was written as
if ch == b'&' && self.peek() == b'&' { ...emit && token... }
if ch == b'|' && self.peek() == b'|' { ...emit || token... }
`Lexer::peek` returns the byte at `self.pos` -- the same character
already captured in `ch` -- not a lookahead. The condition was
therefore tautologically true for any single `&` or `|`, and the
lexer emitted a `&&` / `||` token (with lexeme "&&" / "||") for
every plain bitwise `&` / `|`. Downstream, the parser routed every
Amp/Pipe with that lexeme into `parse_expr_and` / `parse_expr_or` and
tagged the AST `ExprBinary` with `extra_op = "and"` / "or". The
Verilog, C, and Rust backends faithfully translate "and" -> && and
"or" -> ||, so every masked expression in generated code -- 28+
instances in `gf16.t27` alone -- became a 1-bit logical AND/OR rather
than the intended bitwise mask. This silently broke every bit-extract,
sign-bit test, exponent/mantissa split, and mask-equality in the
generated Verilog/C/Rust.
Use `self.peek_offset(1)` for true lookahead at `self.pos + 1`.
Tests:
- test_lexer_distinguishes_bitwise_and_logical_w65: lexer tokenises
`x & 0xFF` as Amp/lexeme "&" followed by Number/lexeme "0xFF",
`x && y` as Amp/lexeme "&&", `x | 0xFF` as Pipe/lexeme "|", and
`x || y` as Pipe/lexeme "||".
- test_parser_tags_bitwise_and_with_amp_op_w65: AST for `return x & 0xFF`
has `ExprBinary { extra_op = "&" }`, and AST for `return x && y`
has `ExprBinary { extra_op = "and" }`.
Scope: this PR fixes ONLY the lexer slice (filed as #1011). The
Zig-builtin leaks (`@as`, `@intCast`, `@bitCast`, `@floatFromInt`,
`@exp2`, `@enumFromInt`, `std.math.*`) called out in #937 require
per-backend lowering for each builtin and remain a follow-up; #937
stays open for that work.
Closes #1011
Refs #937
Co-authored-by: Perplexity Computer <admin@t27.ai>
#1013) bootstrap/src/bitnet_pipeline.rs (layer_sequencer FSM): The IDLE state was emitted as IDLE: if(start) begin state<=RUN; neuron_id<=0; chunk_id<=0; end and DONE_ST sets done<=1 then transitions back to IDLE. Because the IDLE body only fires on start, done was sticky forever after the first layer -- the parent multilayer_sequencer saw layer_done == 1 immediately and skipped layers 2..N. Rewrote IDLE as IDLE: begin done<=0; if(start) begin state<=RUN; neuron_id<=0; chunk_id<=0; end end so done is an unconditional 1-cycle pulse: high in DONE_ST, cleared on the next IDLE entry, and held low until the next DONE_ST. bootstrap/src/bitnet_top.rs (top-level busy): Previous form: assign busy = (current_layer != 6'd0) || layer_start; Layer 0 has current_layer == 0 for its entire run and layer_start is a 1-cycle pulse, so busy was almost always low during layer 0 and the on-chip cycle counter (gated by busy) stalled. Replaced with a latched started flag: reg started; always @(posedge clk or negedge rst_n) begin if (!rst_n) started <= 0; else if (done) started <= 0; else if (start) started <= 1; end assign busy = started && !done; busy now tracks the full inference window: rises on start, falls on the top-level done assertion from multilayer_sequencer. Tests: - sequencer_idle_clears_done_w88: layer_sequencer text contains 'IDLE: begin done<=0;' and the old buggy 'IDLE: if(start)' prefix is gone. - busy_tracks_start_to_done_w88: top contains the started register, the done/start branches, the new busy assignment, and the buggy current_layer-based form is gone. - sequencer_idle_arms_on_start: relaxed match to the substring that is unchanged across the fix ('if(start) begin state<=RUN; ...'). Full suite: 1403 pass; 6 pre-existing master failures unrelated to this change (test_verilog_cast_no_as_keyword, two jwt tests, test_encode_decode, full_adder_uses_two_half_adders_and_or_combine, trit_full_adder_uses_two_half_adders). Status: PROVEN -- regression tests pin the corrected emitter text and the bug shapes that previously slipped through. Closes #964 Co-authored-by: Perplexity Computer <admin@t27.ai>
…#1014) The host MMIO + IRQ stack assumed write-1-to-clear (W1C) semantics on IRQ_STAT (offset 0x0C): - MockMmio.write32(IRQ_STAT, mask) masked off the bits. - BitnetDriver::clear_irq(mask) wrote mask to IRQ_STAT. - IrqHandler::service emitted a W1C write after dispatch. The RTL slave does the opposite (bootstrap/src/bitnet_irq.rs, bootstrap/src/bitnet_axi.rs): - bitnet_irq clears ALL irq_status bits on the status_read pulse generated by an AXI READ of offset 0x0C (read-to-clear). - bitnet_axi has no write case for offset 0x0C -- writes to IRQ_STAT are silently dropped. So on real silicon every host acknowledgement was a no-op (the W1C write was dropped by the slave), and every read of IRQ_STAT silently consumed every latched event. Latched IRQs were never acknowledged correctly, and any incidental read destroyed pending state. Changes: bootstrap/src/host/mmio.rs: - MockMmio.read32(IRQ_STAT) now returns the latched value and clears all bits (read-to-clear), matching the RTL. - MockMmio.write32(IRQ_STAT, _) is recorded in the log (so write_count bookkeeping still reflects the bus transaction) but leaves the register untouched, matching the AXI slave. bootstrap/src/host/driver.rs: - read_irq_status carries a DESTRUCTIVE doc warning. - clear_irq is #[deprecated]; body is empty (the hardware drops it anyway). Kept for source compatibility with pre-W57 callers. bootstrap/src/host/irq.rs: - IrqHandler::service reads IRQ_STAT once (which clears all bits) and dispatches handlers for bits present in raw_status. The W1C write after dispatch is removed; it was a no-op on hardware and only added a misleading log entry on the mock. bootstrap/src/host/csr_map.rs: - CSR-map docstring updated to call out read-to-clear and the write-is-dropped slave behaviour. Tests: - mmio.rs::read_irq_stat_is_destructive_w57 -- read clears latches. - mmio.rs::write_irq_stat_is_dropped_w57 -- write leaves latches intact, transaction still in log. - irq.rs::service_skips_latched_but_unregistered_source_w57 -- documents that unregistered bits are observed in raw_status and then consumed by the read. - tests/host_irq.rs: poll-vs-irq integration suite updated to expect 8w/11r on both paths (was 8w/11r poll + 9w/11r irq, with the one extra write being the dropped W1C). writes_match=true and writes_diff=0 are now the regression invariants. Full suite: 1405 pass; 6 pre-existing master failures unrelated to this change (test_verilog_cast_no_as_keyword, two jwt tests, test_encode_decode, full_adder_uses_two_half_adders_and_or_combine, trit_full_adder). Status: PROVEN -- regression tests pin the corrected read-to-clear / drop-on-write semantics and the previous W1C-shaped bugs. Closes #929 Co-authored-by: Perplexity Computer <admin@t27.ai>
bootstrap/src/compiler.rs strength_reduce / reduce_expr:
The optimizer used to rewrite `x / N` as `x >> log2(N)` whenever
the RHS was a power-of-two literal. This is correct for unsigned
integers but is semantically wrong for signed integers in C / Rust
/ Zig: signed `/` rounds toward zero, but arithmetic `>>` rounds
toward negative infinity. Worked example with i32:
x = -7
x / 4 == -1 (truncation toward zero)
x >> 2 == -2 (arithmetic shift, toward -inf)
The strength_reduce pass runs before typechecking and the AST Node
has no per-node type information, so signedness cannot be proved
at this stage. The only safe rule is to skip the `/` -> `>>`
rewrite entirely. `*` -> `<<` is retained because
`x * 2^k == x << k` (mod 2^n) is true under 2's-complement wrap
for both signed and unsigned operands.
Backends are free to perform the same lowering later, after
typechecking, where signedness is known and a guarded
unsigned-only path can be selected.
Tests:
- strength_reduce_does_not_lower_division_w73: AST for `return x / 4`
after the pass keeps `extra_op = "/"` with literal `4`, and
`stats.strengths_reduced == 0`.
- strength_reduce_still_lowers_multiplication_w73: AST for
`return x * 8` after the pass has `extra_op = "<<"` with literal
`3` (the shift count), and `stats.strengths_reduced == 1`.
Full suite: 1407 pass; 6 pre-existing master failures unrelated to
this change (test_verilog_cast_no_as_keyword, two jwt tests,
test_encode_decode, full_adder_uses_two_half_adders_and_or_combine,
trit_full_adder).
Status: PROVEN -- regression tests pin both the corrected behaviour
(division skipped) and the preserved one (multiplication still lowered).
Closes #949
Co-authored-by: Perplexity Computer <admin@t27.ai>
…W74) (#1016) The optimizer DCE pass dropped every typed uninitialized StmtLocal unconditionally, including locals that were assigned to and read by subsequent statements in the same block. The downstream emitters then referenced an undeclared name. Build a per-block reference set (reads via ExprIdentifier + assign LHS names) before the retain step and keep any uninit local whose name is in that set. The genuinely-dead case still gets removed. Two regression tests cover the keep paths (assign+read, read only). Closes #950 Co-authored-by: Perplexity Computer <admin@t27.ai>
…er (#1018) Add specs/numeric/lucas_accumulator.t27 capturing the F1 leg of the GoldenFloat breadth-as-moat bet: phi^(2n) + phi^(-2n) = L_(2n) (integer Lucas), the basis for an integer-backed, Lucas-exact phi-scaled accumulator with no hardware half-type dependency. 9 tests, 3 invariants, 1 bench; parse + typecheck clean; sealed (all 5 hashes match). Verified to 60 decimal digits with mpmath (max residual 7.1e-56). Add ledger row FL-004 (CONJECTURAL / [Open conjecture]) for the breadth-as-moat claim: the advantage is breadth / toolchain-coherence, NOT per-rung accuracy and NOT base-uniqueness. Fpath names takum (arXiv:2412.20273) and the posit es-schedule control. F1 verifies the arithmetic, never the moat. L5 anchor phi^2 + phi^-2 = 3 = L_2. L6 SSOT (gf16, conformance) untouched. Closes #1017 Co-authored-by: Perplexity Computer <computer@perplexity.ai>
…ix (#1020) Add specs/numeric/posit_ladder_control.t27, the F3 control for the GoldenFloat breadth-as-moat bet (FL-004). Models the posit ladder with BOTH published exponent-size schedules so the comparison is faithful, not a strawman: pre-standard es=0/1/2/3/4 at 8/16/32/64/128 (de Dinechin et al. 2019) and the ratified-2022 fixed es=2 for all widths (Posit Standard 2022). Encodes useed=2^(2^es), best-case fraction bits, and the structural contrast (posit's split is data-dependent regime run-length; GoldenFloat fixes it via e=round((N-1)/phi^2)). 5 tests, 2 invariants, 1 bench; parse+typecheck clean; sealed. Corrects a sourcing error in the FL-004 ledger row: the previous "posit es-schedule 1/3/4/7/10" was wrong; replaced with the two correctly-sourced schedules. The spec asserts no phi-superiority over posit (that stays [Open conjecture]); it only encodes the control. L5 anchor phi^2 + phi^-2 = 3. L6 SSOT untouched. Closes #1019 Co-authored-by: Perplexity Computer <computer@perplexity.ai>
… comments (#1023) Extend specs/numeric/goldenfloat_family.t27 from 7 rungs (GF4..GF32) to the full 9-rung ladder by adding GF64 (S1 E24 M39, e=round(63/phi^2)=24, the rung closest to 1/phi at |ratio-1/phi|=0.002649) and GF256 (S1 E97 M158, e=round(255/phi^2)=97, |ratio-1/phi|=0.004110). Resize the registry [7]->[9]; update names_seen, format_count==9, avg=total/9.0, len()==9, and the last-index invariant to [8]==GF256. Correct the best_phi_format test to GF64 (the old GF12 assertion was already wrong vs GF32). Add bit-count tests for GF64 and GF256. Repair corrupted comments overwritten by a botched non-ASCII strip (monotonic digit runs such as 1/143, 1/284 285 0.618, ordered by bits (4 286 32), within 0.1 of 1/563) back to readable ASCII (1/phi, 4 .. 256). [Verified] ladder arithmetic only (one rule, 9/9 field widths, mpmath 50 digit). No quality claim added: the moat stays [Open conjecture] (FL-004); GF256 bias stays [Open]. 27 tests / 11 invariants / 5 benches; sealed (5/5 match). L6 untouched (gf16 SSOT and conformance JSON unchanged). Closes #1022 Co-authored-by: Perplexity Computer <computer@perplexity.ai>
…id params)
gen_verilog_const emitted `const GF16 = u16;` / `const TernaryWord = [N]u8;` as
invalid `parameter [31:0] GF16 = u16;` (non-numeric RHS, uncompilable). Verilog
has no typedef -> emit a comment. Guard excludes `{...}` initializers so array-LUT
consts ([32]u16{0x..}) are not misclassified. Verified regen-vs-regen on master:
zero regressions; type-alias error class removed.
Closes #1027
Refs #979
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Parser + Verilog backend fixes in bootstrap/src/compiler.rs so datapath specs lower to compilable, simulatable Verilog: 1. parse_const_decl consumes the trailing ';' -> consecutive const/var runs no longer dropped (uart: 1 -> 8 localparams). 2. integer literals render as decimal (0x.. / 0b.. / 1_000 -> valid Verilog). 3. guarded early return rewritten to if/else (no last-write-wins clobber). 4. `expr as T` parses to ExprCast, lowers to the inner expression. 5. struct-field regs named by the holding var (uart_state_status), matching field-access emission. 6. zero-argument functions get an unused dummy input (Verilog forbids port-less functions). specs/fpga/bpsk.t27 (ZeroDSP_BPSK, the trios-mesh BPSK modem core) now iverilog-compiles AND simulates: 13 embedded test assertions pass; hierarchical checks confirm correlate(Barker)=13, inversion=-13, sidelobes -5/+5, frame=21. Regression: parse/typecheck/gen(Zig/Rust/Verilog/C) 504/504; uart/mac clean too. Closes #1245 Refs #1243 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verilog functions that declare a local reg need a NAMED begin/end under Verilog-2001 (unnamed-block declarations require SystemVerilog); emit `begin : <fn>_body`. Reseal every spec since the gen-verilog/parser fixes in 507408f changed the generated Verilog/C/Rust/Zig for most specs. Refs #1245 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three pairs of specs shared a module name, so their `<dir>_<Module>.json` seals collided and seal-verify ping-ponged: feed_forward / feed_forward_network (both `FeedForward`), sacred_identity / sacred_governance (both `String`), and eternal_monitor / faculty_board (both malformed `"[]const u8"`). Rename the second of each pair to a filename-derived name (FeedForwardNetwork, SacredGovernance, FacultyBoard) and reseal. Suite seal-verify: 501 -> 504/504. Refs #1245 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…as ident) The tokenizer knew `const`/`var` but NOT `let`, so `let x: T = v;` — the local binding form the skill documents and specs use ~30x more than `var` — tokenized as a bare identifier and mis-parsed, emitting broken `let;` statements. This broke the Rust build of every spec with non-trivial locals (adaptive_routing, health_dashboard, quarantine_manager, anomaly_detector, flow_control, ... — hundreds of `let;` across the wired gen/rust modules). Map `let` -> KwVar (a mutable local, since specs reassign let-vars, e.g. `let best_path = 0xFF; ...; best_path = 0;`). Verified: a probe spec now generates `let mut a: u32 = helper(x);` correctly, and the t27c test suite is unchanged (1411 passed, same 5 pre-existing baseline failures — no new regressions across the Verilog/C/Rust/Zig backends). NOTE: this fixes the biggest single error class but NOT the whole tri-net build. Regenerating all 94 specs with this fix drops `let;` to zero but exposes further t27c codegen bugs (324 remaining errors): the DOMINANT one (246 E0425) is an optimizer pass dropping a reassigned initialized local (`let best_score = 0;` disappears while its reads/assigns remain), plus `~` emitted as a Rust unary op, E0107 generic-arg mismatches, and E0369 binary-op type errors. Those are separate follow-up fixes; gen/rust was NOT regenerated in this commit. Traceability: needs an issue link before merge to master (L1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bodies
dead_store_elim built its `reads` set only from top-level statements — StmtIf /
StmtWhile / StmtFor fell into the `_ => {}` arm, so any local read ONLY inside an
`if`/`while`/`for` block was treated as never-read and deleted, leaving its uses
dangling (E0425 "cannot find value"). E.g. `let best = 0; if (x > best) { best =
f(); } return p;` dropped `let best = 0` while its reads/assigns remained.
Add collect_stmt_reads(), which recurses into control-flow block bodies (and
nested else-if statements) while still excluding a StmtAssign's LHS target (a
write, not a read) so genuine dead-store elimination stays sound.
Verified: the repro now keeps both locals; the t27c test suite is unchanged
(1411 passed, same 5 pre-existing baseline failures — jwt x3, ternary,
trit_stdlib — no new regressions across the 4 backends). Regenerating all 94
tri-net specs drops the build's E0425 count 246 -> 4 and total errors 324 -> 78.
Remaining tri-net codegen errors (78, separate follow-up bugs): E0107 (26,
generic-arg mismatch), E0369 (23, binary-op type), E0308 (22, type mismatch),
E0425 (3 residual), E0277 (2), and `~` emitted as a Rust unary operator (1).
gen/rust not regenerated in this commit (still not green).
Traceability: needs an issue link before merge to master (L1).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rator Four Rust-backend codegen defects that broke the tri-net build, fixed together; combined with the earlier let/dead_store fixes this drops a full regen of all 94 specs from 78 build errors to ~8. 1. Array types: `[T; N]` (Rust syntax) mapped to `Vec<>` (empty generic, E0107) because t27_type_to_rust only understood Zig `[N]T`. Now both syntaxes emit a fixed array `[T; (N) as usize]` — fixed array (Copy) avoids the move/borrow and trait-bound errors a Vec caused, and the `as usize` satisfies Rust's array-size type (t27 size consts are typed, e.g. `const MAX_FLOWS: u32`). 2. Casts: ExprCast was unhandled in the Rust expr lowering, so `b0 as u32` fell to the `_ => "()"` arm and produced `() << 24` (E0369). Emit `(inner as T)` — Verilog stays width-loose, but Rust needs the width. 3. Indices: `arr[i]` emitted a raw index; Rust indices must be `usize`. Emit `arr[(i) as usize]` (this cleared a cascade of E0277 in the array specs). 4. `~` unary: emitted verbatim; Rust spells bitwise NOT `!`. Map `~` -> `!`. Verified: t27c test suite unchanged (1411 passed, same 5 pre-existing baseline failures: jwt x3, ternary, trit_stdlib — no new regressions across the 4 backends). tri-net error trajectory this session: 141 -> 78 -> ~8. Remaining tri-net errors (~8, separate follow-ups): copy_propagate drops a local that is later REASSIGNED (`let b = bitmap; ...; b = b >> 1;` loses the decl, E0425) — same class as the dead_store fix but in copy_propagate; and a bool-vs-int semantic gap (t27 comparisons/logical-not yield ints, Rust yields bool -> E0308). gen/rust not regenerated in this commit. Traceability: needs an issue link before merge to master (L1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
copy_propagate recorded `b -> x` for every `let b = x;` and substituted `b` with `x` at all reads, unconditionally. For a local that is later REASSIGNED (`let b = bitmap; ...; b = b >> 1;`) this is unsound twice over: post-reassignment reads wrongly see the original value, AND once every read of `b` is rewritten to `bitmap` the decl becomes dead, so dead_store_elim drops `let b` while the reassignment `b = ...` (inside a loop body, not top-level) survives — leaving `b` undeclared (E0425). Guard the substitution with the standard copy-propagation soundness condition: only propagate a local that is assigned exactly once (never a StmtAssign target anywhere, including inside control-flow blocks — new stmt_assigns_to() recurses). Verified: t27c test suite unchanged (1411 passed, same 5 baseline failures — no new regressions). A full regen of the 94 tri-net specs drops from ~8 build errors to 6 (cleared `b`/`cw` undeclared-local errors in gateway/csma_timing). Remaining tri-net errors (6, one class): the t27 bool-as-integer model vs Rust's distinct bool — `return (a >= b);` into a u32 return, `if (!u32_call())`, and a chained `(x == C) == C` — all need type-directed lowering (a comparison yields bool for an if/while/&& context but must be `as u32` in an integer context; logical `!` on an int means `== 0`). Plus one const-fold artifact and one shift-overflow lint. Session error trajectory: 141 -> 78 -> 6. Traceability: needs an issue link before merge to master (L1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ackend A spec calling into another t27 module (`Gf256::gf_mul(...)`) typechecked and the Rust backend emitted `Gf256::gf_mul` verbatim -- but with no import, so it did not compile (the t27 module `Gf256` is the file gf256.rs, wired into the crate as `crate::gf256`). This blocked spec composition (e.g. reed_solomon building on a GF(256) spec), forcing every spec to inline its dependencies. gen_rust now walks the module for `OtherModule::` references and emits `use crate::<snake> as <Module>;` per referenced module (self-modules excluded), mapping the PascalCase t27 name to its snake_case file/module name (Gf256 -> gf256, AdaptiveMcs -> adaptive_mcs). New helpers collect_module_refs() and pascal_to_snake(). Verified: rs_generator.t27 (which calls Gf256::gf_mul/gf_add/gf_pow) now emits `use crate::gf256 as Gf256;` and compiles + passes a scratch test proving the RS generator's roots are field zeros. t27c test suite unchanged (1411 passed, same 5 baseline failures). Depends on both modules being wired into the crate. Traceability: needs an issue link before merge to master (L1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
t27 if-expressions were ternary-only: `if (c) a else b`. The brace form
`if (c) { a } else { b }` -- which matches if-STATEMENTS, function bodies and
every other block in the language, and is what authors naturally write -- failed
to parse. Worse, it failed SILENTLY: in a `let x = if (c) { a } else { b }`
binding the parse error was swallowed by statement-boundary recovery, the whole
binding was dropped, and typecheck still reported OK, so the backend emitted a
function referencing an undeclared local (E0425) with no diagnostic.
parse_if_expr now parses each branch through parse_if_branch, which consumes an
optional `{ expr }` wrapper (single expression) or falls through to a bare
expression. `else if (...)` chains keep working (parse_if_branch -> parse_expr ->
parse_if_expr). The AST is the unchanged ExprIf, so all backends render both
source forms identically -- no backend change.
Verified: braced, else-if-chain and bare forms all parse, typecheck, and gen-rust
to correct Rust that compiles and runs; C/Verilog backends unaffected (Zig has no
if-expr support, pre-existing). Compiler suite 1411 passed / 5 baseline failures
(jwt x3, ternary, trit_stdlib) -- zero regressions.
parse_fn_body drops any statement that fails to parse (recover_to_stmt_boundary)
without recording it, so the body parses "OK", typechecks with 0 errors, and
generates a function missing its dropped if/while/assignment -- and the saved
seal blesses that broken output. Measured: 131 of 504 specs drop >=1 statement,
755 statements total.
Dominant cause is paren-free `if cond {}` / `while cond {}` (parser requires
`if (cond)`); also compound assignments beyond `+=`. Documents the two reverted
fix experiments (fatal-parse breaks 131 specs + a compiler test; syntax-support
changes ~100 specs' output and so is coupled to a corpus reseal) and the
recommended combined path: extend the parser, regenerate, reseal on the
corrected output, land with the pending codegen-clean fixes. Needs maintainer
authorization for the reseal/merge; do not reseal ahead of the syntax fix.
No code change -- finding only. Compiler stays at braces-fix baseline
(1411 passed / 5 baseline failures, 0 parse failures).
) The dominant cause of the 131-spec silent-statement-drop audit (docs/PARSE_SILENT_DROP_AUDIT.md): the parser required `if (cond)` / `while (cond)` and silently dropped paren-free `if cond { }` / `while cond { }` (Zig/Rust style), which ~164 specs write for `if` and ~49 for `while`. A dropped statement left the body missing its conditional/loop while typecheck still reported OK. Two parts: - parse_condition() makes the parentheses optional; a bare condition parses as an expression that stops at the `{` opening the block, used by parse_if_stmt and parse_while_stmt. - a no_struct_literal parser flag, set while reading a bare condition, suppresses `Ident { ... }` struct-literal parsing there (otherwise `while i < n {` parsed `n { ... }` as a struct literal and ate the loop body). Parenthesized conditions are unaffected, so `if (Foo{..}.x) {}` still works. Verified: paren-free `if` and `while` now parse and generate correct Rust (body present); compiler suite 1411 passed / 5 baseline failures (jwt x3, ternary, trit_stdlib) -- zero regressions; corpus suite 0 parse failures, 0 gen failures on all four backends (seal mismatches rise, as expected -- the output is now correct and awaits the coupled reseal). Patch in scratchpad. This is fix #10 toward landing codegen-clean; remaining silent-drop causes are compound assignments and array-of-struct types.
Silent-drop audit cause: a parameter typed `[ClockDomainPower]` (a slice of a struct, no size) generated the malformed Rust `[; (ClockDomainPower) as usize]` and would not compile. In t27_type_to_rust the no-semicolon bracket arm assumed Zig-style `[N]T` and read the element `inside` the brackets as a SIZE, with an empty element after `]`. Fix: when nothing follows `]`, `[T]` is a slice of T -> `Vec<T>`. Zig-style `[N]T` (element after the bracket) and `[]T` (earlier arm) are unchanged. Verified: `[ClockDomainPower]` -> `Vec<ClockDomainPower>`, `[SZ]u8` still -> `[u8; (SZ) as usize]`; compiler suite 1411 passed / 5 baseline failures -- zero regressions; corpus suite 0 parse / 0 gen failures on all backends. Patch in scratchpad. Fix #11 toward landing codegen-clean; the last silent-drop cause is compound assignments (|= *= /= etc., only += is lexed).
…ause) Only `+=` was lexed; every other compound assignment (`result |= x`, `pow3 *= 3`, ...) tokenised as `<op>` then `=`, so parse_expr consumed the operator, hit `=`, and the whole statement was silently dropped (audit cause "Unexpected token: Equals"). - Lexer: one CompoundAssign token for `<op>=`, op in | & ^ - * / %. The `= ` second char means it never shadows `||`, `->`, `//`, `**`, `<=`, etc.; the operator char rides in the lexeme. - Parser: desugar `x <op>= y` to `x = x <op> y` (a plain StmtAssign with a binary RHS), so no backend needs to change. Verified: `r |= b` -> `r = (r | b)`, `r /= 3` -> `r = (r / 3)` (not a comment), `r *= 2` -> `r = (r << 1)` (strength-reduced); the corpus pack_trit's dropped `result |= encoding << bit_pos` now generates. Compiler suite 1411 passed / 5 baseline; corpus suite 0 parse / 0 gen failures on all backends. Patch in scratchpad. Fix #12; this closes the parser-fix phase of the codegen-clean landing (paren-free #10, slice-of-struct #11, compound-assign #12) -- remaining drops are minor (array literals in expressions).
Biggest single silent-drop cause (272 events, 263 in test_* blocks): test/bench
bodies use an assertion form `invariant est_power_mw == 0;`, but parse_body_stmt
had no case for the `invariant` keyword, so it failed, recovery skipped past the
block's closing brace, and it cascaded into the next module-level `invariant`/
`test` ("Unexpected token: KwInvariant"). Every following statement/assertion in
that block was dropped too.
parse_body_stmt now parses `invariant <expr>;` inside a body as a labelled
StmtExpr (kept, not dropped). The top-level `invariant name assert COND`
declaration is parsed on a different path and is unaffected; test/bench bodies are
not code-generated, so the wrapped expression is inert.
Verified: in-body and top-level invariants both parse; power_analysis_tb (had
test_reset_state with the in-body form) typechecks; compiler suite 1411 passed /
5 baseline -- zero regressions. Patch in scratchpad. Fix #13; closes the dominant
test-block cascade. Remaining drops are array literals in array-heavy specs
(sort/hashmap/NN -- needs real array support) and the given/then test DSL.
The 13 codegen/parser fixes on this branch changed generated output, so the saved seals no longer matched. Resealed every spec on the corrected output; suite Seal Verify now 504 passed / 0 failed, 0 mismatches. Precedent: bf1f07e.
Rust requires 'let mut' for locals that are reassigned. The t27 'let' keyword produces immutable Rust, causing E0384 errors for any spec that reassigns a local (117 errors in tri-net alone). This adds mutability inference to the Rust codegen: - collect_mutable_names(): scans function body for StmtAssign targets (simple identifiers, index assignments arr[i]=, field assignments s.f=) - RustCodegen.mut_names: per-function set of names needing mut - Both let-emission sites (gen_fn + gen_rust_stmt) check the set Impact on tri-net: cargo check --lib 141 errors -> 0 errors, 101 tests pass. Over-inference (mut on read-only locals) produces warnings, not errors. phi^2 + phi^-2 = 3
|
Closing as superseded by #1461. Reason: this PR's base is The actual fix (commit Reproducibility numbers (141 → 0 errors, 101 tests pass) that were originally claimed against this PR are cited in #1461 with source SHA Please review and merge #1461 instead. phi^2 + phi^-2 = 3 |
…#1460) (#1461) * feat(codegen): infer let mut from assignment analysis Rust requires 'let mut' for locals that are reassigned. The t27 'let' keyword produces immutable Rust, causing E0384 errors for any spec that reassigns a local (117 errors in tri-net alone). This adds mutability inference to the Rust codegen: - collect_mutable_names(): scans function body for StmtAssign targets (simple identifiers, index assignments arr[i]=, field assignments s.f=) - RustCodegen.mut_names: per-function set of names needing mut - Both let-emission sites (gen_fn + gen_rust_stmt) check the set Reproducibility (from ssdm4 macbook, ветка feat/mut-inference-2026-07-13): cargo check --lib in tri-net: 141 errors -> 0 errors, 101 tests pass. cited SHA: 96d3581 (measurement realm) Over-inference (mut on read-only locals) produces warnings, not errors. Note: cleanly cherry-picked from PR #1460 head onto current master (4832ec6). Discards 249 unrelated commits from stacked branch. Also re-seals FROZEN_HASH per M5 ceremony (compiler.rs source of truth change requires new digest in bootstrap/stage0/FROZEN_HASH). Co-Authored-By: ssdm4 <ssdm4@MacBook-Pro.local> phi^2 + phi^-2 = 3 * docs(NOW): update for mut-inference PR #1461 (Fixes #1463) phi^2 + phi^-2 = 3 --------- Co-authored-by: Perplexity Computer <agent@perplexity.ai> Co-authored-by: SSD DDD <ssdm4@MacBook-Pro.local>
Problem
Rust requires
let mutfor locals that are reassigned. The t27letkeyword produces immutable Rust, causing E0384 errors for any spec that reassigns a local.Impact on tri-net: 117 E0384 errors across 18 spec files.
Fix
Mutability inference in the Rust codegen backend:
collect_mutable_names(): scans function body forStmtAssigntargets — simple identifiers (x = ...), index assignments (arr[i] = ...), field assignments (s.f = ...)RustCodegen.mut_names: per-functionHashSet<String>of names needing mutlet-emission sites (gen_fn+gen_rust_stmt) check the set before emittingletvslet mutMeasured impact (tri-net, real cargo verdict)
Over-inference (
let muton read-only locals) produces warnings, not errors — Rust is lenient about unnecessary mut.Files
bootstrap/src/compiler.rs— +61 linesphi^2 + phi^-2 = 3