Commit Graph
148 Commits
Author SHA1 Message Date
BFE Engineering e3a6026ba5 docs: README leads with the vendor-SDK comparison
Drop the Why / What Works sections and the product-origin story; the
text before Quick Start is now one comparison table of the upgrades over
the vendor SDK. Coverage methodology stays in the internal docs as the
reliability standard rather than the project's identity.
2026-08-31 08:27:42 -06:00
BFE Engineering 8677f8d061 docs: tighten README and core docs
Second conciseness pass per review feedback: bullets and tables over
prose (vendor-SDK failings, repo layout, sim models, kernel-port
rationale, QEMU scenarios), duplicated content cut (provenance pointer,
scenario prose the qemu README already carries). Also fixes two stale
claims in architecture.md: the flared seam unification and real-image
boot both landed (flare-edge #110, PR #8) but were still marked pending.
2026-08-31 08:19:51 -06:00
github-actions[bot] 673cd8b1d1 ci: update loc/tests/coverage badges [skip ci] 2026-08-31 14:09:56 +00:00
noah bab6500146 Merge pull request #12 from blueflare-energy/docs-reposition
docs: reposition as the 86 Panel development environment
2026-08-31 10:09:13 -04:00
github-actions[bot] 060ed84d29 ci: update loc/tests/coverage badges [skip ci] 2026-08-31 14:00:36 +00:00
BFE Engineering ed62a7d099 Merge remote-tracking branch 'origin/main' into docs-reposition 2026-08-31 08:00:33 -06:00
noah 1b694bd1ba Merge pull request #9 from blueflare-energy/org-transfer-refs
Org transfer: update repo references to blueflare-energy
2026-08-31 09:59:48 -04:00
noah 9dcf1486cb Merge pull request #8 from blueflare-energy/ota-full-apply
Full OTA apply scenario: write rootfs_b, flip AvbABData, boot the applied slot
2026-08-31 09:59:43 -04:00
noah fbd672c591 Merge pull request #7 from blueflare-energy/btlpm-618-modules
Matched 6.18 module set from the hermetic build (issue #4)
2026-08-31 09:58:57 -04:00
noah aca66bac26 Merge pull request #6 from blueflare-energy/vdso-timer-fix
Clock diagnostics + VM sanity guard for issue #3 (DT fix bench-gated)
2026-08-31 09:58:37 -04:00
BFE Engineering 643a05bffa docs: the device sim runs the full UI in a window (make it discoverable) 2026-08-31 07:58:34 -06:00
BFE Engineering d465d97b99 docs: repo renamed to bfe-core1106-sdk
The last product-first artifact after ADR-0008 was the repo name itself.
Current and forward-looking references updated; period-bound history in
the ADRs keeps the old name, with the rename recorded in ADR-0008.
Crate names, WARDEN_* env vars, and binaries are unchanged.
2026-08-31 07:17:00 -06:00
BFE Engineering 31c6d2496d docs: mark the NPU feasibility study as point-in-time context
Its 'our boards' / 'this product' phrasing predates the repositioning
(ADR-0008); a banner marks the context instead of rewriting a dated study.
2026-08-30 22:20:16 -06:00
BFE Engineering c756622c96 docs: reposition as the 86 Panel development environment
The repo's documentation framed it as a support repo for one product
(WardenOS). Since going public the real audience is anyone with a Luckfox
Pico 86 Panel: a maintained 6.18 kernel, an off-device development loop,
and a device simulator that exist nowhere else for this board. Reframe the
README and top-level docs board-first, with WardenOS documented as the
downstream consumer it is (ADR-0008).

Also an editorial pass over the whole doc set:
- every H1/H2 is now a short title, not a sentence (ADRs, qemu/, patches/,
  drivers/, architecture, NPU feasibility, config-lint, payload); workflow
  flowchart titles fixed at the source in tools/flowgen.py and regenerated
  with fresh bench numbers
- README Quick Start commands verified against the scripts; requirements
  corrected (curl, bare python, gcc >= 14) and the MC/DC gate added as a
  step (run green locally on gcc 14.2)
- dropped the 'needs python (not python3)' vendor dig: build-kernel.sh
  inherited the same requirement (filed #10 to remove it)
- glossed MC/DC and HPMCU on first use; marked the tests/uboot-ab
  reference as flare-edge; deduplicated the three-simulator list into the
  root README table
2026-08-30 22:19:12 -06:00
BFE EngineeringandClaude Fable 5 084df85450 docs: repo moved to the blueflare-energy org — update references
The transfer removes the last personal-name slug from the project's
public surface; old URLs redirect but the badge and ADR references now
point at the canonical home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 20:59:05 -06:00
BFE EngineeringandClaude Fable 5 7ab07a35d3 qemu: real-image milestone — boot an actual flare-edge build in the VM
mkimage gains --rootfs-image/--oem-image (raw ext4 matched pair placed
into slot A, size-checked fail-closed; slot B keeps the skeleton as a
known-good fallback), and stage-1 rc now hands the live devtmpfs to the
new root across switch_root — busybox switch_root moves nothing, our
skeleton init remounted defensively, but a REAL image's getty opens
/dev/console immediately and looped on ENOENT without it.

Verified with the 2026-08-24 flare-edge build (rootfs 192M + oem 26M):
the image's own rcS chain runs on the 6.18 virt kernel, real daemons
start (warden-modbus confirmed running via root shell; the shipped
0.2.4 flared reproduces flare-edge#106 exactly, and S99hciinit reports
the issue-#4 btlpm symptom — the VM doubles as a faithful reproducer of
known field bugs), and a getty answers on the console.
qemu/tests/real-image-boot.sh asserts switch_root + daemon starts +
login prompt, credential-free. REAL-IMAGE-BOOT-PASS.

Regression sweep after the shared-path changes: OTA-APPLY-PASS,
PORTAL-SCENARIO-PASS, UI-SHOT-PASS, boot smoke OK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 11:00:02 -06:00
BFE EngineeringandClaude Fable 5 d63c2117c1 qemu: full OTA apply scenario — write rootfs_b, flip AvbABData, boot it
Closes the loop every prior harness stopped short of, entirely
off-hardware: the real flared (WARDEN_HARD_RESET-gated build) pulls a
real signed tier-1 .wfw whose payload is a BOOTABLE rootfs stamped
0.0.2, verifies it, writes /dev/block/by-name/rootfs_b inside disk.img,
and flips the AvbABData (slot B: priority 15, 3 tries, unsuccessful —
the exact pre-first-boot arming state, round-tripped through a portal
check-in). The harness then boots slot _b and asserts the applied
version + marker are what runs. OTA-APPLY-PASS verified end to end.

- mkimage: the misc partition now carries REAL provisioned AvbABData
  (bytes mirror flare-edge's provisioning defaults) — slotctl fail-closes
  on bad AB magic before writing, which a zeroed misc tripped.
- run.sh --allow-apply / cmdline warden.fwapply: per-boot opt-in that
  makes stage-2 init export WARDEN_FW_ALLOW_APPLY=1; never the default.
- stage-2 init also exports WARDEN_HARD_RESET=0 (the CRU poke is fatal on
  virt, same class as the HPMCU probe); the harness performs the reboot.
- ADR-0006 boundary documented in the scenario and README: BCB slot
  CHOICE and the physical reset remain emulated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 10:40:08 -06:00
BFE EngineeringandClaude Fable 5 c258a2da68 build: produce the matched 6.18 module set (issue #4 root cause)
The hermetic build only ever made zImage+dtb — no 6.18 .ko set existed,
so the panel's only aic8800_btlpm.ko was the stale 5.10 build, which
fails the struct-module ABI check at load. New WARDEN_MODULES_COLLECT
env runs the full `make modules` (required: zImage alone emits no
Module.symvers, so per-directory M= builds cannot link) and collects the
listed dirs' modules fail-closed; CI's kernel-build collects
drivers/net/wireless/aic8800 and ships the .ko files in the artifact.

Verified: all three aic8800 modules (bsp 326K, fdrv 1.5M, btlpm 36K)
compile clean against 6.18, and bsp+btlpm INSMOD WITH RC=0 on the 6.18
kernel in the qemu/ device sim — no linkonce/this_module ABI error;
btlpm's rfkill init runs and only hardware power-up fails (no AIC silicon
on virt, expected). On-panel BT bring-up remains bench-gated (c8a3
currently dark).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 09:56:43 -06:00
BFE EngineeringandClaude Fable 5 1d7dec167e clock diagnostics for issue #3: probe, VM sanity scenario, fix plan
The qemu/ device sim answered the first question off-board: the same
kernel family under -M virt gives musl vDSO rate ratio 0.99963 — the
generic 6.18 armv7 vDSO is correct, so the board symptom is RV1106
register state (CNTFRQ/CNTVOFF, firmware-owned, secure-world boot chain).

- qemu/tests/clockprobe: interval-based musl probe separating RATE error
  (CNTFRQ) from boot OFFSET (CNTVOFF) — the original single absolute
  sample cannot distinguish them.
- qemu/tests/clock-sanity.sh: VM regression guard asserting the vDSO rate
  within 1% (PASSES: 1.00026); cross-builds the probe and stages it as
  payload itself.
- kernel/rv1106-enablement/timer/PLAN.md: the DT fix
  (arm,cpu-registers-not-fw-configured + measured clock-frequency on the
  board dts) gated on the two bench measurements; c8a3 is currently
  physically dark, needs hands at the bench.
- clockprobe joins the CI test loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 09:49:50 -06:00
github-actions[bot] 0077873995 ci: update loc/tests/coverage badges [skip ci] 2026-08-30 15:30:34 +00:00
noah ff62991e8d Merge pull request #5 from bfe-noah/qemu-device-sim
qemu/: QEMU device simulator — boot the real kernel + userspace off-hardware
2026-08-30 09:29:50 -06:00
BFE EngineeringandClaude Fable 5 abbc690277 ci: silence SC2187 on the busybox-sh shebangs (shellcheck 0.9 vs 0.10)
ubuntu-latest ships shellcheck 0.9.0, which warns that ash scripts are
checked as dash; 0.10 (local) does not. shell=dash is the accurate dialect
for busybox ash. Verified clean under BOTH versions; boot smoke re-run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 09:02:33 -06:00
BFE EngineeringandClaude Fable 5 f5c2cc9a42 review: iteration-3 fixes (convergence pass)
- ci-cd.md phrasing made time-invariant (true before and after the
  visibility flip; the prior wording asserted a state not yet real).
- ui-shot: liveness check before the tap QMP call too — the commit-message
  claim of "before every QMP call" is now actually true.
- busybox fetcher checks its pin before downloading, mirroring the
  kernel-tarball fetcher's ordering.
- Fragment disable-arm: absence of a deps-gated symbol is a valid disable
  outcome; only "still set" is fatal (the iteration-2 symmetrization
  overcorrected). Negative-tested both directions under set -e.

Iteration 3 produced four findings, none blocking, all applied — the
recursive review is converged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 08:45:46 -06:00
BFE EngineeringandClaude Fable 5 973a414f07 review: iteration-2 fixes (fragment assertion, evidence paths, hardening)
- build-kernel.sh fragment assertion: survives a missing trailing newline
  (the read footgun, reproduced) and treats an absent symbol on a disable
  line as FATAL, symmetric with the enable arm.
- fetch-kernel-tarball.sh checks the pin before downloading; both fetchers
  add --retry-connrefused.
- mkimage rejects '.'/'..' state keys.
- ui-shot: VM liveness checked before every QMP call, console.log preserved
  as evidence on every failure path, repaint deadline widened to 90s with
  the contended-runner rationale documented.
- rs485-bridge: overflow discards back off one gap and rate-limit their log
  line, mirroring the accept-loop fix; clippy nit fixed.
- .gitignore ignores *.elf/*.map so the untracked artifacts cannot silently
  return; CI shellcheck glob now covers build/ and the rootfs boot scripts
  (directives added for the deliberate in-guest source paths).
- Docs: NPU parity row matches its sibling verification docs; line-pinned
  audit cross-references unpinned; CROSS_COMPILE documented in the build
  header; payload README lists warden-ui; ci-cd tense settled.

Verified: guards negative-tested (bad state keys, no-newline fragment);
boot smoke, portal scenario, ui-shot all PASS; 53 tests green; shellcheck
clean across the widened glob; clippy zero.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 08:34:47 -06:00
BFE EngineeringandClaude Fable 5 2756de0b46 review: iteration-1 fixes across CI, bridge, VM harness, and docs
CI/pipeline:
- KERNEL_TARBALL passed as a YAML env literal '~' was never tilde-expanded
  and would have failed every hosted kernel-build dispatch; the path is now
  exported from the shell. Verified reproducible before the fix.
- Every job gets timeout-minutes; boot smoke uses timeout -k so a wedged
  qemu is SIGKILLed instead of holding the job.
- Tarball fetch + fail-closed sha256 verification deduplicated into
  build/fetch-kernel-tarball.sh (with curl retries), used by build-kernel.sh
  and both CI jobs. busybox fetch gains retries too.
- ccache layer for kernel-build (cache keyed on defconfig+patches) recovers
  the incremental-compile speed the ephemeral-runner move cost.
- build-kernel.sh now asserts every fragment option survived olddefconfig —
  merge_config -m pastes text and Kconfig silently drops unmet symbols.

rs485-bridge:
- pending-buffer cap (2x max RTU ADU) instead of unbounded growth;
  explicit accept-loop error handling with backoff instead of .flatten();
  per-arm inline bounds instead of the string-keyed lookup whose default
  would have mis-bounded a future get-input; control-socket cleanup errors
  surfaced; flag-shaped values rejected in arg parsing; doc example uses a
  private mktemp dir. Test timing margins widened for contended runners
  (gap 25->120ms, 60x margin on the split-frame test).

VM harness:
- stage-1/stage-2 boot scripts share one validated slot parser and one
  by-name populator (qemu/rootfs/etc/warden-lib.sh) — the duplicated
  parser had already diverged on validation; userdata/oem mount failures
  now fail fast with a greppable sentinel; udhcpc fallback keys off the
  interface actually having an address; switch_root applet guarded.
- boot-smoke delegates the qemu invocation to run.sh (machine shape lives
  in ONE place); run.sh port 0 disables a hostfwd.
- mkimage: unknown partition names fail at build time; DISK_END is a max,
  not last-entry; --state keys validated as filenames.
- portal-scenario: mock readiness is asserted (no silent fall-through),
  hostfwd port collisions retried, mount-failure sentinel fails fast.
- ui-shot: fixed sleeps replaced with bounded screendump polling; the
  repaint assertion is real and documented as such. qmp.py loses its
  module-global and gains argv validation.

Docs/scrub: bench-host paths and the site AP name removed from six more
port docs and two evidence tables; path-bearing build artifacts (.elf,
.map) untracked (the 154-byte firmware .bin is path-free and stays);
ADR-0003 marked visibility-superseded by ADR-0007; stale section
cross-reference fixed; flare-edge noted as private for outside readers;
stale root-level review report removed per the new workspace rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 08:19:17 -06:00
BFE EngineeringandClaude Fable 5 b667ff5b1e license: GPL-2.0-only repo-wide
Replaces the dual MIT/Apache-2.0 licensing (and its GPL carve-out) with a
single GPL-2.0-only license for the whole repository: coherent with the
kernel patch series it exists to carry, the embedded-Linux norm, and the
stack's openness principle — anyone shipping products on this SDK publishes
their changes. GPL-2.0 (not 3) deliberately: the Installation-Information
clause of GPLv3 sits badly with WardenOS's signed A/B firmware chain.
LICENSE is now the canonical GPLv2 text; crate manifests updated;
inbound = outbound noted in the README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 07:44:32 -06:00
BFE EngineeringandClaude Fable 5 16368bb871 public-readiness: remove internal person-name attributions from docs
Decision/directive lines keep their dates and content, drop the name;
hands-on confirmations become "confirmed by hand at the panel"; a dangling
CLAUDE.md reference becomes "repo policy". Upstream vendor author
attributions in the GPL kernel sources (MODULE_AUTHOR, copyright headers,
LKML citations) are third-party legal attributions and stay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 07:27:57 -06:00
BFE EngineeringandClaude Fable 5 5155224594 public-readiness: scrub internal details, split licensing, hosted kernel-build
Preparing the repo to go public (ADR-0007):

- CI: kernel-build moves from the self-hosted runner to ubuntu-latest
  (installs its own cross toolchain + qemu, caches the pristine tarball).
  On a public repo a registered self-hosted runner is reachable from
  approved fork-PR workflows — i.e. arbitrary code on private
  infrastructure — and the build never actually needed the SDK host.
  ADR-0004 marked superseded-in-part; docs/ci-cd.md rewritten (site
  specifics now live only in the private deployment log).
- Licensing: LICENSE gains the GPL-2.0 carve-out for patches/ and the
  kernel source excerpts (Linux derivatives; per-driver provenance was
  already tracked in PROVENANCE.md); patches/README.md states it too.
- Scrubbed from the tip: bench-unit dev credentials and its gadget IP
  (m2-boot notes), the site AP SSID+BSSID and a neighboring AP's BSSID
  and the device WLAN MAC (wifi bring-up evidence — BSSIDs are
  geolocatable), the runner mesh IP. NOTE: these remain in git history;
  decision on a pre-publication history rewrite is separate.
- Emoji cleanup across 21 tracked files (kernel port docs, review report,
  enforce-mcdc.sh) per repo text conventions: status marks became
  [x]/[wip]/[ ]/OK plain text.
- "[maintainer]-gated" process phrasing normalized to "maintainer-gated"
  (attributions in dated evidence docs kept).

Verified: zero emojis tracked; scrub grep clean; patches carry no internal
references; ci.yml parses; shellcheck unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 07:25:57 -06:00
BFE EngineeringandClaude Fable 5 fea36faaec docs(ci-cd): runner provisioned with qemu-system-arm (boot smoke unblocked)
Installed 2026-08-30 on bfe-mpc-0640 (QEMU 10.0.11; cpio/curl already
present); recorded in flare-deployment deploy-log. End-to-end CI check of
the smoke step waits on the account-wide Actions billing stall.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 07:12:44 -06:00
BFE EngineeringandClaude Fable 5 13b7d063a2 qemu: display + touch scenario, ADR-0006, docs
Phase 4+5 of the device sim:

- Display + touch verified end-to-end: virtio-gpu at 720x720 (fbdev
  emulation) renders the real WardenOS dashboard from the static LVGL
  fbdev+evdev UI build (flare-edge qemu-vm-support tools/build-ui-vm.sh);
  QMP input-send-event taps the Metrics tab and qemu/tests/ui-shot.sh
  asserts the repaint from screendumps. Two load-bearing QEMU flags found
  and documented: -global virtio-mmio.force-legacy=false (gpu/input are
  VERSION_1-only) and the 200ms press hold (an instantaneous press+release
  lands inside one LVGL indev poll and never clicks).
- qemu/tests/qmp.py: minimal QMP client (screendump, tap, quit).
- stage-2 init starts warden-ui when present and fb0 exists.
- docs/decisions/0006-qemu-device-sim.md: virt-not-custom-board, the
  enters-at-kernel boundary, fragment policy, naming, consequences.
- docs/architecture.md: new section 7 (device emulation), order-of-work
  item 7; modbus cross-reference to the bridge.
- qemu/README.md: emulated-vs-not table, scenarios, gotchas, host/runner
  requirements. docs/ci-cd.md: runner needs one-time qemu-system-arm
  install (fail-closed smoke until then, [maintainer]-gated). Repo README updated.

Final sweep on this commit: shellcheck clean, bridge 7/7 tests, boot smoke
PASS, portal scenario PASS (check-in + fw pull + signed .wfw download),
ui-shot PASS (touch navigates to Metrics) — all under the final flags.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-29 20:16:36 -06:00
BFE EngineeringandClaude Fable 5 c7e06514ad qemu: RS485 bridge to sim, portal E2E scenario, CI wiring
Phase 3 of the device sim, all verified on QEMU 10.0.11:

- qemu/rs485-bridge/: std-only crate bridging a QEMU serial chardev (unix
  socket) to warden_sim::ModbusSlave — gap-based RTU framing (CRC failures
  degrade to real-slave silence), line-protocol control socket for register
  seeding and fault injection (drop/exception/clear), bounds-checked so a
  scenario typo answers err instead of panicking the bus. 7 unit tests,
  bench in the sim_bench pattern. Verified end-to-end: guest master frame
  on /dev/ttyS4 (pci-serial) answered from the sim slave, CRC-correct.
- virt machine gains highmem=off: the 32-bit non-LPAE kernel cannot reach
  virt's default 40-bit PCIe ECAM (pci-host-generic EOVERFLOW); with it the
  full PCI set probes (16550A ttyS0, i6300esb).
- Watchdog scenario verified: guest arms /dev/watchdog, no petting, i6300esb
  resets the VM ~30s later (first environment where this arm is testable).
- qemu/tests/portal-scenario.sh: the real static-musl warden-flared inside
  the VM against flare-edge's mock portal on the host — authenticated
  check-in, firmware desired-state pull, and download of a real signed
  tier-1 .wfw offer, asserted from the portal log. Found and filed
  flare-edge#106 (fatal SIGBUS in the HPMCU boot-loaded probe on
  non-RV1106 memory maps); runs against a flared built from the
  qemu-vm-support fix branch.
- stage-2 init: WARDEN_FLARE_INSECURE=1 + WARDEN_HPMCU=0 (documented VM
  deviations), firmware-version stamp, newline-terminated state seeds.
- CI: rs485-bridge joins the test loop and bench job; new qemu-tools job
  (shellcheck + initramfs + disk image on hosted runners); kernel-build
  gains a fail-closed qemu boot-smoke step. All qemu scripts shellcheck-clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-29 19:59:44 -06:00
BFE EngineeringandClaude Fable 5 bf3c93cf85 qemu: device-sim bringup — boot smoke, A/B disk harness, virt kernel variant
The third simulator (deliberately not named "sim"): a QEMU -M virt VM that
boots the real 6.18.46 kernel and enters at -kernel zImage — everything below
(BootROM/idblock/U-Boot/real BCB A/B selection) is closed blobs + mask ROM
and is explicitly out of scope.

- qemu/mkinitramfs.sh: pinned static busybox (sha256 fail-closed) + rootfs/
- qemu/mkimage.sh: unprivileged sparse disk image with the device's canonical
  12-partition blkdevparts A/B layout (vda == mmcblk0 mapping)
- qemu/rootfs/: stage-1 init (by-name symlinks from PARTNAME uevents,
  whole-token warden.slot= parse, switch_root) + stage-2 init (userdata/oem
  mounts, slirp networking, payload daemon start)
- qemu/run.sh: runner with --slot/--rtc/--watchdog/--rs485/--qmp/--display
- qemu/configs/virt.fragment + WARDEN_KCONFIG_FRAGMENT hook in
  build/build-kernel.sh (canonical RV1106 build untouched when unset):
  adds PCI, pci-serial, i6300esb watchdog, WireGuard, virtio-gpu/input
- qemu/tests/boot-smoke.sh: sentinel-asserting boot test

Verified on QEMU 10.0.11: canonical zImage boots -M virt unmodified (the
feared DEBUG_UNCOMPRESS decompressor hang does not exist in 6.18); full
stack boots both slots; 12 by-name symlinks; userdata persists across
reboot; -rtc base=2021-01-01 reproduces the no-RTC wrong-clock class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-29 18:53:16 -06:00
BFE Engineering 44cdf2bf38 Merge pull request #2 from bfe-noah/artifact-gc
ci: automatic artifact GC for kernel builds (account-wide storage)
2026-08-26 12:53:07 -06:00
BFE Engineering 0833d61bfa ci: automatic artifact GC for kernel builds (account-wide storage)
Mirror flare-edge's artifact GC so the shared Actions storage quota can't fill
from either repo. A new prune-artifacts job (ubuntu, gh + built-in token,
actions:write) runs before the kernel-build upload and deletes older
kernel-rv1106 artifacts beyond the newest 3; kernel-build needs it. retention-days
14 -> 5 as the backstop. Best-effort so it never blocks a build. Completes the
account-wide auto-clear (an over-quota once blocked ALL runs, not just uploads).
2026-08-26 10:44:48 -06:00
github-actions[bot] 3764d1d7e8 ci: update loc/tests/coverage badges [skip ci] 2026-08-26 00:10:48 +00:00
BFE EngineeringandClaude Opus 4.8 94cd0bcd35 kernel/rv1106-enablement: mark as port-provenance; patches/ is canonical
The .c/.dts/.frag files here are a point-in-time port-provenance snapshot (with
standalone test programs + instructional fragments the narrative docs reference),
not a maintained second copy of the kernel delta. Add a prominent canonical-source
note so the snapshots (which can lag the final series, e.g. the clk CLK_IGNORE_UNUSED
fix) are never mistaken for current code. patches/ + build/build-kernel.sh remain the
single source of truth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 18:10:09 -06:00
BFE EngineeringandClaude Opus 4.8 82b8bc91bb docs(ci-cd): runner installed + online, kernel-build verified (2026-08-25)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 17:40:24 -06:00
github-actions[bot] 40a61b5e62 ci: update loc/tests/coverage badges [skip ci] 2026-08-25 23:17:26 +00:00
BFE EngineeringandClaude Opus 4.8 a804a35e08 ci: kernel-build artifact upload is best-effort (storage-quota resilience)
The kernel build itself is the gate; uploading the zImage/dtb can fail on an
account-wide GitHub artifact-storage-quota hit unrelated to the build. Mark the
upload continue-on-error so a good build isn't red-X'd by it. Outputs also remain
on the self-hosted runner host.

Verified: kernel-build compiled the RV1106 6.18.46 kernel end-to-end on
bfe-mpc-0640 (zImage 8.25 MB + rv1106-warden.dtb 37 KB); the patch series applied
(issue #1 fix confirmed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 17:16:36 -06:00
BFE EngineeringandClaude Opus 4.8 5e1b02ba60 kernel-build: apply patches outside the checkout + fail-loud sentinel (#1)
The kernel-build job set WORK inside the repo checkout, so build-kernel.sh's
`git apply` ran from a subdirectory of the warden-sdk git repo — where git apply
silently ignores out-of-subdir paths and exits 0 without applying anything. The
build then failed at the (never-created) rv1106-warden.dtb target, and the
unconditional `echo "applied"` had masked it.

- ci.yml: WORK -> ${{ runner.temp }}/kbuild-out (outside the checkout); artifact
  paths follow.
- build-kernel.sh: the patch loop now fails loudly instead of echoing on silent
  failure, and a post-apply sentinel assert (rv1106-warden.dts must exist) makes a
  silent no-op impossible to ship — the regression guard for this class.

Refs #1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 16:55:46 -06:00
github-actions[bot] cb26739164 ci: update loc/tests/coverage badges [skip ci] 2026-08-25 22:28:04 +00:00
BFE EngineeringandClaude Opus 4.8 5a9432b8bc ci: self-provision python + cross toolchain in kernel-build job
Make the self-hosted kernel-build runnable with zero manual toolchain/python setup
on the runner host (docs/ci-cd.md steps 3-4 move into the workflow):

- build/build-kernel.sh: honor a caller-provided CROSS_COMPILE (default stays the
  Luckfox uclibc prefix). The kernel is freestanding, so a generic arm cross
  compiler links it.
- kernel-build job: set CROSS_COMPILE=arm-linux-gnueabihf- (Debian
  gcc-arm-linux-gnueabihf, already on 0640) instead of depending on the ephemeral
  SDK checkout path; symlink python->python3 into $RUNNER_TEMP/bin on $GITHUB_PATH
  for the bare-`python` SDK quirk.
- docs/ci-cd.md: only steps 1-2 (systemd service + cgroup cap) still need 0640 sudo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 16:27:21 -06:00
BFE EngineeringandClaude Opus 4.8 4f060db1b1 ci: make main the canonical branch (badges guard, docs)
Point the badges job's branch guard at refs/heads/main and update the README /
ci-cd docs to name main as the canonical branch, ahead of establishing main from
bringup as the SDK's first main commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 16:18:36 -06:00
github-actions[bot] 620a21928d ci: update loc/tests/coverage badges [skip ci] 2026-08-25 22:06:24 +00:00
github-actions[bot] aa47a537c2 ci: update loc/tests/coverage badges [skip ci] 2026-08-25 22:02:56 +00:00
BFE EngineeringandClaude Opus 4.8 5a8b60ba3a review: iteration-2 fixes (fail-closed loader whole-name match + doc accuracy)
Second recursive pass: two adversarial re-reviewers verified the iteration-1 fixes.
Fuzz/empirical checks cleared the freshness min-budget rewrite (200k random trials),
the config-lint reg-token scanner, its UTF-8 boundary safety, and the build-kernel.sh
trap (5 exit scenarios) — no defects. Three items corrected here:

- config-lint is_known_safe_loader: match the WHOLE normalized loader name, not an
  unanchored substring. The iteration-1 allowlist swap kept `contains()`, so a future
  coprocessor whose name merely contained a boot word ("AudioLoader" ⊃ "loader",
  "SplRtos" ⊃ "spl", "Bl32" ≠ "bl31") would have been waved through — reopening the
  0x40000-brick false-negative the fail-closed change exists to prevent. Regression
  test added with those exact adversarial names.
- docs/architecture.md §3: the `cru` bullet no longer claims flared's devmem `Bus`
  seam is shipped — it lands when flare-edge consumes warden-sdk ([maintainer]-gated), which
  is what §7 item 3 already said. Resolves an in-document contradiction.
- drivers/README.md: modbus "11 pty scenarios" -> "8 pty scenarios + 3 wire/daemon
  checks (11 total)", matching flare-edge tools/modbus-sim's actual SCENARIOS list.
- docs/decisions/0002-mcdc-tiering.md: Consequences now describe the shared
  drivers/enforce-mcdc.sh + drivers/<name>/test/ layout actually built (not the
  per-driver dirs the ADR first anticipated); Rust MC/DC tooling reality noted.

config-lint: 9 tests pass; clippy clean under -D warnings; gitleaks clean. C drivers
untouched (still relays 40/40, freshness 66/66 MC/DC).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 16:02:22 -06:00
github-actions[bot] c23de76ccc ci: update loc/tests/coverage badges [skip ci] 2026-08-25 21:49:36 +00:00
BFE EngineeringandClaude Opus 4.8 4ee4dfcf88 review: fix review findings (correctness, hardening, doc-accuracy)
First recursive code-review-harness pass over the authored SDK code (sim/,
config-lint, flowgen, drivers/{relays,freshness}, build/, ci.yml). Four parallel
dimension reviewers; all findings at every severity corrected per workspace rule.

Correctness / reliability:
- freshness.c min_budget_ms: use a `seen` flag, not `best==0`, as the empty
  sentinel — a zero-tolerance (max_stale_ms==0) binding was silently widened to a
  looser neighbour's budget. Regression test added; still 66/66 MC/DC.
- config-lint parse_reserved_ranges: match `reg` as a whole property token (ident
  boundary before, `=` after) so `reg-names` / a `region-*` label no longer
  mis-parses into a bogus reserved range.
- config-lint loader check: fail closed — flag any loader with a LOAD_ADDR that is
  not a known-safe boot component, instead of only known MCU names, so a future
  coprocessor ("Rtos"/"Bl32") can't slip past the 0x40000-brick gate.
- build-kernel.sh: sha256 verification is now mandatory (refuse to build if the pin
  is missing) and the mktemp scratch tree is removed on exit (trap), while a
  caller-provided WORK is left intact for CI artifact upload.

Test quality:
- freshness: added the age==max_stale boundary case and a clock-wraparound
  (now < last_ok) fail-safe-to-UNKNOWN test.
- relays: unsetenv(WARDEN_GPIO_ROOT) at main() so the NULL-env arm is hermetic.

Security / CI:
- ci.yml: top-level `permissions: contents: read` (badges overrides to write);
  pin taiki-e/install-action to commit SHA (v2.86.7).

Maintainability / docs:
- drivers/enforce-mcdc.sh: one shared, name-derived gate replaces the two
  copy-pasted per-driver scripts; Makefiles call ../../enforce-mcdc.sh.
- docs/architecture.md: §3/§4/§6/§7 rewritten to match reality — NPU/RGA models,
  config-lint, and the relays+freshness MC/DC harnesses are done; kernel §6 now
  reflects the 5.10->6.18.46 forward-port (ADR-0001), not the superseded plan44/6.6.
- README: status blurb + layout table corrected (kernel/, .github/; stale ci/ and
  patches/ descriptions fixed). hpmcu "8 tests" -> 7 in docs.
- freshness.{c,h}: ADR reference points at flare-edge ADR-0004 (warden-sdk's
  ADR-0004 is the CI runner — number collision).
- normalize rustfmt drift across sim/ + config-lint.

All green: sim 37 tests, config-lint 8 tests, both drivers 100% MC/DC (relays
40/40, freshness 66/66), clippy clean under -D warnings, gitleaks clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 15:48:52 -06:00
github-actions[bot] 75ff711e43 ci: update loc/tests/coverage badges [skip ci] 2026-08-25 21:23:54 +00:00
BFE EngineeringandClaude Opus 4.8 880b043506 docs+tools: workflow flowchart harness (P4) + drivers Tier-1/Tier-2 split
- tools/flowgen.py: generates docs/workflows/<name>.md — one outcome-first mermaid
  flowchart per workflow the SDK tests (hpmcu watchdog, modbus read, cru ladder,
  rga offload, relay drive, freshness contract), each stamped with its benchmark
  ns/op or 100% MC/DC result. Deterministic; reads the cargo-bench trend json.
- drivers/README.md: honest Tier-1 (relays + freshness, 100% MC/DC here now) vs
  Tier-2 (modbus/rga — serious testing + fault-injection + benchmarks via sim
  models; driver sources migrate in with the flare-edge unification) per ADR-0002/0005.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
2026-08-25 15:22:58 -06:00