Commit Graph
9 Commits
Author SHA1 Message Date
Yichen Jiang 1144cf1e8e refactor(boot): use native entry diagnostics and static plugin declarations 2026-09-11 15:54:14 +08:00
Yichen Jiang afa57ae093 refactor(boot): keep containment and composition internals off the package root
The root entry now exports only what the CLI, the plugin inventory, the
plugin manager, and tool-cordis use, plus the parameter and result types of
those operations. The contained group and its registry, the external-layer
composition, the row-ownership helpers, the probe format number, and the
runtime option type stay module exports the package's own tests import.
2026-09-08 16:37:16 +08:00
Yichen Jiang d6f5160591 test(boot): type the id-less group row in the ownership spec 2026-09-08 16:16:12 +08:00
Yichen Jiang 20102bcd52 fix(boot): commit user-disabled rows with the composition and name a user row's trust
The runtime's user-disabled row ids now come from the committed composition —
composeProfileStack collects them from the user layers it composed — so a
rejected or half-written patch file no longer makes the list disagree with the
running tree or fail to parse. The inventory attributes a row disabled through
a group the user disabled to the user, through the shared userDisablesEntry
walk, and lists a user-layer row the composition left out under the new user
trust instead of builtin.
2026-09-08 16:13:03 +08:00
Yichen Jiang 5f0ad7c4b1 fix(boot): keep disabled bundles disabled across runs and catch config-override duplicates
reconcileInstalledBundles now enables only the dependencies a run added, so
a bundle the user disabled keeps its state through an unrelated install or
removal. Row registration records the group each row is declared under: a
config override restates a row only under that group, while the same id twice
in one list or under another group is a duplicate — a contained bundle is left
out and a built-in layer fails loud, instead of the Loader rejecting the update
or moving the row at mount.
2026-09-08 16:01:05 +08:00
Yichen Jiang aaf58eb5bf fix(boot): own the rows a config override sets, queue recompositions, and let records follow the configuration
Second review round on the contained-group composition:

- Ownership scanned only `insert` rows, so an external bundle could set a
  built-in row's id as a child through `{ id: own, config: [...] }` and the
  Loader re-parented that row under the bundle while `originOf` still named
  the built-in layer. The rows an id-targeted patch sets as a group's config
  now count as the layer's own: an external bundle that sets an id another
  layer owns is left out whole, a built-in layer that takes another layer's
  id fails loud, and a layer restating its own child is not a repeat.
- `recompose` read the committed profile before awaiting the update, so a
  watcher firing while a bundle was being enabled composed the tree before
  the enable and applied it after; the plugin vanished while `layers` kept
  listing it. Recompositions now queue, each reading what the previous one
  committed, and a rejection stays its own caller's outcome.
- A patch that replaced a built-in target's config removed the wrapper the
  bundle had put there, yet later inserts still targeted the detached
  wrapper and vanished. The composition forgets the wrapper on such an
  override and creates a new one under the same id.
- Generated wrapper ids bypassed the duplicate check, so a bundle declaring
  a row spelled like a wrapper produced two nodes with one id. Declared and
  generated ids now go through one registration.
- Failure records outlived their rows when a bundle update dropped a failed
  row without unmounting the group, or when a pending row sat in a plain
  nested group whose id the record named. A contained group now drops the
  records of rows its configuration no longer names after each update, and
  the audit records every row under the contained group that isolates it.
- `applyEntryPatches` assigned override values by reference, so applying a
  patch list carried a later insert's rows back into the patch object; the
  vendored include now clones object-valued overrides as it does inserts.
2026-09-07 11:28:19 +08:00
Yichen Jiang 0caceb2ced fix(boot): commit a recomposition only once the root include accepts it
`ProfileRuntime.recompose({ reloadBundles: true })` replaced its profile
before the Loader had accepted the update, so a rejected recomposition left
`current`, `layers`, and `originOf` describing a stack that never mounted.
The user patch watchers took a second path through the launcher's
`composeLive`, which wrote the new conflicts into the failure registry
before the update and kept composing from the runtime's stale profile.
Conflict records shared the registry with row failures under a fabricated
entry id and a group id that meant three different things, so the plugin
inventory attributed a conflict to the layer that won the id, and a bundle
disabled after failing kept its import and apply records forever.

The runtime now holds one committed composition — profile, stack ownership,
conflicts — and publishes a candidate only after `entry.update` resolves;
`originOf` reads the committed owners instead of recomputing them. The
watchers call `recompose` through a `reapply` callback, so every live change
goes through the one entry point. Conflicts live on the runtime with their
messages and never enter `pluginFailures`, whose records name rows that
reached the Loader; the inventory lists them under the layer that lost. A
contained group clears its rows' records when it unmounts.
2026-09-05 22:19:36 +08:00
Yichen Jiang 0914a152ff fix(boot): compose external bundles in written order with one wrapper per target
`composeExternalLayer` inserted every id-targeted patch after all inserts,
so a bundle that replaced a group's config and then appended to it lost the
appended row under the `runtime` stage; two inserts into the same built-in
group produced two wrapper groups with one id, which the Loader rejected as
a duplicate and the boot failed; and an id a bundle inserted twice was
indexed once, escaped the ownership check, and failed the boot the same way.

The composition now inserts the bundle's group empty and emits the bundle's
patches in the order written, re-targeting root inserts into the group and
gathering every insert into one built-in group in one wrapper. A repeated id
is a conflict of the bundle with itself: the bundle is left out whole and
recorded like any other conflict, and a built-in layer that repeats an id
fails loud. `composeProfileStack` renders each contained layer once and
returns the owner of every id with the patches and conflicts, and one walker
in `patch-rows.ts` replaces the five recursive scans of inserted rows.
2026-09-05 21:23:11 +08:00
Yichen Jiang 5d742503ea feat(boot): own row ids across the stack instead of prefixing external bundles
An external bundle keeps the ids its patch declares. composeProfileStack
decides ownership before anything mounts: built-in and boot-staged layers
claim first and a duplicate among them fails the boot, a contained bundle
that collides is left out whole, a user-layer insert of a taken id is
dropped, and every row left out is a `conflict` record in pluginFailures,
printed at boot and replaced on each recomposition. Boot, live reloads, and
--dump-config compose through the same function, so the dump shows the
contained groups the tree mounts.

The prefix made a user patch, a third-party manager's disabled rows, and a
bundle's own `!!js` id comparison miss silently; the note records both cases.
2026-09-04 12:02:11 +08:00