feat(modules): owner-aware module lookup — lexical chain walk (Phase 4b C1)

Make the module-loading contract origin-aware so group-owned modules
become resolvable and imports can resolve lexically (§5.5).

- `ModuleScript` gains a polymorphic owner (`app_id`/`group_id` + `owner()`),
  mirroring `Script`.
- `ModuleSource::lookup(&cx, name)` -> `resolve(origin: ScriptOwner, name)`:
  resolution walks the group chain rooted at the importing script's
  defining node (nearest-owner-wins), not the inheriting app's view.
- `PostgresModuleSource::resolve` branches on origin: app-rooted
  (`CHAIN_LEVELS_CTE`) or a new group-rooted `GROUP_CHAIN_LEVELS_CTE`,
  joining `kind='module'` rows.

The executor resolver passes a temporary `App(cx.app_id)` origin (behaviour-
preserving for app scripts; true lexical origin lands in C3). Group scripts
still can't carry imports until C4, so no trust-inversion window opens here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-26 07:07:04 +02:00
parent 3ef3038eb7
commit 7fef098b48
6 changed files with 158 additions and 61 deletions

View File

@@ -319,8 +319,19 @@ impl ModuleResolver for PicloudModuleResolver {
))
})?;
// C1 (Phase 4b): resolve via the owner-aware `resolve`. Until C3
// threads the importing script's true defining node through, the
// origin is the calling app — behaviour-preserving for app-owned
// scripts (group scripts can't yet carry imports). The app-rooted
// chain walk additionally lets an app import an ancestor group's
// modules, which is the intended Phase 4b capability.
let lookup_result: Result<Option<picloud_shared::ModuleScript>, ModuleSourceError> =
tokio::task::block_in_place(|| handle.block_on(self.source.lookup(&self.cx, path)));
tokio::task::block_in_place(|| {
handle.block_on(
self.source
.resolve(picloud_shared::ScriptOwner::App(self.cx.app_id), path),
)
});
let module_row = match lookup_result {
Ok(Some(m)) => m,