A new quality job runs the linter battery (clippy, cppcheck, shellcheck,
ruff, lizard, jscpd, cargo-audit per crate, scc for the LOC denominator)
and feeds tools/quality/score.py: findings convert to SQALE remediation
minutes, debt ratio grades A-F on SonarQube's published grid, and a
separate worst-of security axis can only drag the overall grade down.
The job uploads the full quality.json breakdown and fails when the
security grade is worse than C.
The badges job now renders all four SVGs in-runner with anybadge
(shields hex palette); the previous img.shields.io curls were the one
external-service dependency left in the pipeline. quality.svg is seeded
at the current locally-computed grade (A, debt ratio 0.42%).
Also fixes the two ruff findings the battery surfaced in flowgen.py.
Replace every em dash with real punctuation (rewrites, not hyphen swaps)
in docs, code comments, scripts, configs, and the port records; convert
en dashes, curly quotes, ellipsis glyphs, arrows, and section signs to
ASCII; drop machine-writing tell phrases from living docs. ADR titles
now use a colon. The M2 bring-up DTS model string carried an em dash
into the patch series and its record echoes; fixed at both, and the full
series re-verified to apply cleanly onto pristine 6.18.46. One comment
in freshness.h deliberately names the em dash glyph the UI renders as
the unknown mark; that is data, kept as prose naming it.
Verified: cargo tests (sim, config-lint, rs485-bridge), shellcheck,
both driver MC/DC harnesses, patches-apply.
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
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
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
The c8a3 brick was a memory-map fault no behavioural sim can catch: a
boot-loaded coprocessor firmware dropped at 0x40000, which is a reserved-memory
carve-out on Thunder-Boot boards but plain kernel RAM on ours. config-lint is
the static gate for it — parse the rkbin loader .ini for every LOADERn=Hpmcu
LOAD_ADDR, parse the target devicetree for reserved-memory ranges, fail if any
MCU load lands outside a reservation.
Tests encode the brick as a regression against the REAL Thunder-Boot .ini
(Hpmcu@0x40000): fails with no rtos@40000 node, passes once reserved; our
board's non-TB loader (no boot-loaded MCU) always passes. 6/6 green; CLI
verified against the on-disk rkbin .ini files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf