Skip to content

fix(everything-dev): stamp app.* slots only — plugins are declaration-scoped (rc.26 child-deploy hotfix) - #657

Merged
elliotBraem merged 1 commit into
mainfrom
fix/app-only-slot-stamping
Oct 29, 2026
Merged

elliotBraem merged 1 commit into
mainfrom
fix/app-only-slot-stamping

Conversation

@elliotBraem

Copy link
Copy Markdown
Contributor

The incident

Merging #656 released rc.26 — the first release-carrying slots manifest and the first CLI that stamps. Children deploying with the rc.26 CLI got every non-authored slot in the release manifest's slots map stamped into their leaf payload — including the parent's plugins.* slots. For children that declare their own plugins set (e.g. feedback.nearbuilders.org), that broke deploys in CI:

Release-scoped slots (2.0.0-rc.26): stamped app.auth, app.auth.ui, app.host, plugins.ai, plugins.ai.ui, plugins.proposals, plugins.registry, plugins.template, plugins.votes
Error: Canonicalized publish payload fails a consumer boot — plugins.ai: Invalid input

Two compounding defects, both in the stamp policy:

  1. Extends contract violation. A child that declares any plugins object owns the whole plugin domain in one wholesale replace (merge.ts:109-113 — merged.plugins = cleanNullSentinels(child.plugins)). Stamping the parent's plugin slots into the leaf made the child compose the parent's entire plugin fleet — polugins it deliberately excluded.
  2. Schema-fatal entries. The stamped plugins.<id>.ui record is {production, pin} — PluginUiConfigSchema.name is required, and the wholesale replace means the parent's name never fills it in → the boot-check (plugins.ai: Invalid input) correctly refused the publish.

Nothing was published — the boot-check fires before the registry write. Live apps are unaffected; CI deploys are blocked for rc.26 children with authored plugins.

The fix

The stamp policy allowlists app.* slots only (^app\.[^.]+(\.ui)?$) — the base-runtime slots children never author (app.auth, app.auth.ui, app.host), which merge per-key with the parent, so the parent's ui.name and friends fill the stamped entries and the schema stays valid. The release manifest keeps recording every slot (a record, not a policy); the filter lives at stamp time, so no root-side action is needed.

Tests

  • Unit (the rc.26 regression, pinned): a child with authored plugins stamps only app.*; its plugins object stays byte-equal untouched.
  • e2e: simulateDeploy now runs verifyResolvedBootShape over the merged shape — the coverage that would have caught this. The incident-replay child declares its own plugin while the root release carries plugins.* slots: stamped list is app-only, merged.plugins equals the child's exact set, merged shape schema-valid. (The e2e fixture's live parent config also gains the required app.host/app.api keys the schema parse demands.)

Full framework suite: 1692 passed. Typecheck, biome, effect-lint clean.

Residual (recorded, not hotfix material)

Children that declare no plugins still inherit the parent's plugin pins live — pre-existing behavior, unchanged by this fix; the composition gate still catches their skew pre-publish. Making plugin inheritance release-scoped would require full-entry recording in the slots map — a future seam.

Rollout notes for the rc.26 release body

rc.26 children that author their own plugins are CI-blocked until bos upgrade picks this fix up in the next release; live apps are unaffected.

…-scoped (rc.26 child-deploy hotfix)

The rc.26 release-scoped stamping wrote every non-authored slot from the
release manifest's slots map into the child's leaf payload — including the
parent's plugins.* slots. A child that declares its own `plugins` object owns
the whole plugin domain in one wholesale replace (mergeBosConfigWithExtends),
so the stamps (a) expanded the child's composed plugin set to the parent's
entire fleet and (b) produced schema-invalid plugins.<id>.ui records — the
stamped ui entry carries no required `name`, and the wholesale replace never
fills it — which the boot-check refused: children with authored plugins
(feedback.nearbuilders.org) could not deploy; plugins.ai: Invalid input.

Fix: the stamp policy allowlists app.* slots only (`^app\.[^.]+(\.ui)?$`) —
the base-runtime slots children never author and which merge per-key with
the parent (so the parent's ui.name and friends fill the stamped entries).
The release manifest keeps recording every slot (a record, not a policy).

Tests:
- unit: the rc.26 regression pinned — a child with authored plugins stamps
  only app.*; its plugins object stays byte-equal untouched.
- e2e: simulateDeploy now runs verifyResolvedBootShape over the merged
  shape (the coverage that would have caught this), and the incident-replay
  child declares its own plugin while the root release carries plugins.*
  slots — stamped list app-only, merged.plugins equals the child's exact
  set, merged shape schema-valid. The e2e fixture's live parent config also
  gains the required app.host / app.api keys the schema parse needs.
@elliotBraem
elliotBraem merged commit ccaa284 into main Oct 29, 2026
10 checks passed
@elliotBraem
elliotBraem deleted the fix/app-only-slot-stamping branch October 29, 2026 03:26
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