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
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
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
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
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
- 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
- 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
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
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
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
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
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
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
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
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
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).
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
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
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
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
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
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
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
- 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
freshness.c (ADR-0004 freshness-contract engine) is pure logic via produce/render
callbacks — no hardware seam needed, the callbacks are the seam. Host harness reaches
100% MC/DC (66/66 conditions, 100% lines, 27 checks) by driving warden_fresh_decide
directly + the bind/tick/invalidate/min-budget state machine through fakes, with
-DFRESH_MAX=2 so the table-full and unused/hidden-slot arms are reachable. Directly
serves future-features-2's "never a stale number in the UI" requirement. The CI mcdc
job now enforces relays + freshness.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
Registered a 3rd repo-scoped runner on bfe-mpc-0640 (label warden-sdk,
bfe-mpc-0640-warden-sdk) per ADR-0004. It is offline pending the sudo-gated steps
(service install + CPUQuota/MemoryMax cgroup cap + toolchain/python provisioning),
documented in docs/ci-cd.md as a [maintainer] handoff. kernel-build stays dispatch-gated
and passes JOBS=4 as a belt-and-braces resource bound.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
- patches/: the pristine-6.18.46 -> WardenOS delta as a 13-patch, subsystem-split
series (223 files, ~136K lines: clk, pinctrl, DTs/mach, usb-phy, VOP/panel/rgb,
mailbox, pvtm, rknpu, rga, aic8800 wifi, audio codec, thermal/rtc/adc/gmac/touch).
Verified: every patch applies cleanly onto pristine (git apply --check), the full
series reproduces the hardware-verified tree, and the applied source configures +
builds the warden dtb + rockchip DRM drivers (rc=0).
- build/build-kernel.sh: fetch+verify pristine (sha256-pinned) -> apply series ->
warden_defconfig -> zImage + rv1106-warden.dtb. build/warden_defconfig captured.
- CI: `patches-apply` (GitHub-hosted, cached tarball) enforces the series applies;
`kernel-build` (self-hosted warden-sdk runner, dispatch-gated until registered)
runs the full build and uploads the image.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
benches/sim_bench.rs (harness=false, zero deps): fixed-iteration ns/op timing for
hpmcu_tick, cru_poll, modbus_read_holding, rga_improcess, membus_poke_peek. Human
timings to stdout, one JSON trend line per bench to stderr for CI capture. New CI
`bench` job smoke-runs them. Regression-vs-history gating is future work.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
Bring relays.c into warden-sdk/drivers/ behind a relay_io seam (ADR-0005): the
sysfs plumbing is an injectable backend, the gpio root is $WARDEN_GPIO_ROOT-
overridable. Host harness (drivers/relays/test/) reaches 100% MC/DC (40/40
conditions, 100% lines) by combining fake-io unit tests (logic branches incl.
export->node-appears) with real-sysfs integration tests over a scratch tree
(backend fopen/stat branches). CI `mcdc` job enforces 100% across drivers/*/test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf