Skip to content

feat(cli): mothc run + devices — the flutter-run loop - #17

Merged
shbmx merged 3 commits into
mainfrom
feat/mothc-run
Aug 15, 2026
Merged

feat(cli): mothc run + devices — the flutter-run loop#17
shbmx merged 3 commits into
mainfrom
feat/mothc-run

Conversation

@shbmx

@shbmx shbmx commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • mothc run [app.dart] [-d DEVICE] — compile, deploy, stay attached: program output streams, r = hot restart (recompile + push; 37ms measured on the sim, ~150ms expected on serial), h help, q quit. Compile errors during r keep the session alive with the old program running.
  • mothc devices — boards (serial candidates, never probed: probing resets boards) + the simulator when built.
  • Selection: -d exact-or-unique-substring; one board auto-selects; simulator fallback; ambiguity refuses with the list.
  • Board sessions own the serial port end to end — restarts push through the same open console, no reopen resets. A display filter strips triple-sent verdict frames from the streamed console (split-frame and innocent-"MPH" cases pinned).
  • Honest naming: hot restart, "state resets" in the prompt — reload is a roadmap word.

Test plan

  • run_test.dart: 10 tests over selection rules and the display filter.
  • Scripted sim E2E: launch → stream → r → "Restarted in 37ms" with fresh state → q → exit 0.
  • make test fully green; board verification pending a USB re-seat (transport primitives are the serial-suite-pinned ones).

shbmx added 3 commits August 15, 2026 17:44
`mothc run [app.dart] [-d DEVICE]` compiles, puts the program on a
device, and stays attached streaming its output. Device selection is
flutter run's: -d wins (exact id or unique substring, so the /dev/cu.
spelling is optional), one connected board auto-selects, no board
falls back to the simulator when it is built. `mothc devices` lists
both kinds; listing never probes a port, because probing resets most
dev boards and "listing devices rebooted my board" is a terrible
surprise.

Attached keys: r recompiles and pushes — measured 37ms to a restarted
program on the simulator — h help, q quit. It is named a hot RESTART
throughout, because state genuinely resets; borrowing Flutter's
"reload" would promise state preservation moth does not have yet. A
compile error during r is shown and the session stays attached with
the old program still running, exactly like flutter run.

On a board the attach session owns the serial port for its whole
life: restarts push through the same open port the console streams
from, so there is no port contention and no reset-on-reopen between
edits. The board sends each verdict three times on the shared
console; a display filter strips the frames from what the user sees
without eating innocent "MPH" text or frames split across reads —
both pinned in run_test.dart along with the selection rules.

Terminal raw mode is entered defensively: hasTerminal can report
true where the mode getters throw (backgrounded shells, CI), and a
session that cannot read keys still streams.

Sim E2E scripted end to end: launch, stream, r -> Restarted in 37ms
with fresh state, q -> clean exit. Board run pending the USB re-seat;
the transport primitives it uses are the ones the serial suite pins.
SIGTERM/SIGHUP now restore the terminal (and close the port / kill the
simulator child) before exiting — finally does not run on signal death,
and a raw terminal is the one failure the user cannot fix with a
keystroke in a session built on them. flutter run installs handlers
for exactly this; now so does moth's.

The display filter only strips a verdict frame whose trailing nonce is
one THIS session issued — a program that print()s the literal text
"MPOK" keeps all its bytes (pinned by test). And a held partial match
is flushed when the wire goes quiet, so output ending on a verdict
prefix is no longer withheld until bytes that may never come; the old
test pinned the release-on-next-chunk path and read as though it
covered this — the new one covers the quiet case explicitly.

The sim path cleans its temp directory and kills the child on every
exit path, not just q.
Ambiguity with a human at the keyboard becomes a numbered picker (all
devices listed, sim included) instead of an error; three bad answers
give up. Scripts keep the error-with-list, since they cannot answer,
and an explicit -d that matches nothing stays an error — the user
asked for something specific. The parse rule is pure and pinned:
1-based index, whitespace tolerated, junk/EOF/out-of-range re-prompt.
@shbmx

shbmx commented Aug 15, 2026

Copy link
Copy Markdown
Owner Author

On-board verified: attached session on /dev/cu.usbmodem21201 — pushed in 178ms, console streamed, r restarted in 172ms (inside the documented ~150ms class), q exited clean. Note the board re-enumerated on a new port number after a re-seat and discovery/auto-select handled it.

@shbmx
shbmx merged commit cf72f92 into main Aug 15, 2026
2 checks passed
@shbmx
shbmx deleted the feat/mothc-run branch August 15, 2026 12:37
shbmx added a commit that referenced this pull request Aug 15, 2026
162ms appeared in two sample blocks and nowhere in any measurement
record — likely a transcription slip of the recorded 163. Replaced
with 178ms, the number recorded publicly in PR #17's on-board
verification comment, so the figure a reader meets first is one they
can trace. (Timing samples stay hand-checked: unlike byte counts they
are samples of a distribution, not constants a test can pin.)
shbmx added a commit that referenced this pull request Aug 15, 2026
* chore(release): publish prep for v0.1.0

Both packages are pub.dev-ready: real pubspecs (repository, issue
tracker, executables: mothc), package READMEs written for their pub.dev
pages, CHANGELOGs, and the MIT license in each package directory.
Dry-runs pass with the only warning being the uncommitted state this
commit resolves; package:moth archives at 14KB.

The scaffold's no-checkout fallback flips to the hosted `moth: ^0.1.0`
— the exact change the last review flagged as dangerous BEFORE
publishing is what makes pub-global installs work AFTER it, which is
why this branch merges at publish time and not before (RELEASING.md
step 4 claims the name first).

RELEASING.md records the order that matters: green suite, versions
agree, dry-runs, publish moth then mothc, tag, repo public, then the
stranger's sanity loop. getting-started opens with the pub-global
path; the clone remains the route to the simulator and boards.
Release notes for the tag live in docs/release-notes-v0.1.0.md.

* docs(release): review round 1 — capability claims match what ships

The moth README promised checks 'on every save'; the scaffold wires a
BUILD task (Ctrl/Cmd-Shift-B), invoked manually — the wording now
matches mothc's own careful latency claim. The image CHANGELOG line
regains the stored-program qualifier PR 14's code comment kept. And
RELEASING step 4 now states the load-bearing reason for moth-then-mothc:
the scaffold writes the hosted name the moment mothc is installable.

* chore(release): refresh v0.1.0 artifacts for run, devices and parity

The publish prep predated three merged PRs; the package pages and
release notes now describe what actually ships. mothc installs TWO
names via the executables map — `moth` (the friendly entry the README
now leads with: moth create, moth run) and `mothc` (every existing doc
stays true). The mothc README's hero is the flutter-run loop with the
measured numbers; the CHANGELOG covers run/devices/check/create/push.
package:moth's CHANGELOG gains Prefs, Servo and bulk I2C. The release
notes lead with `moth run`, extend the hardware story to both
firmwares sharing one native set, and the honest-limits paragraph now
names attachInterrupt and SPI explicitly. RELEASING's stranger loop
runs `moth run` under the alias name specifically, since the alias
only exists installed. Both dry-runs: 0 issues beyond the dirty state
this commit resolves; 15KB and 57KB archives.

* docs(release): the sample push time is a recorded one

162ms appeared in two sample blocks and nowhere in any measurement
record — likely a transcription slip of the recorded 163. Replaced
with 178ms, the number recorded publicly in PR #17's on-board
verification comment, so the figure a reader meets first is one they
can trace. (Timing samples stay hand-checked: unlike byte counts they
are samples of a distribution, not constants a test can pin.)

* docs(create): the scaffold teaches moth run, not the pre-run flow

The first file a newcomer reads still led with make ui and bare
mothc --push — both superseded by moth run as the hero flow. WiFi
stays on mothc --push because run has no network target; claiming
one here would be the drift the docs rules exist to prevent.

* docs(readme): the clips — moth run and touch, one unedited take each

Two GIFs recorded off the real S3, terminal and board camera composited
live in one OBS canvas so the timing between 'pushed in 174ms' and the
panel changing is genuine: the moth run loop with the device picker and
an r-restart to a new color, and controls.dart tracking a finger.
Captions claim only what is legible in frame — the restart figure
scrolled off, so the text does not cite one. examples/ui/hello.dart is
the clip's program (identical to the mothc create scaffold), committed
so a cloner can moth run it directly.

* docs: claim only what the clip shows; the pubspec is load-bearing

Review round on the GIF commit: the caption cited a 174ms line the
three-row pane never displays (board logs overwrite it within one
frame) — clause dropped. 'exactly what mothc create scaffolds'
softened to 'the same program' (headers differ deliberately). The
scaffold README stopped calling the pubspec editor-only: outside a
checkout the compiler resolves package:moth through the package
config pub get writes from it, so deleting it breaks compiles.
Accepted as-is: 12.2MB of clip weight per clone — front-page proof
is worth it and neither published package carries it.
shbmx added a commit that referenced this pull request Aug 15, 2026
feat(cli): mothc run + devices — the flutter-run loop
shbmx added a commit that referenced this pull request Aug 15, 2026
* chore(release): publish prep for v0.1.0

Both packages are pub.dev-ready: real pubspecs (repository, issue
tracker, executables: mothc), package READMEs written for their pub.dev
pages, CHANGELOGs, and the MIT license in each package directory.
Dry-runs pass with the only warning being the uncommitted state this
commit resolves; package:moth archives at 14KB.

The scaffold's no-checkout fallback flips to the hosted `moth: ^0.1.0`
— the exact change the last review flagged as dangerous BEFORE
publishing is what makes pub-global installs work AFTER it, which is
why this branch merges at publish time and not before (RELEASING.md
step 4 claims the name first).

RELEASING.md records the order that matters: green suite, versions
agree, dry-runs, publish moth then mothc, tag, repo public, then the
stranger's sanity loop. getting-started opens with the pub-global
path; the clone remains the route to the simulator and boards.
Release notes for the tag live in docs/release-notes-v0.1.0.md.

* docs(release): review round 1 — capability claims match what ships

The moth README promised checks 'on every save'; the scaffold wires a
BUILD task (Ctrl/Cmd-Shift-B), invoked manually — the wording now
matches mothc's own careful latency claim. The image CHANGELOG line
regains the stored-program qualifier PR 14's code comment kept. And
RELEASING step 4 now states the load-bearing reason for moth-then-mothc:
the scaffold writes the hosted name the moment mothc is installable.

* chore(release): refresh v0.1.0 artifacts for run, devices and parity

The publish prep predated three merged PRs; the package pages and
release notes now describe what actually ships. mothc installs TWO
names via the executables map — `moth` (the friendly entry the README
now leads with: moth create, moth run) and `mothc` (every existing doc
stays true). The mothc README's hero is the flutter-run loop with the
measured numbers; the CHANGELOG covers run/devices/check/create/push.
package:moth's CHANGELOG gains Prefs, Servo and bulk I2C. The release
notes lead with `moth run`, extend the hardware story to both
firmwares sharing one native set, and the honest-limits paragraph now
names attachInterrupt and SPI explicitly. RELEASING's stranger loop
runs `moth run` under the alias name specifically, since the alias
only exists installed. Both dry-runs: 0 issues beyond the dirty state
this commit resolves; 15KB and 57KB archives.

* docs(release): the sample push time is a recorded one

162ms appeared in two sample blocks and nowhere in any measurement
record — likely a transcription slip of the recorded 163. Replaced
with 178ms, the number recorded publicly in PR #17's on-board
verification comment, so the figure a reader meets first is one they
can trace. (Timing samples stay hand-checked: unlike byte counts they
are samples of a distribution, not constants a test can pin.)

* docs(create): the scaffold teaches moth run, not the pre-run flow

The first file a newcomer reads still led with make ui and bare
mothc --push — both superseded by moth run as the hero flow. WiFi
stays on mothc --push because run has no network target; claiming
one here would be the drift the docs rules exist to prevent.

* docs(readme): the clips — moth run and touch, one unedited take each

Two GIFs recorded off the real S3, terminal and board camera composited
live in one OBS canvas so the timing between 'pushed in 174ms' and the
panel changing is genuine: the moth run loop with the device picker and
an r-restart to a new color, and controls.dart tracking a finger.
Captions claim only what is legible in frame — the restart figure
scrolled off, so the text does not cite one. examples/ui/hello.dart is
the clip's program (identical to the mothc create scaffold), committed
so a cloner can moth run it directly.

* docs: claim only what the clip shows; the pubspec is load-bearing

Review round on the GIF commit: the caption cited a 174ms line the
three-row pane never displays (board logs overwrite it within one
frame) — clause dropped. 'exactly what mothc create scaffolds'
softened to 'the same program' (headers differ deliberately). The
scaffold README stopped calling the pubspec editor-only: outside a
checkout the compiler resolves package:moth through the package
config pub get writes from it, so deleting it breaks compiles.
Accepted as-is: 12.2MB of clip weight per clone — front-page proof
is worth it and neither published package carries it.
shbmx added a commit that referenced this pull request Aug 15, 2026
feat(cli): mothc run + devices — the flutter-run loop
shbmx added a commit that referenced this pull request Aug 15, 2026
* chore(release): publish prep for v0.1.0

Both packages are pub.dev-ready: real pubspecs (repository, issue
tracker, executables: mothc), package READMEs written for their pub.dev
pages, CHANGELOGs, and the MIT license in each package directory.
Dry-runs pass with the only warning being the uncommitted state this
commit resolves; package:moth archives at 14KB.

The scaffold's no-checkout fallback flips to the hosted `moth: ^0.1.0`
— the exact change the last review flagged as dangerous BEFORE
publishing is what makes pub-global installs work AFTER it, which is
why this branch merges at publish time and not before (RELEASING.md
step 4 claims the name first).

RELEASING.md records the order that matters: green suite, versions
agree, dry-runs, publish moth then mothc, tag, repo public, then the
stranger's sanity loop. getting-started opens with the pub-global
path; the clone remains the route to the simulator and boards.
Release notes for the tag live in docs/release-notes-v0.1.0.md.

* docs(release): review round 1 — capability claims match what ships

The moth README promised checks 'on every save'; the scaffold wires a
BUILD task (Ctrl/Cmd-Shift-B), invoked manually — the wording now
matches mothc's own careful latency claim. The image CHANGELOG line
regains the stored-program qualifier PR 14's code comment kept. And
RELEASING step 4 now states the load-bearing reason for moth-then-mothc:
the scaffold writes the hosted name the moment mothc is installable.

* chore(release): refresh v0.1.0 artifacts for run, devices and parity

The publish prep predated three merged PRs; the package pages and
release notes now describe what actually ships. mothc installs TWO
names via the executables map — `moth` (the friendly entry the README
now leads with: moth create, moth run) and `mothc` (every existing doc
stays true). The mothc README's hero is the flutter-run loop with the
measured numbers; the CHANGELOG covers run/devices/check/create/push.
package:moth's CHANGELOG gains Prefs, Servo and bulk I2C. The release
notes lead with `moth run`, extend the hardware story to both
firmwares sharing one native set, and the honest-limits paragraph now
names attachInterrupt and SPI explicitly. RELEASING's stranger loop
runs `moth run` under the alias name specifically, since the alias
only exists installed. Both dry-runs: 0 issues beyond the dirty state
this commit resolves; 15KB and 57KB archives.

* docs(release): the sample push time is a recorded one

162ms appeared in two sample blocks and nowhere in any measurement
record — likely a transcription slip of the recorded 163. Replaced
with 178ms, the number recorded publicly in PR #17's on-board
verification comment, so the figure a reader meets first is one they
can trace. (Timing samples stay hand-checked: unlike byte counts they
are samples of a distribution, not constants a test can pin.)

* docs(create): the scaffold teaches moth run, not the pre-run flow

The first file a newcomer reads still led with make ui and bare
mothc --push — both superseded by moth run as the hero flow. WiFi
stays on mothc --push because run has no network target; claiming
one here would be the drift the docs rules exist to prevent.

* docs(readme): the clips — moth run and touch, one unedited take each

Two GIFs recorded off the real S3, terminal and board camera composited
live in one OBS canvas so the timing between 'pushed in 174ms' and the
panel changing is genuine: the moth run loop with the device picker and
an r-restart to a new color, and controls.dart tracking a finger.
Captions claim only what is legible in frame — the restart figure
scrolled off, so the text does not cite one. examples/ui/hello.dart is
the clip's program (identical to the mothc create scaffold), committed
so a cloner can moth run it directly.

* docs: claim only what the clip shows; the pubspec is load-bearing

Review round on the GIF commit: the caption cited a 174ms line the
three-row pane never displays (board logs overwrite it within one
frame) — clause dropped. 'exactly what mothc create scaffolds'
softened to 'the same program' (headers differ deliberately). The
scaffold README stopped calling the pubspec editor-only: outside a
checkout the compiler resolves package:moth through the package
config pub get writes from it, so deleting it breaks compiles.
Accepted as-is: 12.2MB of clip weight per clone — front-page proof
is worth it and neither published package carries it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant