Skip to content

v0.3: Kconfig-aware configurator + downloadable defconfig fragment#2

Merged
widgetii merged 1 commit into
mainfrom
feat/kconfig-configurator
Jun 5, 2026
Merged

v0.3: Kconfig-aware configurator + downloadable defconfig fragment#2
widgetii merged 1 commit into
mainfrom
feat/kconfig-configurator

Conversation

@widgetii
Copy link
Copy Markdown
Member

@widgetii widgetii commented Jun 4, 2026

Summary

Builds on v0.2's build-time aggregator — same architecture (browser fetches
same-origin only), no new infra, no new files on `OpenIPC/firmware`'s or
`OpenIPC/builder`'s gh-pages. Adds a real "Configure" tab that lets users
tick packages to remove, computes the cascade closure with Kconfig
semantics, and exports a defconfig fragment to append to a board defconfig.

Pairs with OpenIPC/firmware PR for the kconfig-graph emitter. Until
that PR lands and one nightly cycle attaches the assets to release tags,
this PR's prebuild logs "no kconfig assets found in any retained tag"
and the Configure tab stays disabled with a tooltip — `kconfig_available_for`
ends up empty and the gate at `App.tsx` blocks click. By design, not
a bug.

Data path

`scripts/prebuild.mts` now also downloads `kconfig-*.json` from the
most recent nightly tag once per source (graph evolves slowly; per-build
storage isn't worth ~3 GB raw across the retention window). Files land
at:

```
public/data//kconfig/kconfig-graph..json
public/data//kconfig/kconfig-help..json
```

`IndexFile` gains a forward-compatible `kconfig_available_for?: string[]`
field at the source level. Empty / absent → no graph data shipped yet;
populated → the Configure tab works for these platforms. `IndexFile.schema`
stays at 1 — older v0.2-era files (no field) keep parsing without error.
`tests/types-compat.test.ts` covers this.

Dep closure semantics

`src/lib/kconfig.ts` is pure. `closeDisable` iterates to fixed point:

  1. Each pass extends the disabled set with symbols newly disable-able.
  2. A symbol can be disabled only if no still-set ∧ not-in-our-disable-set
    symbol hard-`select`s it. If something still selects it, the disable
    is blocked and the UI explains which symbol is pinning it on.
  3. After the initial sweep, re-checks blocked entries: a symbol previously
    blocked may unblock as its selectors fall in the cascade.

This matches Kconfig's actual save behaviour — telling Kconfig "drop X"
when something still selects X is a silent no-op. The configurator surfaces
the blocker so the user can choose to drop the selector instead.

`defconfigFragment` renders a deterministic, sorted, known-symbol-only
set of `# BR2_PACKAGE_FOO is not set` lines. URL-injected ghost symbols
are filtered at output time.

WhatIfPanel

  • Per-symbol checkbox.
  • Status badge column: `kept` / `would-disable` / `blocked`.
  • Pinned-by column: chips listing the still-enabled hard-selectors for blocked entries.
  • KPI summary: disabled count, blocked count, real rootfs savings via `sizes.json`'s per-package bytes against the disabled closure's package set (no more naive-sum disclaimer the v0.1 stub had), new-headroom projection.
  • "Download defconfig fragment" → `Blob` → `<a download="...">` click. No server round-trip.
  • Help text loads lazily via per-symbol popover on row-label click; missing help is non-fatal.

Bundle invariant extended

`tests/bundle.test.ts` gains the assertion that JS chunks never embed
`releases/download/...kconfig...` URLs. Same regression-guard pattern
that catches the original CORS bug class.

Numbers

v0.2 (shipped) v0.3 (this PR)
Active tests 38 46 (+10 kconfig, +3 types-compat)
JS bundle 166 KB / 53 KB gz 172 KB / 55 KB gz
Initial launch ~60 KB gz unchanged
Per-platform kconfig graph ~14 KB / ~1.8 KB gz
Per-platform kconfig help ~9 KB / ~3.8 KB gz

Bundle still under the 250 KB perf budget.

Test plan

  • `npm test` — 46 active pass, 7 skipped (3 live + 4 bundle pending dist)
  • `npm run build` — clean, bundle invariants pass
  • Manual end-to-end with injected kconfig data: `index.json` exposes `kconfig_available_for`, per-platform `kconfig-graph..json` URL loads with correct schema, BUSYBOX shows `selected_by: [BR2_INIT_BUSYBOX]`
  • Dispatch the workflow on this branch (test+build green, deploy correctly rejected by main-only branch policy) before merge
  • Wait for firmware kconfig-graph PR to merge + one nightly cycle, then merge this
  • After merge: cron-triggered build + deploy populates Pages; Configure tab lights up for platforms with kconfig data; defconfig fragment downloads work

🤖 Generated with Claude Code

Builds on the build-time aggregator from v0.2 — same architecture (browser
fetches same-origin only), no new infra, no new on-firmware-gh-pages files.
Adds a real "Configure" tab that lets users tick packages to remove,
computes the cascade closure with Kconfig semantics, and exports a
defconfig fragment the user appends to their board defconfig.

Pairs with the firmware-side PR that ships kconfig-graph.<plat>.json +
kconfig-help.<plat>.json as release assets next to sizes.<plat>.json
(OpenIPC/firmware PR for the emitter). Until that lands and one nightly
cycle attaches the assets, the Configure tab stays disabled with a
tooltip — `kconfig_available_for` ends up empty and the gate at
App.tsx blocks click. By design, not a bug.

Data path

  scripts/prebuild.mts now also downloads kconfig-*.json from the most
  recent nightly tag once per source (graph evolves slowly; per-build
  storage isn't worth the ~3 GB raw it would cost across the retention
  window). Files land at:

      public/data/<source>/kconfig/kconfig-graph.<platform>.json
      public/data/<source>/kconfig/kconfig-help.<platform>.json

  IndexFile gains a forward-compatible `kconfig_available_for?: string[]`
  at the source level. Empty/absent means no graph data shipped yet;
  populated means "the Configure tab works for these platforms".
  IndexFile schema stays at 1 — older v0.2-era files (no field) keep
  parsing without error (covered by tests/types-compat.test.ts).

Dep closure semantics

  src/lib/kconfig.ts is pure. The closeDisable function:

    1. Iterates to fixed point: each pass extends the disabled set with
       symbols newly disable-able given the current state.
    2. A symbol can be disabled only if no surviving (still-set ∧ not
       in our disable set) symbol hard-selects it. If something still
       selects it, the disable is "blocked" and the UI explains which
       symbol is still pinning it on.
    3. After the initial sweep, re-checks blocked entries: a symbol
       previously blocked may unblock as its selectors fall in the
       cascade.

  This matches what Kconfig actually does at .config save time — telling
  Kconfig "drop X" when something still selects X is a silent no-op. The
  configurator surfaces the blocker so the user can choose to drop the
  selector instead.

  defconfigFragment renders a deterministic, sorted, known-symbol-only
  set of `# BR2_PACKAGE_FOO is not set` lines — URL-injected ghost
  symbols are filtered out at output time.

WhatIfPanel

  Per-symbol checkbox. Status column shows kept / would-disable /
  blocked badges. Pinned-by column shows the symbol chips for blocked
  entries (clickable in a future iteration to jump to the blocker).
  KPI summary: disabled count, blocked count, estimated rootfs savings
  (real, via sizes.json's per-package bytes against the disabled
  closure's package set — not the naive sum disclaimer the v0.1 stub
  carried), new-headroom projection. "Download defconfig fragment"
  triggers a Blob → <a download="..."> click, no server round-trip.

  Help text loads lazily — the per-symbol popover opens on row label
  click; missing help is non-fatal (the column just isn't expandable).

Bundle invariant extended

  tests/bundle.test.ts gains the assertion that JS chunks never embed
  `releases/download/...kconfig...` URLs. Same regression guard pattern
  the original CORS bug catches.

Numbers

  - 46 active tests (was 38): +10 kconfig.test.ts (cascade / block /
    unblock / determinism), +3 types-compat.test.ts
  - JS bundle: 172 KB raw / 55 KB gzipped (+6 KB vs v0.2, under the
    250 KB perf budget)
  - Initial launch byte budget unchanged: ~60 KB gzipped
  - Per-platform kconfig graph: ~14 KB raw / ~1.8 KB gzipped
  - Per-platform kconfig help: ~9 KB raw / ~3.8 KB gzipped

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@widgetii widgetii merged commit daea694 into main Jun 5, 2026
2 of 3 checks passed
@widgetii widgetii deleted the feat/kconfig-configurator branch June 5, 2026 15:18
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