test(cli): extension-point journeys + node-key manifest fix + docs (§5.5 C5)

- Move the manifest `extension_points` from a top-level key to an `[app]`/
  `[group]` node key (`ManifestApp`/`ManifestGroup` + a `Manifest::extension_points()`
  accessor). A bare top-level array placed after the node header was silently
  re-nested by TOML into that table and dropped; as a node key it parses
  unambiguously wherever it sits. build_bundle/pull/init updated.
- Journeys (live server + Postgres): a group default body resolves for an
  inheriting app; the app provides its own module and OVERRIDES the EP (the
  inverse of the Phase 4b sealed import); `extension-points ls` shows the
  provider; a body-less EP with no provider fails `pic plan`.
- Re-bless the schema snapshot (new `extension_points` table).
- Docs: §5.5 marked complete (extension points ) + CLAUDE.md current-focus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-29 20:53:18 +02:00
parent 82b4579317
commit a049397bb0
9 changed files with 295 additions and 15 deletions

View File

@@ -552,9 +552,21 @@ trust inversion). The rule:
> site; the `PicloudModuleResolver` reads the importing node from Rhai's `_source` (set to each module's
> owner via `AST::set_source(encode(owner))` before `eval_ast_as_new` — the lexical chaining), and the
> cache is re-keyed by resolved `ScriptId`. Group modules + group-script imports are now allowed
> (`group_scripts_api`), and the single-node apply runs the §5.5 dangling-import `plan` check. **Opt-in
> extension points** (the dynamic-resolution branch + `[extension_points]` manifest declaration) remain
> the one deferred piece of §5.5 — a clean additive follow-up on top of the now-origin-aware resolver.
> (`group_scripts_api`), and the single-node apply runs the §5.5 dangling-import `plan` check.
> **Resolved (extension points ✅) — §5.5 is complete.** Opt-in polymorphism shipped: an extension
> point is a **pure marker** `(owner, name)` (migration 0051 `extension_points`, owner-polymorphic +
> CASCADE — structurally a `secrets` name), authored declaratively as an `[app]`/`[group]` manifest key
> `extension_points = [...]` (a *node key*, so TOML can't mis-nest it); the optional **default body is a
> co-located `kind=module` script**. The resolver's `ModuleSource::resolve_policy(origin, app, name)`
> applies **nearest-declaration-kind-wins** over the importing node's chain (one query: min EP depth vs
> min module depth, EP wins a tie): a concrete module → lexical; an EP marker → **dynamic**, resolved
> against the inheriting app (its override, else the default body up-chain) — `NoProvider` is a hard
> error. Reconcile mirrors `secrets` (name-only diff/prune/state-token); the single-node apply adds the
> **no-provider `plan` check** (every EP visible to an app must have a provider); read-only
> `pic extension-points ls` + `pull` round-trip complete the surface. **Authoring is declarative only**
> (no imperative `add/rm`). Verified e2e: a group default resolves for an inheriting app, and the app
> can **override** it — the deliberate inverse of the Phase 4b sealed/lexical import.
### 5.6 Tree lifecycle: delete, reparent, rename