Skip to content

Commit 749f6ae

Browse files
committed
deploy: 4685618
0 parents  commit 749f6ae

472 files changed

Lines changed: 141680 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.nojekyll

Whitespace-only changes.

api-docs/ADR-110-BRANCH-STATE.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# ADR-110 — Branch state (as of 2026-05-23, iter 22)
2+
3+
Reference card for anyone collaborating on or near the ADR-110 work. The /loop SOTA sprint that closed the firmware-side substrate ran into multiple cross-branch checkout incidents (see iter 17-19); this page exists so the next collaborator doesn't have to re-derive the layout from `git log`.
4+
5+
## Branch ownership
6+
7+
| Branch | Owner | What it carries | Don't merge from |
8+
|---|---|---|---|
9+
| `main` | shared | shipped release line ||
10+
| `adr-110-esp32c6` | ADR-110 / C6 firmware substrate | Everything described in `WITNESS-LOG-110 §A0.x` (4 firmware tags v0.6.7 → v0.7.0, Python + Rust decoders, sensing-server wire, mesh-aligned timestamp recovery, fps EMA, cross-language conformance gate) | Don't accidentally land `feat/adr-115-ha-mqtt-matter` work here uncommitted |
11+
| `feat/adr-115-ha-mqtt-matter` | ADR-115 / HA-DISCO + HA-FABRIC + HA-MIND | MQTT publisher (`rumqttc`), Matter Bridge, semantic automation primitives, related Cargo features + CLI flags | Don't accidentally land ADR-110 `wifi-densepose-hardware` dep mods here |
12+
13+
## Files each branch touches
14+
15+
### `adr-110-esp32c6` — primary modifications
16+
17+
```
18+
firmware/esp32-csi-node/version.txt # bumped 0.6.6 → 0.7.0
19+
firmware/esp32-csi-node/main/c6_*.{c,h} # LP-core, TWT, timesync, soft-AP HE, ESP-NOW sync
20+
firmware/esp32-csi-node/main/lp_core/main.c # real LP-core polling program
21+
firmware/esp32-csi-node/main/csi_collector.c # byte 19 bit 4 OR-fix; sync packet emit
22+
firmware/esp32-csi-node/main/Kconfig.projbuild # C6_* knobs
23+
firmware/esp32-csi-node/main/CMakeLists.txt # ulp_embed_binary
24+
firmware/esp32-csi-node/sdkconfig.defaults.esp32c6 # C6 overlay
25+
26+
archive/v1/src/hardware/csi_extractor.py # SyncPacketParser + SyncPacket dataclass
27+
archive/v1/tests/unit/test_esp32_binary_parser.py # TestSyncPacketParser (7 tests)
28+
29+
v2/crates/wifi-densepose-hardware/src/sync_packet.rs # new module (15 tests)
30+
v2/crates/wifi-densepose-hardware/src/lib.rs # re-exports
31+
v2/crates/wifi-densepose-sensing-server/Cargo.toml # ONLY adds wifi-densepose-hardware path dep
32+
v2/crates/wifi-densepose-sensing-server/src/main.rs # NodeState::{latest_sync, csi_fps_ema,
33+
# mesh_aligned_us_for_csi_frame,
34+
# observe_csi_frame_arrival}
35+
# udp_receiver_task magic dispatch
36+
# fps_ema_tests module (4 tests)
37+
38+
docs/adr/ADR-110-esp32-c6-firmware-extension.md # 670 → ~750 lines (P10 + sprint summary)
39+
docs/WITNESS-LOG-110.md # 13 §A0.x entries
40+
docs/ADR-110-REVIEW-GUIDE.md # reviewer one-pager
41+
docs/ADR-110-BRANCH-STATE.md # ← this file
42+
```
43+
44+
### `feat/adr-115-ha-mqtt-matter` — primary modifications
45+
46+
```
47+
docs/adr/ADR-115-home-assistant-integration.md # the design
48+
v2/crates/wifi-densepose-sensing-server/Cargo.toml # rumqttc dep + [features] block
49+
v2/crates/wifi-densepose-sensing-server/src/cli.rs # --mqtt / --matter / --semantic flags
50+
```
51+
52+
## Known overlap points (handle with care)
53+
54+
Both branches touch `v2/crates/wifi-densepose-sensing-server/Cargo.toml` and `src/main.rs`. The conflict surface is **disjoint by section**:
55+
56+
| File | ADR-110 region | ADR-115 region |
57+
|---|---|---|
58+
| `Cargo.toml` | `[dependencies]``wifi-densepose-hardware = { path = "../wifi-densepose-hardware" }` near the existing `wifi-densepose-signal` line | `[dependencies]``rumqttc` block below + `[features]` block at end |
59+
| `main.rs` | `NodeState` fields + `impl NodeState` helpers + `update_csi_fps_ema` free fn + `fps_ema_tests` module + `udp_receiver_task` magic dispatch | (TBD per ADR-115 P-plan) |
60+
61+
A merge between the two branches should be **clean line-merge** since the regions don't overlap. If git ever reports a real conflict in either of these files, that means one branch has drifted into the other's region — investigate before resolving blindly.
62+
63+
## Quick test commands (verify either branch is sane)
64+
65+
```bash
66+
# Rust workspace (run from v2/)
67+
cd v2
68+
cargo test --workspace --no-default-features --lib # 1437 tests at iter 22, 0 failures
69+
70+
# Python ADR-110 host decoder (from repo root)
71+
python -m pytest archive/v1/tests/unit/test_esp32_binary_parser.py::TestSyncPacketParser -v
72+
73+
# Cross-language wire-format gate (the iter 21 pin)
74+
cargo test -p wifi-densepose-hardware --no-default-features --lib sync_packet::tests::canonical_wire_bytes_match_python_decoder
75+
python -m pytest archive/v1/tests/unit/test_esp32_binary_parser.py::TestSyncPacketParser::test_canonical_wire_bytes_match_rust_decoder -v
76+
```
77+
78+
If either side of the canonical-wire-bytes pair fails alone, the OTHER decoder has drifted from the wire format — investigate that decoder first, not the failing test.
79+
80+
## Future-proofing
81+
82+
- When the ADR-115 agent ships `feat/adr-115-ha-mqtt-matter` to main and ADR-110 also ships, merge `main` into `adr-110-esp32c6` (or vice versa) and re-run both test suites. The disjoint-region structure above should make the merge a no-conflict fast-forward.
83+
- When a third agent picks up either ADR, point them at this file before they start editing shared files.
84+
- If a /loop drives autonomous iterations and hits a cross-branch checkout, the recovery procedure is in iter 18's commit message (`2997165bc`) — stash on the foreign branch, `git checkout` home, replay the iter locally.
85+
86+
## Lessons for `/loop` and `/loop-worker` future runs
87+
88+
Captured after the 38-iter ADR-110 SOTA sprint (`/loop 5m until sota. and ultra optmized`):
89+
90+
1. **Always verify the current branch at the start of each iter** — when a /loop fires every 5 minutes and another agent is active on a sibling branch, the working tree can flip without your action. Run `git branch --show-current` as the first line of every iter; if it isn't what you expect, stash and switch back BEFORE editing. We burned ~30 min in iter 17-19 recovering from two silent branch flips.
91+
2. **Don't `git add <file>` blindly after a branch switch** — the file may have inherited changes from the foreign branch (uncommitted work that came along on checkout). Always `git diff --cached` before `git commit`. We accidentally absorbed ADR-115's Cargo.toml/cli.rs work into ADR-110's iter-18 commit; required a follow-up revert commit (`ca2059b07`) and stash dance.
92+
3. **Sibling-region edits in shared files** — when two branches both touch `v2/crates/wifi-densepose-sensing-server/Cargo.toml` or `src/main.rs`, agree on which `[section]` or struct each owns. Document the regions in this file (see Known overlap points). Merges then stay clean line-merge fast-forwards instead of needing conflict resolution.
93+
4. **Extract pure helpers before committing inline mutations** — iter 30 (`sync_snapshot`), iter 32 (`apply_sync_packet`), iter 37 (`fleet_role_counts`) all converted inline state-changes into named, free, testable functions. Each saved 4+ inline duplications and let the helper be tested without spinning up axum / tokio. Bake this into every iter's plan: *"what's the smallest helper I can extract here?"*
94+
5. **Cross-language wire-format gates** — when shipping a protocol decoder in both Python and Rust, pin the SAME canonical byte string in BOTH test suites (iter 21 pattern). One side drifting fires exactly one named test on exactly the drifted decoder. Don't wait until "later" — add the pin in the iter that ships the second language.
95+
6. **Helper tests > integration tests when state is heavy**`AppStateInner` has too many fields to construct in a test. Instead of fighting it, extract per-field logic into pure helpers (iter 30 sync_snapshot pattern). Tests target the helpers, the handler glue stays thin and trivially correct.
96+
7. **Local stub files lag firmware additions**`firmware/esp32-csi-node/test/stubs/esp_stubs.c` doesn't get rebuilt with the firmware proper, so a new symbol added to a `*.h` won't surface as a fuzz-target link error until CI runs. Iter 38 caught `c6_sync_espnow_is_valid` this way. **Whenever you add a function whose declaration is reachable from `csi_collector.c`, also add a stub** in the same commit.
97+
8. **Cron-based /loop accumulates work across irreversible checkpoints (tags, releases, PR ready)** — once you cut a tag or mark a PR ready, the cost of reverting is much higher than a code edit. Save those for iters when you have surplus confidence (full local test suite green, CI from previous iter green). Iter 12 (v0.7.0 cut) and iter 38 (PR ready) were the right shape: only happened after iter 6 / iter 37 evidence had landed.

api-docs/ADR-110-REVIEW-GUIDE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# ADR-110 review guide
2+
3+
This is the **one-pager** for reviewers of the `adr-110-esp32c6` branch / draft PR. The canonical record is [`docs/WITNESS-LOG-110.md`](WITNESS-LOG-110.md); this guide is just a faster on-ramp.
4+
5+
## What this branch ships
6+
7+
A dual-target build for `firmware/esp32-csi-node`: same source tree compiles for `esp32s3` (existing production) and `esp32c6` (new research target with Wi-Fi 6 / 802.15.4 / TWT / LP-core). Every C6-only module is `#ifdef CONFIG_IDF_TARGET_ESP32C6` gated, so the S3 build path is byte-identical to before.
8+
9+
## Five-minute reviewer tour
10+
11+
1. **Read the ADR**: [`docs/adr/ADR-110-esp32-c6-firmware-extension.md`](adr/ADR-110-esp32-c6-firmware-extension.md) — design, phases, trade-offs.
12+
2. **Read the witness**: [`docs/WITNESS-LOG-110.md`](WITNESS-LOG-110.md) — 4 sections (A = empirically verified, B = architectural-but-not-measured, C = bugs fixed, D = bugs found but not yet fixed, D-workaround = ESP-NOW pivot).
13+
3. **Skim the new firmware modules**: `firmware/esp32-csi-node/main/c6_{twt,timesync,lp_core,sync_espnow}.{h,c}`.
14+
4. **Skim the new host decoders + tests**:
15+
- Rust: `v2/crates/wifi-densepose-hardware/src/{csi_frame,esp32_parser}.rs` (search for `PpduType`, `Adr018Flags`, `adr110_*` test names)
16+
- Python: `archive/v1/src/hardware/csi_extractor.py` + `archive/v1/tests/unit/test_esp32_binary_parser.py` (search for `TestAdr110ByteEncoding`)
17+
5. **Glance at CI**: `firmware-ci.yml` `c6-4mb` matrix row runs the C6 build AND the host unit tests on Ubuntu — both green throughout this branch.
18+
19+
## Empirical scorecard (what's actually measured)
20+
21+
| Dimension | Status |
22+
|---|---|
23+
| C6 build + boot + dual-target | ✅ verified on 3 boards (COM6/COM9/COM12), CI matrix green, S3 regression green |
24+
| HE-LTF wire format (ADR-018 byte 18-19) | ✅ verified end-to-end across firmware / Rust / Python (17 unit tests) |
25+
| HE-LTF live capture | ⏸ blocked — need 11ax AP (only 11n AP on bench) |
26+
| TWT graceful NACK | ✅ verified live — `c6_twt: iTWT setup failed: ESP_ERR_INVALID_ARG` captured + handled |
27+
| TWT cadence determinism | ⏸ blocked — same 11ax AP gap |
28+
| ESP-NOW transport TX + stability | ✅ verified — 120 s + 300 s soaks, 4102 cumulative transmits, 0 failures |
29+
| ESP-NOW cross-board RX | ⏸ blocked — 3 of 4 boards dropped USB enumeration mid-experiment |
30+
| Raw 802.15.4 cross-node sync | ❌ broken — IDF v5.4 driver bug, 5 hypotheses tested + rejected; ESP-NOW workaround in place |
31+
| 5 µA hibernation | ⏸ blocked — datasheet number, need INA / Joulescope to measure |
32+
| Witness bundle regenerable + clean | ✅ 6/7 PASS (1 fail is pre-existing Python proof env issue unrelated to ADR-110), all hashes recorded, secret-redacted |
33+
34+
## Honest verdict
35+
36+
Protocol layer + transport substrate are bullet-proofed. **None of the four headline SOTA dimensions is empirically measured** — each is blocked on hardware the bench doesn't have. Each blocker is documented in `WITNESS-LOG-110.md` §B with the exact instrument needed to unblock it. **This branch is the foundation to build measurement on, not the measurement itself.**
37+
38+
The five concrete bugs found and fixed during the work (MAC/EUI double-FFFE, dual `wifi_pkt_rx_ctrl_t` struct variants, LED GPIO 38 on C6, TWT INVALID_ARG propagation, witness bundle secret leak) are independently real and useful regardless of how the SOTA story lands.
39+
40+
## Security note for the operator (not the reviewer)
41+
42+
The witness bundle's Python proof step was leaking `.env` contents into the bundled log via Pydantic validation error dumps. Bundle was nuked before push, and `scripts/redact-secrets.py` filter was added (commit `f8a2e3695`). **The previously-exposed Docker Hub + PI-cluster tokens should be rotated** — they appeared in local session logs even though they never reached `origin`.
43+
44+
## Commits on this branch (chronological)
45+
46+
| # | SHA prefix | What |
47+
|---|---|---|
48+
| 1 | `f23e34e` | Initial ADR-110 firmware + ADR + tests + docs + witness scaffolding |
49+
| 2 | `6652384` | TWT INVALID_ARG graceful + diagnostic counters |
50+
| 3 | `4c39e28` | PAN-match + 4-experiment D1 record |
51+
| 4 | `f8a2e36` | **SECURITY**: witness bundle secret redaction |
52+
| 5 | `88be283` | ESP-NOW transport (D1 workaround) |
53+
| 6 | `3959fab` | Rust host decoder + 6 unit tests |
54+
| 7 | `8eaa92c` | Python host decoder + 5 unit tests |
55+
| 8 | `b808a63` | 120 s ESP-NOW soak witness |
56+
| 9 | `89972c0` | CHANGELOG expanded |
57+
| 10 | `fc75a8a` | Fuzz harness extended for byte 18-19 |
58+
| 11 | `9de34ba` | ADR-110 indexed in docs/adr/README.md |
59+
| 12 | `553b07d` | README C6 row tightened (claim → wire-format-ready) |
60+
| 13 | `e255b7d` | firmware/README acknowledges S3+C6 |
61+
| 14 | `9a46fc8` | 300 s ESP-NOW soak witness (2.5× sample) |
62+
| 15 | _(this commit)_ | This review guide |

0 commit comments

Comments
 (0)