docs: record Phase 4-lite (group-owned scripts) as shipped, live-resolved

Mark §11.4  shipped with the live-resolution decision (no body
materialization / invalidation fan-out, mirroring Phase 3's config), the
shipped surface, and the deliberate Phase-4b deferrals (group modules + lexical
import resolver, invoke-by-id, live auto-rebinding). Note the sharp edge:
routes.script_id ON DELETE CASCADE means deleting a group script drops
descendant routes. Update the §5.1 materialization box and CLAUDE.md
(current-focus + the data-model invariant now covers group-owned scripts).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-25 20:44:51 +02:00
parent c29e4b9c53
commit 9598db149b
2 changed files with 44 additions and 2 deletions

View File

@@ -440,6 +440,10 @@ Two distinct constraints:
> **deferred plan for *script-body* inheritance (Phase 4)** — where a hot per-request path may
> justify a cache — and as a future optimization for config if a deep tree ever demands it. Read the
> rest of §5.1 as "the materialization design, if/when we need it," not as Phase 3's runtime.
> **Update — Phase 4-lite (§11.4 status) also shipped live, not materialized:** group scripts are
> referenced by id on the runtime hot path and resolved by name only at bind/invoke time, so even
> script inheritance needed no materialized view or invalidation fan-out. The materialization design
> now stands purely as a future optimization, gated on a *measured* cost.
- manager-core **resolves-at-write into a materialized per-app effective view** (§3 rule applied:
sparse merge, env filter, proximity, CoW, `enabled`).
- The orchestrator/executor serve from that view, **keyed by `app_id` + a generation/version**. The
@@ -942,6 +946,44 @@ Resolved items now live inline next to their topic. What genuinely remains:
> apply reconciliation (Phase 5 — Phase 3 ships CRUD API + CLI), and materialization (above).
4. **Group-inherited scripts/modules.** CoW overrides; the **scope-aware module/import resolver +
extension points** (§5.5); cache-invalidation fan-out hardening; versioning/pinning if needed.
> **Status (Phase 4-lite): ✅ shipped — group-owned ENDPOINT scripts, live-resolved (no
> materialization).** Like Phase 3's config, group scripts resolve **live**, not via a cache: a
> script is referenced by id on the runtime hot path (routes/triggers store the resolved id; the
> orchestrator's per-app route trie already scopes dispatch to one app and never reads
> `script.app_id`), and by **name** down the chain only at bind/invoke time. No materialized
> per-app body view, no generation counter, no fan-out invalidation — so §11.4's
> "cache-invalidation fan-out hardening" is **moot for Phase 4-lite** (revisit only if a measured
> hot-path cost appears).
>
> Shipped surface:
> - **Schema:** `0050_group_scripts.sql` makes `scripts` polymorphic-owned (`group_id` XOR
> `app_id`, `scripts_owner_exactly_one` CHECK, per-owner partial-unique `LOWER(name)` indexes),
> `ON DELETE RESTRICT` (code is not data — a group can't be deleted out from under its scripts).
> `Script.app_id` became `Option<AppId>` + `group_id`; the cross-app isolation backstops
> (dispatcher, invoke, trigger-bind, orchestrator `/execute/{id}`) generalized from
> `app_id == row.app_id` to **chain-membership**, fail-closed for app-owned (zero-cost in-memory
> fast path; only a group candidate pays a chain query).
> - **Resolver (reuses `CHAIN_LEVELS_CTE`):** `get_by_name_inherited(app_id, name)` (nearest-owner
> wins; app's own script shadows the inherited group one — CoW) and `is_invocable_by_app(script_id,
> app_id)` (the isolation predicate lifted to the hierarchy; sibling apps' *own* scripts are NOT
> cross-invocable — only group scripts inherit).
> - **Admin + CLI:** `GroupScripts{Read,Write}` caps (viewer+/editor+ on the group, resolved via the
> group-ancestor walk); `POST/GET /groups/{id}/scripts` (endpoint-only, self-contained — modules +
> imports rejected); the by-id `/scripts/{id}` handlers are owner-polymorphic; `pic scripts ls
> --group` / `deploy --group` (create-or-update; `--app`/`--group` mutually exclusive).
> - **`invoke("name", …)`** resolves inherited (a script runs a shared group endpoint; it always
> executes under the *caller's* app context, never the owner's).
> - **Declarative apply:** a manifest route/trigger `script = "name"` resolves to an inherited group
> endpoint (`resolve_inherited_targets` → `name_to_id`), with the diff resolving the group-bound
> route's id → name so **re-apply is idempotent**.
>
> Deliberate Phase-4-lite limits (deferred to Phase 4b): group **modules** + the origin-aware
> (lexical) **import** resolver (§5.5) — group scripts must be self-contained; **invoke-by-id** stays
> app-scoped (inheritance is by-name only); CoW is **redefine-in-app + re-apply** (no live
> auto-rebinding). Sharp edge to track: `routes.script_id` is `ON DELETE CASCADE`, so deleting a
> group script removes descendant apps' bound routes (within group-editor authority; the *group*
> delete itself stays `RESTRICT`).
5. **Project tool maps onto groups.** Nested manifests, attach point, single-owner, server-computed
tree plan, per-env approval gating.
6. **(Much later) group-level collections/topics** — the v1.3 cross-app data-sharing problem, with a