34 Commits
Author SHA1 Message Date
twislaandClaude Opus 5.5 4744163683 M3 done: the listening hour heard nothing; a reference node is needed for M4
Outside, on battery, an hour on LongFast with a Capture running: 0
packets, 0 headers. The noise is no lower than at the desk and its peaks
follow the device, so about 15 dB of the floor is the Cardputer's own
(issue #20). With IRC on TLS and the radio listening, 52.6 KB free. One
"Done when" item is half met: Sweep was never tried against a known
transmitter. The card's refused writes are issue #21.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 21:40:30 +02:00
twislaandClaude Opus 5.5 b1de0f8804 M3 step 5: Sweep, the band's signal strength as bars and a waterfall
Tab in the LoRa Scanner sweeps 863-870 MHz in 100 kHz steps (the
strongest of three RSSI readings at each, at 125 kHz), shown as bars with
peak hold over a waterfall, the Sniffer's frequency marked (Q98). The
Sweep pauses the Sniffer and keeps its packets; Tab resumes it (Q99).
Status Bar: SW. The floor, top and peaks are host-tested; `lora sweep
on|off|dump` prints them on the console.

At the desk: about 607 ms a pass, a flat floor at -100 to -102 dBm
(15 dB above the chip's own) and a steady carrier at 863.2 MHz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 20:28:46 +02:00
twislaandClaude Opus 5.5 2fce79aebd M3 step 4: the LoRa Scanner App, Sniffer and Captures
The Sniffer lists packets newest first (time, RSSI, SNR, and for
Meshtastic the sender, receiver and hops), with the clear header and a
hex dump on Enter (Q96); `p` picks an EU868 preset, kept in Settings
(Q95). `c` starts a Capture: pcap with LoRaTap in /captures/lora, its own
Clean-up category, recorded by a small Service so it carries on with the
App closed (Q97, Q100). The Status Bar shows L while listening, bright on
each packet, and CAP while capturing (Q101). StorageService gains raw
appends for binary files. Debug Builds get `lora inject` to test all of
this with no transmitter in range: a Capture made on the device reads
back in TShark field for field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 20:14:58 +02:00
twislaandClaude Opus 5.5 3242475699 M3 step 2: Meshtastic header and presets, pcap with LoRaTap (host-tested)
The 16-byte clear header (hops away, channel hash, relay node), the EU_868
presets and their frequency slots, and the channel hash, all checked
against Meshtastic's source. Captures are pcap with LoRaTap v0, read back
with TShark 4.2.5; packet RSSI is plain dBm, as Wireshark reads it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 18:59:23 +02:00
twislaandClaude Opus 5.5 fed065a6f9 M3 step 1: RadioLib and lora probe
The probe finds the SX1262 (TCXO 1.8 V works) and measures the antenna
path: the Cap's PI4IOE5V6408 at 0x43 must drive P0 high, or the receiver
is deaf (-111.9 dBm flat vs -87 to -94 dBm with P0 high). DIO2 makes no
difference to reception. Receive only; the radio is left asleep.

RadioLib 7.8.1 + probe: +23.6 KB flash, +656 B static RAM (release).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 18:52:38 +02:00
twislaandClaude Opus 5.5 6485f277b5 M3 plan: radio bring-up, receive only (Q89-Q104)
The Radio Service owns the SX1262 and shares the SPI bus with the card;
the LoRa Scanner shows packets (Sniffer) and the band (Sweep). Nothing in
M3 can transmit. Notes and the File Browser move to issues #19 and #3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 18:44:25 +02:00
twislaandClaude Opus 5.5 dafbdaddf6 G1 done: Saved Pages read offline, checked by hand
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 01:49:12 +02:00
twislaandClaude Opus 5.5 fe886e2c1d G1: accept a ~12 KB heap dip during a fetch with IRC connected (Q86 revised)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 01:47:59 +02:00
twislaandClaude Opus 5.5 9ca9a16de8 Gemini: read big pages from the card as you scroll (Q88)
A page larger than memory allows is now windowed instead of cut: one
pass over its file (cache or Saved Page) counts lines, indexes every
64th (offset, and the preformatted state there in bit 31: about 1 KB
for a 1 MB page) and loads the first window. Scrolling near either end
reads the next or previous window on the Gemini task; the line on top
of the screen stays put, the scrollbar follows the whole page, and Tab
at a window's edge pages on instead of wrapping. Window budgets count
the memory the old window gives back.

Display pages alternate between two cache files so the one on screen
is never overwritten by the next fetch; jobs use a third.

On the device, Cosmos with IRC connected: 226 of 419 lines at first,
then lines 192-419, back to 64 and 0 while scrolling. `key space` added
to the console's key command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 01:44:01 +02:00
twislaandClaude Opus 5.5 fd306d5013 G1 steps 5-6: input prompts, bookmarks, downloads, Saved Pages
Everything touching the network or the card is a job on the Gemini
task (a missing card can block 5 s, past the main loop's watchdog):
about:start is composed from /gemini/bookmarks.gmi and the Saved Pages
(by capsule, newest first); file:// opens a Saved Page; s, S, r, d, b
and downloads report one line to the URL bar, S with progress Toasts.

A Saved Page is the cached body with a first line "> Saved from <url>
on <date>": it shows as a quote and gives relative links their base;
inside one, links to other Saved Pages open the saved copy, others go
online or say "Not saved, and offline". Input prompts (11 masked)
request the same URL with the answer as its query.

Fixed on the way: re-wrapping indented lines rebuilt the text from its
rows, inserting a space where a long word had been cut; refreshing
loaded the whole Saved Page next to a TLS connection (heap down to 436
bytes), now it reads one line and every fetch checks the 55 KB floor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 01:24:29 +02:00
twislaandClaude Opus 5.5 7b8d9391a4 G1 step 3: the Gemini fetcher (TOFU, redirects, floors, pages via the card)
GeminiService fetches on a short-lived task and hands the App a
GeminiPage: header, the body as lines in 4 KB chunks (TextBuffer: no
large block, no doubling copies), the final URL after up to 5
redirects. Certificates are pinned on first use per host and port; a
change comes back as its own outcome with both fingerprints. No fetch
starts below 55 KB free (Q86).

With a card, the body streams to /gemini/cache/page.gmi in 1 KB pieces
while the connection is open, then loads into RAM once its memory is
back (Q87); StorageService::runAndWait (moved from the Debug Console)
keeps every card access on the storage task. Without a card: RAM, with
the steady and transient floors.

Measured with IRC connected: Cosmos (31.6 KB) went from 4.6 KB to the
whole page on the card and 20 KB on screen; lowest free heap 19.5 KB
in transfer, 43 KB once loaded. `gemini get` and `gemini trust` on the
console. 14 Gemini tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 00:58:01 +02:00
twislaandClaude Opus 5.5 babb1d114e G1 step 2: Gemini parsers (host-tested), and the memory decision (Q86)
lib/gemini: URLs split per RFC 3986 appendix B and resolved per section
5.2 (all 32 reference examples of 5.4 pass, with gemini:// for http://),
the request form (no fragment, lower-case host, never an empty path),
query encoding for input prompts, Saved Page paths; the response header
(status, category, MIME type and parameters, 1024-byte meta limit);
gemtext's line types; and display text for the Latin-1 fonts. 12 tests.

Q86, decided after step 1: free heap stays above 40 KB in steady state
and 20 KB during a handshake; a fetch won't start below 55 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 00:29:55 +02:00
twislaandClaude Opus 5.5 2d384f5cff G1 step 1: gemini get, and two TLS connections measured
`gemini get <url>` fetches on a short-lived task (a handshake would trip
the main loop's watchdog; an idle client should cost no stack) and
reports the header, size, certificate fingerprint and heap. First page:
geminiprotocol.net, 20 text/gemini, 1,184 bytes in 0.7-1.1 s.

With IRC connected over TLS, a fetch dips to about 24 KB free during its
handshake, about 36-40 KB after it; nothing leaks. Antenna is down, so
the default aggregator becomes Cosmos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 00:27:05 +02:00
twislaandClaude Opus 5.5 dc9b0e14ce G1 design: Gemini client plan, glossary (Capsule, Saved Page)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-05 00:18:01 +02:00
twislaandClaude Opus 5.5 fff28af961 Rebuild the framework with smaller TLS buffers (ADR 0006)
custom_sdkconfig makes pioarduino regenerate the ESP-IDF libraries:
asymmetric TLS buffers (16 KB in, 4 KB out) and dynamic buffers that
free handshake-only data once connected. The rebuild also follows the
board: no PSRAM, 8 MB flash. The project now carries the partition
table the hybrid build needs (identical to the framework's: the app
slots must not move under updates over the air). Generated files are
ignored.

Debug Build, GNSS on, IRC on TLS: 78 KB free and a 59 KB low (M2 began
at 31 KB and 12.6 KB; the floor is 40 KB). The full stress set passes
on it: refused installs over Wi-Fi and from SD, put/get, screenshot,
core dump decode, Probation; under all of it at once, the low is 46 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:32:29 +02:00
twislaandClaude Opus 5.5 4e439410cd Stop IRC by hand; remove mDNS
IrcService::disconnect() stops the session from any state: QUIT if
connected, then no more retries. /quit goes through it (before, it only
stopped a connected session; while waiting for Wi-Fi or retrying it did
nothing), and so does the new `irc stop` command. Stopped by hand,
opening the IRC App no longer reconnects; typing a line does.

mDNS is gone: it never crossed the dev box's routed network, and it
cost about 7.5 KB of RAM. Pushes go to the IP shown in Settings ->
Firmware, which drops its Name row. OTA Q54 records the change.

On the device, Debug Build: 105.6 KB free with Wi-Fi (was 98); with IRC
on TLS 54 KB free (was 46); after `irc stop`, back to 99 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:17:48 +02:00
twislaandClaude Opus 5.5 db265fb757 Trim task stacks and buffers by measurement: +15 KB with IRC up
Peak stack use was measured through each task's worst case (an
ECDSA-checked install over Wi-Fi and from SD, get/put, a core dump
fetch, an IRC TLS handshake); stacks are now peak plus about 2 KB: loop
8 -> 6 KB, update 8 -> 5, storage 10 -> 6, irc 8 -> 6. The Debug Build's
console ring goes 6 -> 4 KB, serial TX 2 -> 1 KB, the GNSS UART buffer
1 KB -> 512 B.

On the device, with IRC on TLS: 46 KB free (was 31), an 18 KB low (was
9.4). The re-run of every worst case left at least 1.6 KB of stack free
in each task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:02:35 +02:00
twislaandClaude Opus 5.5 578a39d3c1 M2: record cold-start time to first fix and the heap measurement
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 22:52:07 +02:00
twislaandClaude Opus 5.5 988253ef02 M2 step 3: GNSS Service, with standby and the clock from a Fix
The GNSS Service reads the receiver (UART 15/13, 115200, 1 KB buffer)
from its 50 ms tick and feeds the NMEA parser. With a Fix it sets the
clock (GNSS is the most trusted TimeSource) and refreshes it every 10
min. Settings gets GNSS On/Off and the coordinate format (Q64).

Off puts the receiver in standby with $PCAS12,65535, renewed hourly; On
wakes it with a hot start, $PCAS10,0. Both measured on the device: the
output stops within a second, and a command wakes it within a second.

Commands: gnss status (Fix, satellites per constellation, bytes and
sentences), gnss nmea on|off, gnss send <sentence>, gnss restart. The
probe is gone; never drive GPIO 15 (the receiver's output): the first
probe's swapped-pin attempt silenced it until a power cycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 22:26:53 +02:00
twislaandClaude Opus 5.5 f812c62a3d M2 step 1: gnss probe finds the receiver on RX 15 / TX 13 at 115200
A temporary `gnss probe` command listens on both pin orders at 115200
and 9600. Only RX 15 / TX 13 at 115200 carries NMEA, as Meshtastic's
board file says; M5Stack's GPIO 8/9 are the keyboard's I2C bus. The
output format (NMEA 4.10, GSA system IDs, GSV per signal) is recorded in
docs/milestones/M2.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 22:05:01 +02:00
twislaandClaude Opus 5.5 9c30d47982 M2 design: GNSS plan, glossary (GNSS Service, Fix, Track), ADR 0001 note
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 22:00:09 +02:00
twislaandClaude Opus 5.5 388e847cd4 Debug Console: files, screenshots and Update from SD over Wi-Fi
New commands everywhere: ls, rm and install <path> (Update from SD
without the Firmware page), all as Storage Service jobs. In Debug
Builds the console task answers get and put (one storage job per
transfer, file kept open, TCP flow control: about 300 KB/s, against
55 KB/s over serial) and screenshot (the RGB332 frame the UI composes
into). rdbg.py turns those into files and PNGs.

A failed put closes the connection: the rest of the file had been
parsed as commands. Card writes are retried 3 times after closing,
truncating to the last good byte and reopening, since FATFS keeps a
file in error after one failed write (seen once at 1.3 MB on this card).
onStorage() decides with one compare-and-swap whether the job or the
timeout wins, so an abandoned job can't touch a returned stack frame.

Verified on the device: screenshot; a get round trip byte-identical;
4 puts in a row; a tampered .ota refused by install; a good one put,
installed from SD, confirmed on Probation. The retry path itself has
not fired since it was added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 03:18:23 +02:00
twislaandClaude Opus 5.5 14ff13f634 Safe Mode, crash reports, and a watched main loop
Every build now records at boot which version runs and, after a crash
restart, which one crashed (even across a Rollback). The core dump
summary (task, PC, reason, backtrace) is printed and raised as a
Notification; `crash` shows it later. After 3 crash restarts in a row
the firmware starts in Safe Mode: clock, Wi-Fi, Update Service and Debug
Console only (SafeMode, 2 host tests). A normal restart or a minute up
resets the count.

The main loop is now on the task watchdog (enableLoopWDT): Arduino only
watched core 0's idle task, so a stuck loop hung the device for good.
The Update Service restarts into an installed update by itself if the
main loop hasn't after 90 s.

Debug Builds: `coredump get` and `reset` are answered by the console's
own task; rdbg.py crash decodes the backtrace and rdbg.py coredump runs
esp-coredump, against ELFs archived by version and digest in .pio/elves.

The StorageService mutex is now made in the constructor: Safe Mode never
starts that Service, and `info` crashed on the null mutex, 29 times in a
row before the fix was pushed into Safe Mode over Wi-Fi.

Verified on the device: crash report and full core dump decoded over
Wi-Fi; Safe Mode at exactly 3 crashes, left by `reboot`; a hung loop
caught by the watchdog in 5 s; `reset` from the console task. ADR 0005.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 03:07:56 +02:00
twislaandClaude Opus 5.5 0fb7f4e9d5 Debug Builds: the console over Wi-Fi (Debug Console, TCP 2323)
cardputer-adv-debug (-DRORO_DEBUG, version +debug) adds a Debug Console:
after a token line, a client gets the last 6 KB of console output, live
lines (ESP-IDF logs included) and the serial commands. The socket task
only queues lines; the main loop runs them. Release builds compile none
of it. The token lives in ~/.config/roro9stack/debug-token, created by
_docker.sh and passed into the container.

All output now goes through `console`, which never waits for USB: a host
that was attached but not reading stalled the main loop up to 2 s per
line. New commands everywhere: info (slots with their versions from NVS,
since the framework stamps its own into each image), tasks, reboot,
boot other, log level, help. scripts/rdbg.py is the client; flash.sh
--debug builds it; CI builds both variants. ADR 0004.

Verified on the device: USB-flashed, then updated over Wi-Fi to a Debug
Build that confirmed on Probation; both slots hold Debug Builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 02:44:14 +02:00
twislaandClaude Opus 5.5 8ec4e9e449 ADR 0003: the bootloader does roll back; Arduino had validated the image
Verified on the device: a crashing update pushed over Wi-Fi died once,
and the next boot was the previous firmware, with the "Update ... failed"
Toast. bootGuard() stays as a second line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 02:15:38 +02:00
twislaandClaude Opus 5.5 45542dcbff OTA: the firmware rolls itself back; the bootloader doesn't
A deliberately crashing update looped forever on the device: the
prebuilt bootloader ignores ESP_OTA_IMG_PENDING_VERIFY despite the
app-side rollback config. UpdateService::bootGuard() now runs first in
setup(): it counts starts on Probation in NVS and, on the second
unconfirmed start, marks the image invalid and reboots into the
previous one. Confirming (or the Wi-Fi rollback) resets the counter.
ADR 0003 records the limit: a crash in the first milliseconds still
needs USB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 21:48:39 +02:00
twislaandClaude Opus 5.5 e9efbcca4d OTA design: glossary, ADR 0003 (own signature check), plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 21:27:04 +02:00
twislaandClaude Opus 4.8 28d025fb13 Wi-Fi Tools: scan logging to CSV, plus sort and filter
- lib/wifi (host-tested): scan_log (CSV field quoting, header/row, a
  once-per-interval throttle) and network_list_view (sort by signal /
  channel / name, filter open-only / hide-hidden / strong-only)
- ScanEntry moved to lib/wifi so both are testable on the PC
- Wi-Fi Tools networks view: s sort, o/h/w filters, l toggles logging
  to /wifi/scans/<date>.csv (header + one row per AP per logged scan,
  throttled 30 s, needs the clock for timestamps); foreground-only
- Wi-Fi scan logs replace probe-request logs as a clean-up category
- Serial: cat <path>, and key <char> injects a character

Verified on the device: CSV written with header, timestamps, BSSID,
channel, RSSI, security and SSID; hidden networks marked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 20:52:43 +02:00
twislaandClaude Opus 5.5 e7da9bd388 Close M1: record outcome and carried-over items
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 18:42:24 +02:00
twislaandClaude Opus 5.5 4cd6a63498 M1 step 6: Wi-Fi Tools from ordinary scans
- lib/wifi (host-tested): channel occupancy (neighbour spill, signal
  weighting, quietest of 1/6/11) and a signal tracker (history, lost
  detection, click interval)
- WifiService: scan results carry BSSID, channel and security; a scan
  can target one channel for quick tracker refreshes; endListScans()
  turns the radio back off when Wi-Fi is disabled
- Wi-Fi Tools App: networks nearby, channel occupancy bars, signal
  tracker with clicks (m to mute)
- M1 plan: Monitoring-mode views and captures deferred

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 16:33:11 +02:00
twislaandClaude Opus 5.5 0c470922bc M1 design: glossary for Wi-Fi/IRC, milestone plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 14:15:38 +02:00
twislaandClaude Opus 5.5 cf2e7968b9 Close M0: record outcome and carried-over items
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 14:02:13 +02:00
twislaandClaude Opus 5.5 fe3be01eec Add M0 milestone plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-01 18:29:07 +02:00
twislaandClaude Opus 5.5 87b6b845fb Add domain glossary and initial architecture decisions
CONTEXT.md captures the roro9stack domain language (Apps, Services,
Mesh Service, Nodes, Channels, Storage rules). ADR 0001 records building
our own firmware that speaks Meshtastic instead of forking it; ADR 0002
records an own widget kit on M5GFX instead of LVGL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 18:25:36 +02:00