docs: plugin materialization / system-plugin replaceability design#289
Open
Stvad wants to merge 2 commits into
Open
docs: plugin materialization / system-plugin replaceability design#289Stvad wants to merge 2 commits into
Stvad wants to merge 2 commits into
Conversation
Proposes how a user replaces a built-in plugin with their own version while dependents keep working: on-demand "eject" of plugin source into the existing extension-block pipeline, a first-class replaces relation against system toggle ids (position-preserving substitution, built-in fallback on unapproved devices / compile failure), staged seam hardening for cross-plugin behavior imports, and a gated SW module-URL override stage extending #253. Parallel track materializes system type/property definitions via the existing definition-block projector. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DRfy4BX6EUBSoQS6rG8vUE
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…ssemble Per review: the v1 model (static paints first, replacement swaps in via the async dynamic resolve) made ejection second-class — the original ran before being displaced, and forks were barred from changing their data extensions because staticDataExtensions installs pre-React. Revised model: stage 0 loads the kernel floor (DB, approval store, overrides), stage 1 instantiates enabled+approved extension blocks from their pinned compiled output (no Babel on the boot path — approval pre-compiles), stage 2 assembles ONE unified plugin set with substitutions applied at an id-keyed manifest before anything runs. Materialized plugins gain full schema-contribution parity; the flash-of-original and frozen-data-extension caveats disappear, replaced by narrower constraints (idempotent localSchema, dragged-composition pruning, per-workspace scope, boot-blocking policy). Manifest unification is promoted from deferred to Stage-1 prerequisite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DRfy4BX6EUBSoQS6rG8vUE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A design proposal (
docs/plugin-materialization.md) for the question from the voice notes: how does somebody replace a system plugin with their own version, such that everything that relied on the original keeps relying on the replacement? — and whether to materialize system plugins into the DB (all up front vs on demand).Core argument
The two hypothesized abstraction layers (module system with override vs materialize-into-DB) are separable, and most of both already exist:
type:'extension'blocks → Babel → blob import), with synced intent + device-local approval, and the importmap ("@/": "./src/") +preserveModulesbuild means a materialized copy imports the same live module instances the app uses — no walled garden.facet.id, mutators/queries, renderer ids, type membership, deterministic block ids) — those references retarget for free. The irreducible residue is static cross-plugin imports of behavior (daily-notes has ~10 importers ofgetOrCreateDailyNoteet al.), which produce split-brain no materialization scheme fixes.Proposal (staged)
sourcesContent, zero build changes) into an extension block carryingextension:replaces: 'system:<id>'. Boot is restaged so stage 1 instantiates enabled+approved extension blocks from their pinned compiled output (no Babel on the boot path — approval pre-compiles), and stage 2 assembles ONE unified plugin set with substitutions applied at an id-keyed manifest before anything runs: the replaced original never executes, and materialized plugins get full schema-contribution parity (types/schemas/processors/localSchema from the same lifecycle position as static plugins). Substitution requires enabled + approved + instantiated on this device — so unapproved devices, instantiation failures, and safe mode all fall back to the built-in instead of running neither. Default eject output is a wrapper (recompose the original's imported parts) with progressive inlining toward a fork.requiresedges./src/**module, extending Stable /module/<blockId> URLs for user extensions (enables cross-block plugin imports) #253; new boot-path bytes-at-rest problem documented; explicit trigger discipline, not built now.block-type/property-schemablocks from system definitions via the existing definition-block projector (Data-defined contributions over facets — projector-shim pattern #90); shadowing works today by construction.Plus: upgrade/divergence tracking via base-version/base-hash provenance, trust-model analysis (no new execution paths; a synced malicious
replacesblock can't run or suppress anything without device approval), rejected alternatives (materialize-all, blessed auto-approval tier, importmap remapping), an implementation sketch (manifest unification and boot restage as independently landable first steps), and open questions (toggle-id stability as public API, boot-blocking policy for additive extensions, base-source storage, #253 trigger).Notes
docs/extension-seam-gaps.md(2026-06-21) statesdefineVerbFacetis unbuilt, butsrc/facets/verbFacet.ts+ the paste/navigation/action-dispatch verbs have since shipped and are exported fromapi.ts— that doc's headline finding is now partially outdated (not touched in this PR).🤖 Generated with Claude Code
https://claude.ai/code/session_01DRfy4BX6EUBSoQS6rG8vUE