Skip to content

feat(firmware): add manifest preflight check (check:manifest) - #516

Merged
meganetaaan merged 7 commits into
developfrom
feat/check-manifest-preflight
Jul 11, 2026
Merged

feat(firmware): add manifest preflight check (check:manifest)#516
meganetaaan merged 7 commits into
developfrom
feat/check-manifest-preflight

Conversation

@meganetaaan

@meganetaaan meganetaaan commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

概要

再設計フォローアップ文書(docs/architecture/firmware-rearchitecture-followups_ja.md)§9「今後の優先順位」の 3, 4 を実装します。

  • mcconfig-m なしで実行する軽量な manifest preflight npm run check:manifest を追加
  • Moddable warning を分類し、allowlist 外の missing module / resource / data を失敗扱いに

変更内容

  • firmware/scripts/check-manifest.js(新規): 全6ビルドターゲット(esp32/m5stack, esp32/m5stack_cores3, m5stackchan_cores3, stackchan_rt, takao_core2_sg90, wasm)の manifest 解決を検証。生成物は一時ディレクトリ(-o)へ出力し毎回破棄
  • connectivity manifest の stale 参照を削除: typings/btutilse1bd474@moddable/typings へ移行済みだが manifest に参照が残っており、全 ESP32 build で no modules match warning が出ていた。allowlist に載せず参照自体を削除(これで全ターゲット warning ゼロ)
  • CI: build.yml の test ジョブに preflight ステップを追加し、重い ESP32 build matrix の前に manifest 問題を検出
  • 文書: フォローアップ文書 §9 に実装済みステータスを反映

Release impact

none — 開発ツールと CI のみの変更です。typings/btutils の manifest 参照削除は、存在しないファイルへの参照(warning のみ)の除去であり、ビルド成果物に影響しません。release note / changeset は不要です。

検証

  • npm run check:manifest: 6ターゲット全て通過(warning ゼロ)
  • ネガティブテスト: 存在しない glob → mcconfig エラーを検出して失敗、stale な typings/btutils 参照 → unexpected missing resolution で失敗、をローカルで確認
  • 既存チェック: biome / 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

  • New Features
    • Added a firmware CI “manifest preflight” step (runs check:manifest) to fail fast on packaging/manifest issues.
  • Bug Fixes
    • Improved runtime shutdown robustness: audio now always detaches TTS callbacks even if microphone stop throws; lighting shutdown continues after LED-off errors; overall runtime close now records/re-throws the first shutdown error after attempting all components.
  • Tests
    • Added tests for the updated audio and lighting shutdown behaviors.
  • Documentation
    • Updated firmware rearchitecture follow-up notes with implementation status.

meganetaaan and others added 4 commits July 7, 2026 17:34
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>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5972f474-2a57-4a92-999c-05866b74b348

📥 Commits

Reviewing files that changed from the base of the PR and between 799c740 and 65655e2.

📒 Files selected for processing (2)
  • firmware/host/app/__tests__/runtime-audio.test.ts
  • firmware/host/app/runtime-context.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • firmware/host/app/tests/runtime-audio.test.ts

📝 Walkthrough

Walkthrough

Adds firmware manifest preflight validation through mcconfig, integrates it into CI, updates a connectivity manifest entry and architecture documentation, and hardens runtime shutdown cleanup against microphone, camera, and LED failures.

Changes

Manifest Preflight Validation

Layer / File(s) Summary
Manifest preflight script and CI wiring
firmware/scripts/check-manifest.js, firmware/package.json, .github/workflows/build.yml
Adds concurrent mcconfig checks for configured targets, warning classification, failure reporting, the check:manifest npm script, and a firmware CI step.
Manifest entry and implementation tracking
firmware/host/modules/connectivity/manifest.json, docs/architecture/firmware-rearchitecture-followups_ja.md
Replaces the connectivity typings entry and records the related manifest improvements as implemented.

Runtime Shutdown Cleanup

Layer / File(s) Summary
Resilient capability shutdown
firmware/host/app/runtime-context.ts, firmware/host/app/runtime-audio.ts, firmware/host/app/runtime-lighting.ts
Ensures remaining runtimes close after camera failures, TTS callbacks detach after microphone-stop failures, and LED shutdown continues after individual errors.
Shutdown failure coverage
firmware/host/app/__tests__/runtime-audio.test.ts, firmware/host/app/__tests__/runtime-lighting.test.ts
Adds tests for callback detachment and continued LED shutdown when cleanup operations throw.

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
Loading

Possibly related PRs

Suggested reviewers: stc1988

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the firmware manifest preflight check script.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/check-manifest-preflight

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread firmware/host/app/runtime-context.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
firmware/scripts/check-manifest.js (2)

56-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Early exit on spawn error skips remaining targets without a summary.

If mcconfig can'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 | 🔵 Trivial

Six 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.all with 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 win

Consider guarding against a throwing microphone.stop().

If #microphone?.stop() throws, the subsequent TTS handler resets (onPlayed/onDonenoop) are skipped, leaving stale handlers attached after a failed close. Since close() 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 win

One failing LED aborts the rest of shutdown cleanup.

If any led.off() throws, the for...of loop 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1136abb and 71bd656.

📒 Files selected for processing (19)
  • .github/workflows/build.yml
  • .gitignore
  • docs/architecture/firmware-rearchitecture-followups_ja.md
  • docs/architecture/firmware-rearchitecture_ja.md
  • firmware/docs/chat-audioio-integration.md
  • firmware/docs/piu-performance-policy.md
  • firmware/host/app/__tests__/runtime-audio.test.ts
  • firmware/host/app/__tests__/runtime-lighting.test.ts
  • firmware/host/app/runtime-audio.ts
  • firmware/host/app/runtime-context.ts
  • firmware/host/app/runtime-lighting.ts
  • firmware/host/modules/audio/microphone.ts
  • firmware/host/modules/audio/wasm/microphone.ts
  • firmware/host/modules/connectivity/manifest.json
  • firmware/host/modules/testing/fakes/capabilities.ts
  • firmware/host/modules/testing/fakes/microphone-type.ts
  • firmware/package.json
  • firmware/scripts/check-manifest.js
  • firmware/tasks.md
💤 Files with no reviewable changes (2)
  • firmware/tasks.md
  • firmware/host/modules/connectivity/manifest.json

Comment thread firmware/host/app/__tests__/runtime-audio.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1859b and 799c740.

📒 Files selected for processing (6)
  • firmware/host/app/__tests__/runtime-audio.test.ts
  • firmware/host/app/__tests__/runtime-lighting.test.ts
  • firmware/host/app/runtime-audio.ts
  • firmware/host/app/runtime-context.ts
  • firmware/host/app/runtime-lighting.ts
  • firmware/scripts/check-manifest.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • firmware/scripts/check-manifest.js

Comment thread firmware/host/app/__tests__/runtime-audio.test.ts Outdated
Comment thread firmware/host/app/runtime-context.ts
@meganetaaan
meganetaaan merged commit 28b0b82 into develop Jul 11, 2026
11 checks passed
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