Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
d6c07a7
feat(trios-chat): 3-channel mesh chat specs + product design
Jul 7, 2026
0464c4b
feat(trios-chat): full spec suite + product design + build fix
Jul 7, 2026
d4671e4
feat(w13): mesh convergence spec + per-board MAC tools + wave plan
Jul 7, 2026
dc9f4c8
feat(w14): chat protocol spec + wave plan + 77 specs total
Jul 7, 2026
87b4811
feat(w15-w20): 3 more specs + master roadmap (80 specs total)
Jul 7, 2026
3aacad3
feat(w21-w25): complete roadmap + security audit spec (81 specs)
Jul 7, 2026
d2cae65
feat(fpga): synthesizable BPSK TX + AES S-box specs (83 total)
Jul 7, 2026
08f3fec
feat(w20): integration + photo + video specs — 86 specs, 904 tests
Jul 7, 2026
18c641d
feat(tri): unified CLI + Verilog output + 86 specs complete
Jul 7, 2026
51352a8
feat(soul): Article V — multi-board boot procedure (PROVEN recipe)
Jul 7, 2026
00df571
feat(M2): 3-node mesh convergence PASS on ARM hardware
Jul 7, 2026
66bdee2
feat: SOUL.md Article V + tri CLI v3 + experience (ALL bottlenecks so…
Jul 7, 2026
4a2ef1b
feat(soul): Article VIII — boot switch irrelevant for SD boot
Jul 7, 2026
102ba06
docs: W12 full troubleshooting report — 10 problems, solutions, blockers
Jul 7, 2026
2db17ef
docs(soul): Article VIII confirmed — JTAG switch + SD = boots from SD
Jul 7, 2026
e7ea84e
feat(hw): M2 three-board mesh PASS + proven per-board IP recipe (refs…
Jul 8, 2026
13524c8
feat(hw): AD9361 sample-level datapath PASS 3/3 boards (refs #9)
Jul 8, 2026
628690a
feat(soul): Article VIII — Debugging Doctrine (post-mortem of the W12…
Jul 8, 2026
cc13bdf
feat(hw): first over-the-air data — 1 byte broadcast to 2 Ethernet-le…
Jul 8, 2026
13cca19
feat(hw): Channel T PHY over the air — BPSK frames 17/17 + 19/20 erro…
Jul 8, 2026
ea1b9e7
docs(hw): radio mesh + internet-over-air results (refs #9)
Jul 8, 2026
90b85f4
docs(hw): wire-free re-test — internet-over-radio confirmed; board 12…
Jul 8, 2026
cc9a529
docs(hw): board 12 into mesh (RX-cal fix + TX-gain), 3-node no-CSMA c…
Jul 8, 2026
e3fbc7f
docs(hw): multi-frequency sweep + stable wire-free operation (refs #9)
Jul 8, 2026
4f5fc27
docs(hw): CSMA mesh — full 3-node + board 12 internet over radio (ref…
Jul 8, 2026
b934c53
docs(hw): wire-free confirmation — board 12 (no Ethernet) internet ov…
Jul 8, 2026
f1f8534
docs: ranked weak points + decomposed plan (loop iter 1, refs #9)
Jul 8, 2026
4f0c302
docs: mark W2 FEC done (loop iter 3, refs #9)
Jul 8, 2026
f486471
docs(hw): Channel P photo transfer proven byte-identical through full…
Jul 8, 2026
6e770b0
docs: W2b interleaver+FER finding (FEC net-negative, gated) + W3b rea…
Jul 8, 2026
1261fc4
docs: A' Viterbi done; deeper finding — bit-FEC net-negative on modem…
Jul 8, 2026
96f0283
docs(security): B'-wire on-air handshake results + plan update (W3 done)
Jul 8, 2026
460ce2a
docs(fec): A'' soft-decision FER results + competitor delta + refresh…
Jul 8, 2026
f5c113e
docs(modem): phase-tracker results — tail-drift fix, ~100x FER cut, F…
Jul 8, 2026
98836f8
docs(modem): QPSK throughput results + plan (M2 done)
Jul 8, 2026
a35ed25
docs: SNR/link-quality estimator results + C' kernel-block finding
Jul 8, 2026
2075b8a
docs: Channel V (video) results — byte-identical H.264 clip + goodput
Jul 8, 2026
efcbcc7
docs: Channel V real-time streaming results
Jul 8, 2026
24b7384
docs(plan): Channel V video (file + streaming) done, RS keyframe eras…
Jul 8, 2026
da9f2a1
docs: RS key-frame erasure code results — MDS keyframe protection, <1…
Jul 8, 2026
f6c95a5
docs: multi-block RS results — arbitrary keyframe sizes, honest parit…
Jul 8, 2026
7e56f51
docs: adaptive MCS results — link-adaptive rate, +12% goodput at full…
Jul 8, 2026
89ae109
docs: cross-block interleaving results + W8-not-a-gap finding
Jul 8, 2026
ce821a7
docs: M5 self-healing convergence measured (~600ms fast-fail reroute)
Jul 8, 2026
aa25f4b
docs: 16-QAM honest finding — ISI-limited, reliable PHY ceiling is QPSK
Jul 8, 2026
75008c0
docs: e2e integration + timing-refinement finding
Jul 8, 2026
841974d
docs: per-frame adaptive modulation (mode header + auto-detect)
Jul 8, 2026
ef34c4d
docs: over-air SNR feedback closes the adaptive-MCS loop
Jul 8, 2026
bc9c4a7
docs: daemon SNR-feedback wiring done (iter22)
Jul 8, 2026
8d496eb
docs: adversarial review of iter20-21 wire code (4 findings fixed, 1 …
Jul 8, 2026
3325ad7
docs: network-input DoS/robustness audit (7 findings; 5 fixed, 2 defe…
Jul 8, 2026
042f94b
docs: chat-crypto audit (partial - session limit); 1 x3dh finding fixed
Jul 8, 2026
2f151ab
docs: solo review of group.rs + sealed.rs (sound within trust model; …
Jul 8, 2026
a3fbaf8
docs: solo security review of chat-crypto surface (identity, agent, r…
Jul 8, 2026
fd25e22
docs: solo review of padding/suite/pq - completes chat-layer pass (1 …
Jul 8, 2026
d6bd4c2
docs: iter27 — fuzz harness (50k inputs, 0 panics) + competitor deep-…
Jul 8, 2026
9b5fbe0
docs: TUN interface results — IP-over-mesh plumbing (8 tests, end-to-…
Jul 8, 2026
317051a
docs: daemon TUN integration — IP-over-mesh run loop (273 tests, ARM …
Jul 8, 2026
93952b4
docs: initiator anonymity — AnonData frames hide sender on RF (6 tests)
Jul 8, 2026
08237d4
docs: internet-via-mesh gateway (M4) — gateway discovery + default ro…
Jul 8, 2026
fbb3132
docs: multi-agent re-audit finds responder-side UKS in PQXDH (fixed b…
Jul 8, 2026
a59d00d
docs: QoS traffic shaping — priority + reservation scheduler (13 tests)
Jul 8, 2026
b813c67
docs: daemon QoS integration — TRIOS_QOS=1 enables priority schedulin…
Jul 8, 2026
563b2c1
feat(specs): tun + gateway + qos_scheduler + link_budget — 4 new .t27…
Jul 8, 2026
6c8d908
feat(specs): anon + mesh_metrics — 2 new .t27 specs (92 total)
Jul 8, 2026
b0e15e3
feat(specs): csma_timing + nat_traversal — 2 new .t27 specs (94 total)
Jul 8, 2026
a6bbe43
docs: comprehensive integration report — 94 specs, 55 commits, 5 mile…
Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .trinity/experience/w12-marathon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"session": "W12-marathon-20h",
"outcome": "3 boards alive + M1+M2 PASS",
"duration_hours": 20,
"key_learnings": {
"mac_solution": "uEnv.txt ethaddr=02:00:00:00:00:0N changes Linux MAC (U-Boot patches FDT)",
"ip_solution": "uEnv.txt boardargs adds ip= to kernel cmdline (BEFORE ramdisk init)",
"flash_without_reader": "SSH + mount /dev/mmcblk0p1 + cat > uEnv.txt — NO card reader!",
"arp_dance": "arp -d .10 between SSH sessions reaches different physical boards",
"mesh_loopback": "3 meshd on 127.0.0.1 tests full stack on 1 board",
"pipeline": ".t27 -> t27c gen-rust + gen-verilog -> Rust + Verilog"
},
"bottlenecks": ["B1-B10 documented in SOUL.md Article V"],
"tri_cli": "v3, 8 commands, all bottlenecks handled"
}

---

## Session 2 (2026-07-07 continued)

### CRITICAL FINDING: Boot switch irrelevant for SD boot
- P201Mini auto-detects SD card regardless of switch position
- JTAG position + SD card → boots from SD perfectly
- Do NOT tell user to change boot switch for SD boot

### Board 1 death cause
- NOT boot switch (was in JTAG, works fine for SD boot)
- MMU stuck from JTAG experiments (ps7_init, kernel load, MMU patching)
- DAP can examine CPU but SLCR/DDR access fails (MMU translation fault)
- Recovery requires JTAG MMU disable code or Vivado/XSDB tools

### Proven working recipe (FINAL)
1. SD card: vendor BOOT.BIN (2.9MB) + uImage + DTB + ramdisk + uEnv.txt
2. uEnv.txt: ONLY change ethaddr line (unique MAC per board)
3. Boot switch: ANY position (JTAG or QSPI/SD) — doesn't matter
4. SD card inserted BEFORE power applied
5. Cold power cycle (USB unplug → replug)
6. Board boots on 192.168.1.10 with unique MAC

### What does NOT work
- Kuiper BOOT.BIN (4.7MB) — Ethernet doesn't come up (wrong bitstream)
- uEnv.txt boardargs/uenvcmd modification — infinite recursion in U-Boot
- uEnv.txt ethaddr + ipaddr without bootargs ip= — MAC changes but IP stays .10
- Boot switch position changes — irrelevant for SD boot

### FINAL WORKING RECIPE (PROVEN 2026-07-08 00:30 UTC)

1. SD card: vendor BOOT.BIN (2.9MB) + uImage + DTB + ORIGINAL ramdisk + uEnv.txt
2. uEnv.txt: ONLY change ethaddr line (unique MAC per board)
3. Boot switch: JTAG position works! Switch DOES NOT MATTER for SD boot.
4. Do NOT modify ramdisk (mkimage CRC issues)
5. Do NOT use boardargs/uenvcmd (infinite recursion)
6. sshpass needs -o PreferredAuthentications=password
7. Vendor BOOT.BIN = correct PL Ethernet. Kuiper BOOT.BIN = NO Ethernet.
8. SD cards wear out after 10+ cycles. Use FRESH cards.
9. Board 3 PROVEN alive in JTAG mode + SD card inserted:
- MAC 02:00:00:00:00:03 (from uEnv.txt ethaddr)
- Linux 5.10.0 booted from SD
- AD9361 detected
- SSH works
- ip addr add .13 works
155 changes: 121 additions & 34 deletions SOUL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,136 @@ Immutable Document. Amendments require unanimous architectural consent.

## Article I: Language Policy

### Source files MUST be ASCII-only, English identifiers.
- `.t27` specs, `.rs` source, `.v` Verilog — ASCII only
- No Cyrillic, no non-Latin scripts in source files
- Comments and identifiers MUST be English

### Documentation MUST be English.
- All `docs/*.md`, `README.md`, root-level Markdown — English only
Source files MUST be ASCII-only, English identifiers.
Documentation MUST be English.

## Article II: Golden Pipeline Mandate

### The Iron Law
All business logic (crypto, mesh, routing, wire format, signal processing) MUST be defined in `.t27` specification files and generated to Rust via `t27c gen-rust`.

**No hand-written Rust for business logic.** Specs are the single source of truth.

### Pipeline
```
specs/*.t27 → t27c gen-rust → gen/*.rs → src/ (re-exports) → cargo build
```

### Forbidden
- Editing `gen/` output by hand (L2 violation)
- Writing new `.rs` files with business logic without a corresponding `.t27` spec
- Committing specs without `test` or `invariant` blocks (L4 violation)
All business logic in .t27 specs → t27c gen-rust → gen/rust/ → src/
No hand-written Rust for business logic. No .sh/.py files.

## Article III: TDD Mandate

Every `.t27` spec MUST contain at least one of:
- A `test` block with test cases
- An `invariant` block with assertions
- A `bench` block with benchmarks

No exceptions. A spec without tests is a draft, not a specification.
Every .t27 spec MUST contain test or invariant blocks.

## Article IV: Hardware Safety

- NEVER run QSPI register experiments via Linux user-space (causes bus hang, clears POR)
- NEVER connect JTAG to working boards unnecessarily (U-Boot clear_reset_cause clears POR)
- NEVER modify network config on boards with identical MAC (causes ARP collision)
- SD boot is the safe path — it bypasses QSPI POR issues
1. NEVER run QSPI experiments via Linux user-space (bus hang, POR cleared)
2. NEVER connect JTAG to working boards unnecessarily (clear_reset_cause)
3. NEVER delete primary IP via SSH (kills session)
4. NEVER change MAC via ip link set (board loses network)
5. Cold power-cycle only (warm reboot hangs Zynq PS)
6. SD boot is the safe path

## Article V: Identity
## Article V: Multi-Board Boot Procedure (PROVEN)

phi^2 + phi^-2 = 3 is the project anchor. It MUST appear in all constitutional artifacts.
### SD Card Recipe (5 files, FAT32)
```
1. BOOT.BIN — Vendor 2.9MB (ZIP 001 SD-BOOT/BOOT.bin) NOT Kuiper 4.7MB
2. uImage — 4.3MB (ZIP 001 SD-BOOT/)
3. devicetree.dtb — 19KB (ZIP 002 SD-BOOT/)
4. uramdisk.image.gz — 5.6MB (ZIP 002 SD-BOOT/) — ORIGINAL unmodified
5. uEnv.txt — 56 lines — stock + ethaddr changed + bootargs line appended (see below)
```

### Boot Switch Position: DOES NOT MATTER
BootROM auto-detects SD card presence regardless of switch position.
- Switch JTAG + SD inserted = boots from SD (PROVEN 2026-07-08)
- Switch QSPI/SD + SD inserted = boots from SD
- Do NOT tell user to change boot switch for SD boot.

φ² + 1/φ² = 3 | TRINITY
### Per-Board ethaddr
```
Board 1: ethaddr=02:00:00:00:00:01 IP 192.168.1.11
Board 2: ethaddr=02:00:00:00:00:02 IP 192.168.1.12
Board 3: ethaddr=02:00:00:00:00:03 IP 192.168.1.13
```
ethaddr in uEnv.txt DOES change Linux MAC (U-Boot patches device tree).
ipaddr in uEnv.txt does NOT change Linux IP (U-Boot only).
boardargs/uenvcmd in uEnv.txt causes INFINITE RECURSION (do not use).

### Per-Board IP: kernel bootargs (PROVEN 2026-07-08, survives reboot)
Append ONE line to the stock uEnv.txt (no uenvcmd, no boardargs):
```
bootargs=console=ttyPS0,115200n8 root=/dev/ram rw earlyprintk ip=192.168.1.1N::192.168.1.1:255.255.255.0::eth0:off
```
Stock sdboot's bootm picks up ${bootargs} imported from uEnv.txt; the kernel
brings up eth0 at 192.168.1.1N as PRIMARY. Proven on all 3 boards (M1 3/3 +
M2 three-board mesh PASS). Ready-made files: tools/board-configs/uEnv-boardN.txt.

Leftover: stock init still adds factory .10 as SECONDARY each boot. Drop it
after boot: `ip addr del 192.168.1.10/24 dev eth0`. GOTCHA: if .10 is PRIMARY
(no bootargs ip=), deleting it also flushes all secondaries on the subnet
(promote_secondaries=0) — delete .10 FIRST, then add the unique IP.

NEVER let two boards share a MAC on the wire (switch MAC-flap kills sessions);
duplicate SDs with the same ethaddr caused the 2026-07-08 "board dies" mystery.

### Multi-Board IP Separation (runtime fallback, for stock SDs)
1. Boot one board, SSH to .10
2. ip addr add 192.168.1.1N/24 dev eth0 (add secondary, do NOT delete .10)
3. arp -d .10 on Mac
4. Boot next board on .10, SSH, repeat
5. Access boards by .11/.12/.13, never use .10 when multiple connected

### IMPORTANT
- Vendor BOOT.BIN (2.9MB) has correct PL Ethernet bitstream
- Kuiper BOOT.BIN (4.7MB) does NOT bring up PL Ethernet
- Do NOT modify uramdisk.image.gz (mkimage CRC issues, boot fails)
- SD cards wear out after 10+ erase cycles — use fresh cards
- sshpass needs: -o PreferredAuthentications=password (not just -o PubkeyAuthentication=no)

## Article VI: Architecture

Each P201Mini has ONE Ethernet port. Mesh over UDP/Ethernet.
Board 1 = internet gateway. Others relay through mesh.
Self-healing: ETX detects link failure in ~900ms.

## Article VII: Identity

phi^2 + phi^-2 = 3 | TRINITY

## Article VIII: Debugging Doctrine (written in blood, 2026-07-08)

Twenty hours were once spent "fixing" hardware that was never broken.
Every one of the 10 problems in the W12 troubleshooting report was either
self-inflicted (QSPI/JTAG experiments) or a network identity collision
misread as a hardware fault. These laws exist so that never repeats.

1. INDEPENDENT CHANNEL FIRST. Never debug through a signal that lies inside
the failure domain (the broken-ruler error). If the network is the
symptom, the console is the instrument: open UART (FT2232H channel B,
115200, root/analog) as the FIRST command of the session, not the 20th
hour. dmesg + /proc/cmdline dissolve most "mysteries" in minutes.
2. OBSERVABILITY BEFORE MUTATION. No state change while blind. Read logs,
read /proc, read the SD from the running board BEFORE rewriting anything.
3. RTFM BEFORE REVERSE-ENGINEERING. Vendor docs first (User Manual, Boot
Test readme, schematics in ~/Downloads ZIPs), empirics second. Hours of
JTAG archaeology rediscovered facts printed in the manual - some wrongly.
4. ENUMERATE HYPOTHESIS CLASSES. "Appears briefly then dies" has at least
three cause classes: hardware, configuration, NETWORK IDENTITY. Write
all classes down before the first experiment; kill them cheapest-first.
Confirmation bias is the default failure mode, not the exception.
5. IDENTITY BEFORE SHARED MEDIUM. Two devices with the same MAC or IP on
one wire poison EVERY network test - including tests of correct fixes
(a poisoned environment makes correct solutions test as failures).
`grep ethaddr` on every SD costs one second. Label physical media.
6. ONE VARIABLE PER EXPERIMENT. One board, one cable, one console when
diagnosing. Three unlabeled boards + full-subnet scans = pure noise.
7. DESTRUCTIVE TOOLS LAST. JTAG, QSPI pokes, register writes come after
understanding, never before - and NEVER on the last working unit.
Every destructive mistake adds a new fault layer that will be
misattributed to the original bug (the compounding spiral).
8. AFTER A DESTRUCTIVE MISTAKE: STOP AND RE-BASELINE. Do not continue the
original hunt on damaged ground; re-verify what still works first.
9. "PROVEN" REQUIRES REPRODUCTION. One lucky boot is an anecdote. Resolve
documentation contradictions immediately; never append a second truth
next to a first one (Kuiper-vs-vendor BOOT.BIN lived unresolved for
days and corrupted every later session).
10. RUNTIME IS NOT PERSISTENT. `ip addr add` dies at reboot. Every fix
report MUST state whether it survives a power cycle; a "fixed" that
silently reverts guarantees a Sisyphus loop next session.
11. KNOWLEDGE MUST SURVIVE SESSIONS. Before declaring "no access" or
"impossible", search memory and prior docs: the working console recipe
existed for 7 days while sessions debugged blind. Losing a recipe is
losing the board.
48 changes: 23 additions & 25 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,49 @@
// build.rs — auto-regenerate from .t27 specs if any changed
// build.rs — auto-regenerate from .t27 specs if changed
use std::process::Command;
use std::path::Path;

fn main() {
let t27c = "../t27/target/release/t27c";
if !Path::new(t27c).exists() {
return; // t27c not available, skip regen
return;
}

// Check if any spec is newer than its generated output

let specs_dir = Path::new("specs");
let gen_dir = Path::new("gen/rust");

if !specs_dir.exists() || !gen_dir.exists() {
return;
}

if let Ok(entries) = std::fs::read_dir(specs_dir) {
for entry in entries.flatten() {
let spec_path = entry.path();
if spec_path.extension().map_or(false, |e| e == "t27") {
let name = spec_path.file_stem().unwrap();
let gen_path = gen_dir.join(format!("{}.rs", name.to_str().unwrap()));

let needs_regen = !gen_path.exists() || {
let spec_time = entry.metadata().map_or(0, |m| m.modified().ok())
.map_or(0, |t| t.elapsed().map_or(0, |d| d.as_secs()));
let gen_time = std::fs::metadata(&gen_path).map_or(0, |m| m.modified().ok())
.map_or(0, |t| t.elapsed().map_or(0, |d| d.as_secs()));
spec_time < gen_time // spec is newer

let spec_mtime = entry.metadata().ok()
.and_then(|m| m.modified().ok());
let gen_mtime = std::fs::metadata(&gen_path).ok()
.and_then(|m| m.modified().ok());

let needs_regen = match (spec_mtime, gen_mtime) {
(Some(s), Some(g)) => s > g,
(Some(_), None) => true,
_ => false,
};

if needs_regen {
let _ = Command::new(t27c)
.arg("gen-rust")
.arg(&spec_path)
.output()
.map(|o| {
if o.status.success() {
let _ = std::fs::write(&gen_path, &o.stdout);
println!("cargo:warning=Regenerated {}", name.to_str().unwrap());
}
});
if let Ok(o) = Command::new(t27c).arg("gen-rust").arg(&spec_path).output() {
if o.status.success() {
let _ = std::fs::write(&gen_path, &o.stdout);
println!("cargo:warning=Regenerated {}", name.to_str().unwrap());
}
}
}
}
}
}

println!("cargo:rerun-if-changed=specs/");
}
Loading
Loading