feat(firmware): add manifest preflight check (check:manifest) - #516
Conversation
StackchanContext.lifecycle.close() promises to release resources owned by the runtime context but only handled motion, camera, and input. Close now also stops the microphone, detaches the TTS playback callbacks, and turns off every LED. Connectivity stays open because it is owned by boot-services, not the context. The wasm Microphone gains a stop() alias for close() so both device and wasm implementations satisfy the same contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tasks.md was a stale ChatGPT tool-call task list from the migration and is deleted. chat.md and piu-faster.md hold durable design/policy notes, so they move under firmware/docs with descriptive names, and the migration plan references follow. Also ignore the xsbug-generated .xsdb.json files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Microphone.stop() only closed the streaming-mode AudioIn, so a finite record() kept holding the microphone until its duration elapsed even after lifecycle.close(). record() now registers an abort handler that stop() invokes, rejecting the pending promise and closing its AudioIn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a lightweight manifest preflight that runs mcconfig without -m for all supported build targets (m5stack, m5stack_cores3, m5stackchan_cores3, stackchan_rt, takao_core2_sg90, wasm). It validates manifest resolution before heavy builds and classifies Moddable warnings: allowlisted warnings pass, unknown missing module/resource/data warnings fail. Remove the stale typings/btutils module entry from the connectivity manifest; the typings moved to @moddable/typings (e1bd474) and the entry only produced a "no modules match" warning on every ESP32 build. Run the preflight in the CI test job before the build matrix so manifest problems surface without waiting for full ESP32 builds. Implements items 3 and 4 of the rearchitecture follow-up short-term priorities (docs/architecture/firmware-rearchitecture-followups_ja.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds firmware manifest preflight validation through ChangesManifest Preflight Validation
Runtime Shutdown Cleanup
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CI as build.yml
participant Check as check-manifest.js
participant Mcconfig as mcconfig
CI->>Check: npm run check:manifest
loop for each target
Check->>Mcconfig: spawn with platform and manifest
Mcconfig-->>Check: output and status
Check->>Check: classify warnings
end
Check-->>CI: pass or failure exit code
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 71bd656b6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
firmware/scripts/check-manifest.js (2)
56-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueEarly exit on spawn error skips remaining targets without a summary.
If
mcconfigcan't be spawned for one target (e.g., PATH not yet set up), the script exits immediately instead of recording it as a failure and continuing to check other targets, unlike the warning/exit-status path below which aggregates failures across all targets.♻️ Optional: aggregate this failure like the others
- if (result.error) { - console.error(`mcconfig could not be started: ${result.error.message}`) - console.error('Set up the Moddable SDK first, e.g. source "$HOME/.local/share/xs-dev-export.sh" or npm run setup.') - process.exit(1) - } + if (result.error) { + console.error('Set up the Moddable SDK first, e.g. source "$HOME/.local/share/xs-dev-export.sh" or npm run setup.') + failures.push(`mcconfig could not be started: ${result.error.message}`) + return failures + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@firmware/scripts/check-manifest.js` around lines 56 - 60, The spawn-error path in check-manifest.js exits immediately inside the mcconfig check, which prevents the script from continuing through the remaining targets and producing the usual failure summary. Update the logic around the mcconfig spawn result to treat result.error as a per-target failure: record it in the same aggregated failure tracking used by the warning/exit-status path, keep iterating over the other targets, and only exit after the loop with the collected summary. Use the existing mcconfig checks and the failure aggregation flow in check-manifest.js to keep behavior consistent.
11-18: 🧹 Nitpick | 🔵 TrivialSix full debug builds (including ESP32 hardware targets) run sequentially in CI.
Each target does a real
mcconfig -d ... -t build, which for ESP32 platforms means invoking the ESP-IDF toolchain. Sequential execution across 6 targets adds a non-trivial amount of wall-clock time to every PR before the heavier build matrix even starts. Consider whether these could run in parallel (e.g.,Promise.allwith worker processes) or whether build artifact caching is desirable to keep this preflight check "lightweight" as intended.Also applies to: 84-99
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@firmware/scripts/check-manifest.js` around lines 11 - 18, The preflight manifest check in check-manifest.js is running all six mcconfig -d builds sequentially, which makes CI slower than intended. Update the target loop to run builds concurrently where safe, using the targets array and the existing build invocation logic to launch independent worker processes in parallel (for example via Promise.all), or otherwise add caching to avoid repeated ESP32 toolchain work. Keep the same target definitions but change the execution flow so the builds no longer block one another.firmware/host/app/runtime-audio.ts (1)
86-91: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winConsider guarding against a throwing
microphone.stop().If
#microphone?.stop()throws, the subsequent TTS handler resets (onPlayed/onDone→noop) are skipped, leaving stale handlers attached after a failed close. Sinceclose()is a shutdown path, wrapping the microphone stop in try/finally (or reordering so TTS detachment always runs) would make cleanup best-effort rather than all-or-nothing.♻️ Suggested defensive ordering
close(): void { - this.#microphone?.stop() - this.#tts.onPlayed = noop - this.#tts.onDone = noop + try { + this.#microphone?.stop() + } finally { + this.#tts.onPlayed = noop + this.#tts.onDone = noop + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@firmware/host/app/runtime-audio.ts` around lines 86 - 91, The close() cleanup in RuntimeAudio can leave stale TTS handlers if microphone.stop() throws before the handler resets run. Update the RuntimeAudio.close method to make the TTS detachment (`this.#tts.onPlayed` and `this.#tts.onDone`) happen reliably even when `#microphone?.stop()` fails, for example by using try/finally or reordering the cleanup so the noop assignments always execute.firmware/host/app/runtime-lighting.ts (1)
45-50: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winOne failing LED aborts the rest of shutdown cleanup.
If any
led.off()throws, thefor...ofloop stops and remaining LEDs are left in their prior state. For a shutdown path, consider making this best-effort per LED.♻️ Suggested fix
close(): void { for (const led of Object.values(this.#led)) { - led.off() + try { + led.off() + } catch { + // best-effort: continue turning off remaining LEDs + } } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@firmware/host/app/runtime-lighting.ts` around lines 45 - 50, The shutdown cleanup in RuntimeLighting.close() should be best-effort per LED because a single failing led.off() currently aborts the loop and leaves other LEDs unchanged. Update the close() method to handle errors around each led.off() call independently so every entry in this.#led is attempted, and preserve the existing shutdown flow even if one LED fails. Use the RuntimeLighting.close() and this.#led iteration as the main places to fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@firmware/host/app/__tests__/runtime-audio.test.ts`:
- Around line 82-84: The test in runtime-audio.test.ts has a duplicate
block-scoped playbackTTS declaration in the same scope, which will fail
compilation. Remove the repeated const and keep a single runtime.tts cast in
this test block, then reuse that variable for the onPlayed/onDone calls so the
assertions remain unchanged. Locate the duplicate around the playbackTTS usage
in the runtime audio test.
---
Nitpick comments:
In `@firmware/host/app/runtime-audio.ts`:
- Around line 86-91: The close() cleanup in RuntimeAudio can leave stale TTS
handlers if microphone.stop() throws before the handler resets run. Update the
RuntimeAudio.close method to make the TTS detachment (`this.#tts.onPlayed` and
`this.#tts.onDone`) happen reliably even when `#microphone?.stop()` fails, for
example by using try/finally or reordering the cleanup so the noop assignments
always execute.
In `@firmware/host/app/runtime-lighting.ts`:
- Around line 45-50: The shutdown cleanup in RuntimeLighting.close() should be
best-effort per LED because a single failing led.off() currently aborts the loop
and leaves other LEDs unchanged. Update the close() method to handle errors
around each led.off() call independently so every entry in this.#led is
attempted, and preserve the existing shutdown flow even if one LED fails. Use
the RuntimeLighting.close() and this.#led iteration as the main places to fix.
In `@firmware/scripts/check-manifest.js`:
- Around line 56-60: The spawn-error path in check-manifest.js exits immediately
inside the mcconfig check, which prevents the script from continuing through the
remaining targets and producing the usual failure summary. Update the logic
around the mcconfig spawn result to treat result.error as a per-target failure:
record it in the same aggregated failure tracking used by the
warning/exit-status path, keep iterating over the other targets, and only exit
after the loop with the collected summary. Use the existing mcconfig checks and
the failure aggregation flow in check-manifest.js to keep behavior consistent.
- Around line 11-18: The preflight manifest check in check-manifest.js is
running all six mcconfig -d builds sequentially, which makes CI slower than
intended. Update the target loop to run builds concurrently where safe, using
the targets array and the existing build invocation logic to launch independent
worker processes in parallel (for example via Promise.all), or otherwise add
caching to avoid repeated ESP32 toolchain work. Keep the same target definitions
but change the execution flow so the builds no longer block one another.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f0bdbb00-9a8a-42a5-9a28-657a962b07fb
📒 Files selected for processing (19)
.github/workflows/build.yml.gitignoredocs/architecture/firmware-rearchitecture-followups_ja.mddocs/architecture/firmware-rearchitecture_ja.mdfirmware/docs/chat-audioio-integration.mdfirmware/docs/piu-performance-policy.mdfirmware/host/app/__tests__/runtime-audio.test.tsfirmware/host/app/__tests__/runtime-lighting.test.tsfirmware/host/app/runtime-audio.tsfirmware/host/app/runtime-context.tsfirmware/host/app/runtime-lighting.tsfirmware/host/modules/audio/microphone.tsfirmware/host/modules/audio/wasm/microphone.tsfirmware/host/modules/connectivity/manifest.jsonfirmware/host/modules/testing/fakes/capabilities.tsfirmware/host/modules/testing/fakes/microphone-type.tsfirmware/package.jsonfirmware/scripts/check-manifest.jsfirmware/tasks.md
💤 Files with no reviewable changes (2)
- firmware/tasks.md
- firmware/host/modules/connectivity/manifest.json
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@firmware/host/app/__tests__/runtime-audio.test.ts`:
- Around line 107-108: Remove the duplicate const playbackTTS declaration in the
runtime close test, retaining only one declaration in the same scope and
updating subsequent references to use it.
In `@firmware/host/app/runtime-context.ts`:
- Around line 580-586: Guard each cleanup operation in the finally block of the
runtime shutdown method so failures do not prevent subsequent cleanup. Wrap
`#inputRuntime.close`(), `#audioRuntime.close`(), and `#lightingRuntime.close`()
independently, preserving the existing camera shutdown behavior while ensuring
every runtime close is attempted even when an earlier one throws.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4ab54b98-680a-4da5-a2bc-20080dc0e27b
📒 Files selected for processing (6)
firmware/host/app/__tests__/runtime-audio.test.tsfirmware/host/app/__tests__/runtime-lighting.test.tsfirmware/host/app/runtime-audio.tsfirmware/host/app/runtime-context.tsfirmware/host/app/runtime-lighting.tsfirmware/scripts/check-manifest.js
🚧 Files skipped from review as they are similar to previous changes (1)
- firmware/scripts/check-manifest.js
概要
再設計フォローアップ文書(
docs/architecture/firmware-rearchitecture-followups_ja.md)§9「今後の優先順位」の 3, 4 を実装します。mcconfigを-mなしで実行する軽量な manifest preflightnpm run check:manifestを追加変更内容
firmware/scripts/check-manifest.js(新規): 全6ビルドターゲット(esp32/m5stack,esp32/m5stack_cores3,m5stackchan_cores3,stackchan_rt,takao_core2_sg90,wasm)の manifest 解決を検証。生成物は一時ディレクトリ(-o)へ出力し毎回破棄typings/btutilsは e1bd474 で@moddable/typingsへ移行済みだが manifest に参照が残っており、全 ESP32 build でno modules matchwarning が出ていた。allowlist に載せず参照自体を削除(これで全ターゲット warning ゼロ)build.ymlの test ジョブに preflight ステップを追加し、重い ESP32 build matrix の前に manifest 問題を検出Release impact
none — 開発ツールと CI のみの変更です。
typings/btutilsの manifest 参照削除は、存在しないファイルへの参照(warning のみ)の除去であり、ビルド成果物に影響しません。release note / changeset は不要です。検証
npm run check:manifest: 6ターゲット全て通過(warning ゼロ)typings/btutils参照 →unexpected missing resolutionで失敗、をローカルで確認check:legacy-names/check:architecture(79 pass)/test:unit(148 pass)全て成功build:lin成功、smoke:linで startup log が app behaviors ready まで到達(runtime error なし)。ESP32 実機でのテストは未実施(ビルド成果物に影響する変更がないため)🤖 Generated with Claude Code
Summary by CodeRabbit
check:manifest) to fail fast on packaging/manifest issues.