feat(plugins): starter-pack kind and its checker (SP1, #1873) #1951

Merged
jason.woltje merged 4 commits from sp1 into refactor 2026-11-12 21:46:49 +00:00
Owner

Starter packs slice SP1 (#1873): the starter-pack plug-in kind and its checker. Design: docs/plans/2026-11-12_starter-packs-design.md section 8, under Jason's full send of 11-12 about 21:05Z.

  • kinds.mjs: the starter-pack kind, interface 1, export starterPack, switch none. namesFor refuses any setting for a none kind with bad-setting, so a pack never stays switched on.
  • starter-pack.mjs (new):
    • checkPack: closed key sets with bounds. Definitions go through checkDefinition, and template refs are pinned by templateHash. Each role must narrow its template, credential lines included. The checker also applies the hire, board, workflow and saved-packs rules, and it never quotes document text in a refusal.
    • installedStarterPacks: lists shipped and installed packs with their origin. A bad shipped pack fails the listing; a bad installed pack is omitted and logged. A native promise refuses, and the module's own then never runs.
    • readSavedTemplate: the account must match ID_PATTERN and the name NAME. Directory and file get lstat, owner and mode checks (account dir 0700, file 0600), with no symlinks or hard links and a 256 KiB cap. The open uses O_NOFOLLOW and confirms dev/ino.
  • refusals.mjs: starter-pack-invalid, starter-pack-not-found and starter-pack-template-changed, each with a rail and a next step that names no path.
  • loader.test.mjs / kinds.test.mjs: the expected kind maps list the new kind. These are test-only edits the new kind forces.

Tests: starter-pack.test.mjs, 25 tests. Red before: the module is missing, and plugins naming the kind refuses as an unknown kind. The lane gate ran 253 selected files, 2854/1; the one failure is the known CHAT-01 base red in conversation reader.test.mjs. test-config.sh passed 55/0. The Lead check ran separately under env -i.

Reviews (fresh Opus delegates):

  • Build: approve.
  • Security: approve, with 8 P3s, 4 of them fixed.
  • Security re-check: approve, with 3 P3s, all fixed in the last commit. That commit is 10 source lines; the Lead read it, and it is covered by new tests and mutation checks.

Deferred:

  • SP4 runs checkComposition on resolved template refs.
  • A broken plugins.lock loses its original code, which needs a loader change.
  • Other kinds' install refusals show up in the pack listing log.
Starter packs slice SP1 (#1873): the `starter-pack` plug-in kind and its checker. Design: `docs/plans/2026-11-12_starter-packs-design.md` section 8, under Jason's full send of 11-12 about 21:05Z. - `kinds.mjs`: the `starter-pack` kind, interface 1, export `starterPack`, switch `none`. `namesFor` refuses any setting for a `none` kind with `bad-setting`, so a pack never stays switched on. - `starter-pack.mjs` (new): - `checkPack`: closed key sets with bounds. Definitions go through `checkDefinition`, and template refs are pinned by `templateHash`. Each role must narrow its template, credential lines included. The checker also applies the hire, board, workflow and saved-`packs` rules, and it never quotes document text in a refusal. - `installedStarterPacks`: lists shipped and installed packs with their origin. A bad shipped pack fails the listing; a bad installed pack is omitted and logged. A native promise refuses, and the module's own `then` never runs. - `readSavedTemplate`: the account must match `ID_PATTERN` and the name `NAME`. Directory and file get lstat, owner and mode checks (account dir 0700, file 0600), with no symlinks or hard links and a 256 KiB cap. The open uses `O_NOFOLLOW` and confirms dev/ino. - `refusals.mjs`: `starter-pack-invalid`, `starter-pack-not-found` and `starter-pack-template-changed`, each with a rail and a next step that names no path. - `loader.test.mjs` / `kinds.test.mjs`: the expected kind maps list the new kind. These are test-only edits the new kind forces. Tests: `starter-pack.test.mjs`, 25 tests. Red before: the module is missing, and `plugins` naming the kind refuses as an unknown kind. The lane gate ran 253 selected files, 2854/1; the one failure is the known CHAT-01 base red in conversation reader.test.mjs. `test-config.sh` passed 55/0. The Lead check ran separately under `env -i`. Reviews (fresh Opus delegates): - Build: approve. - Security: approve, with 8 P3s, 4 of them fixed. - Security re-check: approve, with 3 P3s, all fixed in the last commit. That commit is 10 source lines; the Lead read it, and it is covered by new tests and mutation checks. Deferred: - SP4 runs `checkComposition` on resolved template refs. - A broken `plugins.lock` loses its original code, which needs a loader change. - Other kinds' install refusals show up in the pack listing log.
jason.woltje added 4 commits 2026-11-12 21:46:46 +00:00
KINDS gains "starter-pack" (interface 1, export starterPack) with a new
switch "none": namesFor refuses any setting for it with bad-setting, so an
org file can never switch a pack on through plugins.

starter-pack.mjs adds checkPack(doc, {origin}), narrowsTemplate and
widerLines (step 7's line rules), installedStarterPacks (shipped and
installed packs through loadInstalled, origin set; a refused core pack
fails the listing, a refused installed pack is omitted and logged) and
readSavedTemplate (account against ID_PATTERN, name against NAME, then
lstat, no symlink, owner uid, mode 0600 or narrower, 256 KiB checked
before the read, then parseJsonFileText and checkPack with origin saved).

refusals.mjs gains starter-pack-invalid, starter-pack-not-found and
starter-pack-template-changed.
Adding a kind adds its key to every loaded() map. Outside the SP1 owner
list; kept as its own commit so it can be dropped or moved.
readSavedTemplate refuses a hard-linked file and an account directory
open to group or other; an open error other than ELOOP reads "can't be
read", not "symlink"; a file that grows during the read has its own
message. installedStarterPacks refuses a promise from starterPack() and
swallows its later rejection. Tests pin each refusal's message, so a
check that a neighbour shadows is still covered.
Recognise only a native promise from starterPack() and attach the catch
through Promise.prototype.then, so an installed pack's own then or then
getter never runs; such a pack is omitted and logged again instead of
failing the listing. A link count of 0 after the open reports the file
changed, not a hard link. Tests: a throwing then getter, a self-resolving
thenable, and a 0000 file refusing as unreadable.
jason.woltje merged commit 755ba3fa8b into refactor 2026-11-12 21:46:49 +00:00
Sign in to join this conversation.