Flux Purr
Flux Purr 是 embedded firmware + React control console 的设备 mono-repo。README.md 将仓库组织为 firmware/、web/、tools/flux-purr-devd/、docs/specs/、docs/hardware/、.
- Device mono-repo
- Rust source quality
- ESP32 firmware
- shadcn/ui
- Storybook accessibility
- GitHub Actions scheduling
- Worktree bootstrap
目录
技术栈
运行与交付
- CI 工作流
- ci-main.yml
- ci.yml
- label-gate.yml
- notify-release-failure.yml
- release-completion.yml
- release-preparation.yml
- release.yml
- 能力标记
- bun_lock
- cargo_toml
- github_actions
- package_json
依赖与工具
- package.json
- 开发依赖
- @commitlint/cli
- @commitlint/config-conventional
- commitlint-plugin-function-rules
- lefthook
- 项目脚本
- bootstrap:dev
- build:firmware:web
- check:devd
- check:e2e
配置与质量
- 配置文件
- .env.example
- .gitattributes
- .gitignore
- Cargo.toml
{
"ci_workflows": [
"ci-main.yml",
"ci.yml",
"label-gate.yml",
"notify-release-failure.yml",
"release-completion.yml",
"release-preparation.yml",
"release.yml"
],
"configs_root": [
".env.example",
".gitattributes",
".gitignore",
"Cargo.toml",
"espflash.toml",
"lefthook.yml",
"mcu-agentd.toml",
"package.json",
"rust-toolchain.toml"
],
"markers_present": [
"bun_lock",
"cargo_toml",
"github_actions",
"package_json"
],
"package_json": {
"deps_dev": [
"@commitlint/cli",
"@commitlint/config-conventional",
"commitlint-plugin-function-rules",
"lefthook"
],
"scripts": [
"bootstrap:dev",
"build:firmware:web",
"check:devd",
"check:e2e",
"check:firmware:build",
"check:firmware:clippy",
"check:firmware:fmt",
"check:storybook",
"check:web",
"check:web:build",
"hooks:install",
"prepare",
"test:worktree-bootstrap",
"worktree:setup"
]
}
}Purpose
Flux Purr 是 embedded firmware + React control console 的设备 mono-repo。README.md 将仓库组织为 firmware/、web/、tools/flux-purr-devd/、docs/specs/、docs/hardware/、.github/ 和 scripts/;它是从硬件研究、固件 bring-up 到控制台、host tooling 与 release workflow 的完整产品,而非单一前端或 crate。
Taxonomy
- Tags: Device mono-repo, Rust source quality, ESP32 firmware, shadcn/ui, Storybook accessibility, GitHub Actions scheduling, Worktree bootstrap
Repository map
firmware/是 Rustno_stdESP32-S3 domain crate,firmware/Cargo.toml暴露flux-purr主 binary 和 host-preview binaries。web/是 Vite/React/shadcn/ui 控制台,web/components.json记录new-yorkstyle、Tailwind CSS variables、Lucide icon library 和@/components/uialiases。tools/flux-purr-devd/提供本机 daemon 与flux-purrCLI;它与 firmware、Web 共同消费产品 identity。VERSION、scripts/product-version.py与.github/scripts/release_chain.py分别定义版本解析及 VERSION-only preparation / main-merge provenance 验证。.github/quality-gates.json、label-gate.yml、release-preparation.yml、release-completion.yml、release.yml与notify-release-failure.yml构成统一产品 release surface,不再以过期的 firmware/Web 独立发布表示产品身份。
Runtime and delivery
根 package.json 编排 firmware fmt/clippy/build、Web check/build、Storybook build 与 e2e;mcu-agentd.toml 声明配置化硬件操作。Web 保持独立的 Storybook、交互测试与 bundle build;firmware 与 UI 也不共享一个“build succeeded”的含义。Worktree bootstrap 恢复 root/web Bun 依赖和 Cargo fetch 预热,同时把 Playwright browser cache 与系统前置条件保留为 warning 或手工修复路径(scripts/bootstrap-dev.sh、scripts/test-worktree-bootstrap.sh)。
根 VERSION 是唯一产品版本权威。开发、测试、firmware、flux-purr-devd、CLI、Web build、bundle、manifest 与 release 都只读它;开发 identity 是 nextPatch(VERSION)-dev.<short-sha>,不会写回文件。Cargo/NPM package metadata、Git tag、PR label、snapshot、环境变量和旧 manifest 都不得成为数字版本来源或 fallback(docs/specs/version-source/SPEC.md、scripts/product-version.py)。
产品 PR 在完整 PR CI 与 Validate PR labels 成功后,由 Prepare product version 在同一分支追加只修改 VERSION 的 preparation commit。其 trailers 固化 source SHA、产品版本和已验证 intent;Release completion 核验准备提交、base、labels 与源提交 CI,PR 仍以普通 merge commit 进入 main。Release Product 从合并后的 main 读取锁定 VERSION,一次构建、发布、校验 Web、public demo、firmware 与 host-tools 资产,并从同一归档各部署一次。v0.23.1 是这条完整链路已验证发布;recovery 只能重发同一 main commit/version(docs/adr/0005-pr-local-version-preparation.md、docs/adr/0006-tag-reservation-and-legacy-release-reconciliation.md、.github/workflows/release.yml)。
当 GitHub Actions 需要在 signed-commit 规则下创建该 preparation commit 时,workflow 将写入权限限定为 contents: write,并按 PR/check metadata 验证需要声明 pull-requests: read、checks: read,再使用 job 的 GITHUB_TOKEN 调用 GraphQL createCommitOnBranch。mutation 将 source SHA 作为 expectedHeadOid,只添加 VERSION,并在返回 OID 前后核对 PR branch head;因此并发 source 更新会失败,而不会被静默覆盖。它不配置 RELEASE_GPG_PRIVATE_KEY 或 RELEASE_GPG_PASSPHRASE。无论 preparation 是刚创建还是已存在,后续都验证 branch head、单父/仅 VERSION/trailers provenance,并在有界重试后要求 GitHub commit verification.verified 为真;任一检查失败即停止,而不降低 signed-commit 规则(.github/workflows/release-preparation.yml、.github/scripts/release_chain.py、.github/scripts/test-release-workflows.sh、docs/solutions/release/github-native-verified-version-commit.md)。
Architecture notes
可复用的架构记忆是“双运行面 + 共享验收 + 单一产品 identity”:firmware 负责 ESP32-S3 front-panel 行为,Web 负责控制台和组件状态,tools/flux-purr-devd/ 负责本机控制面,docs/specs/ 持有可执行验收合同。host-preview binary 在无板条件下提供固件早期验证。
当工程同时有 firmware、daemon、CLI、Web 与 release manifest 时,不能让各 consumer 从 package manifest 或环境变量各自重算版本。此仓库通过 scripts/product-version.py 集中派生稳定/RC、开发 qualifier、channel、source SHA 与 build ID,再投递给各 runtime 与 asset identity(docs/specs/version-source/IMPLEMENTATION.md)。
Workflow and quality gates
.github/quality-gates.json 要求 signed commits、PR-only main 与 workflow-backed checks。每个 PR 必须且仅能有一个 type:* 和一个 channel:*;Validate PR labels 是 merge 前 required check,但 labels 只表达发布意图,不能计算或覆盖 VERSION 数字内容(.github/workflows/label-gate.yml、docs/adr/0003-version-file-is-the-product-version-source.md)。
Validate PR labels 与 Release completion 是每 PR 的 required gate:它们分别使用 label-gate-<pr> 和 release-completion-<pr> concurrency group,并设 queue: max、不设 cancel-in-progress: true,使已排队的 gate 以非抢占方式完成。每个 job 都在 trusted base revision github.event.pull_request.base.sha || github.sha checkout;它以 pull-requests: read 的 job token 在运行时调用 gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" --jq '.labels',把当前 labels JSON 传入 validator。这样 gate 不会信任可能在排队期间过时的 event payload labels,也不会为读取 labels 执行不可信 PR 内容(.github/workflows/label-gate.yml、.github/workflows/release-completion.yml)。
Rust source-quality gate 独立于产品 release gate,按四层执行:cargo fmt --all -- --check 负责格式;firmware 与 devd 的 crate-local clippy.toml 配合 -D warnings、too_many_lines、too_many_arguments、excessive_nesting 负责语义 lint;cargo +esp clippy 在 Xtensa target 和 12V/20V/28V feature matrix 上覆盖 target-only code;tools/rust-style-check/ 使用 syn 检查入口文件预算、inline tests 和结构性 allow/expect。这些入口由 package.json、scripts/check-firmware-fmt.sh、scripts/check-firmware-clippy.sh、scripts/check-firmware-clippy-xtensa.sh 与 scripts/check-rust-style.sh 编排。100/7/4 是当前 firmware/devd 的 crate-local 配置和 checker profile,不是跨项目阈值;零结构债务和禁止结构性豁免是该仓库的本地边界,也不替代 target-aware lint(firmware/clippy.toml、tools/flux-purr-devd/clippy.toml、tools/rust-style-check/src/main.rs、docs/solutions/engineering/rust-style-debt-remediation.md)。
PR #102 已于 2026-09-09 合并到 f34dffcac6b3385237e0fe6a31d21f52867b0bc1;其 .github/scripts/test-release-workflows.sh 直接断言两个 required workflow 都含 queue: max、不含 cancel-in-progress: true,并使用当前 labels API snapshot。该验证也记录了本地 actionlint 1.7.12 尚不识别该 GitHub 已文档化语法:在 GitHub Actions concurrency documentation 与 runtime 支持明确之前,它们是权威;兼容性进展跟踪在 actionlint #687。
完整 PR CI 只针对产品源提交运行一次;preparation commit 只做结构验证,CI Main 验证 normal merge relation。带有效 preparation 的 merge 才可进入产品发布。type:docs 与 type:skip non-product merge 使 Release Product 成功 skip,不发布、不部署,也不触发失败通知(.github/workflows/release.yml、.github/workflows/notify-release-failure.yml)。发布失败后,预先锁定在 main/VERSION 的版本必须通过同 SHA recovery 收口,不能计算 successor 或把多次变更压缩为一个 release(docs/specs/version-source/SPEC.md)。
Reusable decisions
- 将 Rust source quality 拆成格式、语义 lint、源码结构和 target/features coverage 四层;入口脚本分别表达这些层,避免把
rustfmt或 host Clippy 当成完整质量证明。 - 把函数行数、参数数和嵌套深度放进 crate-local
clippy.toml,让 firmware 与 host tooling 可以共享方向但独立演进;当前100/7/4只代表 Flux Purr 的参考 profile。 - 对 firmware 入口同时保留 host feature matrix 与 Xtensa target matrix;target-only code 必须在实际 release target 上 lint,不能用 host 结果替代。
- 用
synchecker 约束 source shape 和结构性豁免边界,配合按职责拆分入口文件;不设全仓文件长度阈值,也不通过allow/expect或宏隐藏结构债务。 - 将
components.json与 Storybook a11y 保持在 Web 子项目,避免 shadcn/ui 约定扩散至 firmware。 - host preview binary 是硬件项目降低验证成本的有效模式。
- Worktree bootstrap 负责 repo-managed dependency recovery,但不应在 hook 中隐式进行不可控外部下载。
- 用一个 checked-in 产品版本文件作为唯一权威,让 tags、package metadata、labels、snapshots 与环境变量退回为派生或 intent 信息。
- 用同一个 resolver 为 runtime、CLI、bundle、Web build-info 和 manifest 提供 identity,避免多语言 mono-repo 版本漂移。
- 将版本写入置于同一产品 PR、完整 CI 之后且 normal merge 之前:preparation 只改
VERSION并携带可验证 provenance。 - 对受 signed-commit 规则保护且由 GitHub Actions 创建的 preparation,使用 GitHub-native
createCommitOnBranch、GITHUB_TOKEN与expectedHeadOid,而非请求或保存仓库 GPG 私钥/口令;created 与 existing 两种状态都必须验证 GitHub 签名。 - 保留 label gate 作为 required intent gate;自动 patch 与受控 exact 只决定写入操作,写入后版本文件独占数字版本事实。
- 普通 PR candidate 可按最新提交取消过期工作;
Validate PR labels与Release completion这类 required gate 则用 per-PRqueue: max非抢占排队,并在 job runtime 取得当前 labels snapshot。 - 当
actionlint版本落后 GitHub workflow syntax 时,不为静态工具绿灯删除 required-gate 调度语义;以 GitHub 官方文档、runtime 和项目 workflow fixture 作为临时权威,并保留 issue 跟踪。 - 从 normal main merge 一次构建、校验、发布并部署归档;发布失败时只允许同 SHA recovery。
Risks and open questions
- 主分支必须保留 merge commit,且
Release completion与Validate PR labels同为远端 required checks;否则无法证明 preparation 仍是受保护合并的一部分。 - 版本解析、preparation diff/provenance、main merge relation 与 asset manifest 必须一起验证;只检查 Web display 或 Git tag 会漏掉跨 consumer 漂移。
- tag reservation 需要在 preparation 前和 release build 前重复检查;已有 tag 只有精确指向同一 main merge 时才可被 recovery 复用。
- GitHub 的签名状态可能晚于 mutation 返回;不能把 existing preparation 或返回的 OID 当作签名证明,必须在有界重试后 fail closed。
queue: max最多保留 100 个 pending run,且 FIFO 以开始等待 concurrency group 的时间计;不要把 dispatch 时间当成严格全序,也不要与cancel-in-progress: true组合。- label snapshot 必须随 required gate 的实际执行重新读取;只以事件 payload 或 preparation 前的 labels 推断 release intent,会让延迟 gate 对当前 PR 状态作出过时判断。
Evidence index
README.mdfirmware/Cargo.tomlfirmware/clippy.tomlweb/package.jsonpackage.jsontools/flux-purr-devd/Cargo.tomltools/flux-purr-devd/clippy.tomltools/rust-style-check/Cargo.tomltools/rust-style-check/src/main.rsweb/components.jsonweb/.storybook/main.tsmcu-agentd.tomlVERSIONscripts/product-version.py.github/scripts/release_chain.py.github/quality-gates.json.github/workflows/label-gate.yml.github/workflows/release-preparation.yml.github/workflows/release-completion.yml.github/workflows/release.yml.github/workflows/notify-release-failure.yml.github/scripts/test-release-workflows.shscripts/check-firmware-fmt.shscripts/check-firmware-clippy.shscripts/check-firmware-clippy-xtensa.shscripts/check-rust-style.shscripts/bootstrap-dev.shscripts/test-worktree-bootstrap.shdocs/specs/version-source/SPEC.mddocs/specs/version-source/IMPLEMENTATION.mddocs/adr/0003-version-file-is-the-product-version-source.mddocs/adr/0005-pr-local-version-preparation.mddocs/adr/0006-tag-reservation-and-legacy-release-reconciliation.mddocs/solutions/release/github-native-verified-version-commit.mddocs/solutions/engineering/rust-style-debt-remediation.mddocs/guides/flux-purr-developer-policy.md- Flux Purr PR #102
- GitHub Actions concurrency documentation
- actionlint issue #687
相关内容
Device mono-repo
主题Device mono-repo 表示一个仓库同时承载硬件资料、固件、Web/desktop UI、docs/specs、测试工具或发布流程。它强调边界协调:硬件、firmware、UI 和文档互相影响,不应把仓库当作单一应用。 这个 Topic 的核心是设备级知识不能被某一个技术栈吞掉。
ESP32 firmware
主题ESP32 firmware 表示项目把 ESP32 家族 MCU 作为正式固件目标来维护,并围绕 toolchain、烧录、板级职责、连接能力和与其他 MCU/上位机的分工建立了稳定约定。这里的重点不是“芯片型号”,而是 ESP32 在系统里承担什么角色。 这个 Topic 往往不只是单片机源码。
GitHub Actions scheduling
主题GitHub Actions scheduling 表示仓库把 workflow 触发事件、并发 namespace、cancel-in-progress 和 required check 可见性组织成可审计的运行调度合同。目标不是让每个事件只跑一次,而是让当前 PR head 获得正确且可观察的 gate,同时不让无关 PR、main、deploy 或手工任务被同一个并发键误取消。
Rust source quality
主题Rust source quality 指把 Rust 源码的可读性、结构边界和 lint 可执行性写成可重复运行的 contract。它至少区分格式化、语义 lint、源码结构检查和 target/features coverage,并把每一层绑定到仓库内可运行的命令、配置或 checker,而不是把“能编译”当成质量结论(projects/flux-purr.md、data/topics/rust-source-quality.
shadcn/ui
主题shadcn/ui 表示项目采用官方 shadcn/ui 风格的 checked-in component layer:components.json、@/components/ui aliases、Radix primitives、Tailwind utilities、class-variance-authority、tailwind-merge、clsx 和 Lucide icons 共同构成组件约定。
Storybook accessibility
主题Storybook accessibility 表示 Storybook 不只是组件展示,还明确引入 a11y audit 能力,通常是 @storybook/addon-a11y 或等价的 browser-backed Axe sweep。它关注 story-level 可访问性信号,例如对比度、ARIA 基础错误、表单语义和交互状态,而不是泛化的 Storybook 文档能力。
Worktree bootstrap
主题Worktree bootstrap 表示仓库把 linked worktree 的“首次进入可开发状态”当成正式 contract 来维护,而不是依赖维护者记忆、散落且无统一入口的手工 checklist,或假定只有主工作区才值得被照顾。