Parent: #1930
What to build
A new enhancement-chain function, ResolveResourceDependenciesFn, runs for chat-completion calls to an application, before the per-request key is assigned — while the context still carries the originating user, so the reach checks evaluate the USER's permissions, not the app's (load-bearing timing, made loud by an assert).
Per declared record: resolve the target (current-user/… against the originating user's bucket, concrete global-view paths as-is), verify fresh against the user's reach, intersect with the content-bound admin-consent record, and bake the passing grants into the key the application will hold.
- Fail-closed per record: unreachable or unconsented targets simply do not grant; a
required record that cannot resolve fails the call with an explicit 403 instead of the app half-working. Reach is all-or-nothing per record — no half-grants.
- Malformed records (config-file apps bypass write-time validation) are unresolvable — never a crash, never a grant; the read side re-enforces the write-side vocabulary (kind, READ/WRITE only).
- Runtime audit on the
DIAL_RESOURCE_DEPS_AUDIT stream: one grant line per run, one denial line (unreachable or unconsented), one runtime-fail line when a required record rejects the call.
- Enforcement is unchanged — the existing per-request permission rule honors the grants, folder targets by prefix.
Smoke test proves the v1 happy path end to end: admin-authored app declares a personal prompts folder (current-user/prompts/dep-smoke/, write, required) → admin grants → user chats → the app's key writes inside the declared folder (200) and is refused outside it (403).
Acceptance criteria
Blocked by
#1933
Parent: #1930
What to build
A new enhancement-chain function,
ResolveResourceDependenciesFn, runs for chat-completion calls to an application, before the per-request key is assigned — while the context still carries the originating user, so the reach checks evaluate the USER's permissions, not the app's (load-bearing timing, made loud by an assert).Per declared record: resolve the target (
current-user/…against the originating user's bucket, concrete global-view paths as-is), verify fresh against the user's reach, intersect with the content-bound admin-consent record, and bake the passing grants into the key the application will hold.requiredrecord that cannot resolve fails the call with an explicit 403 instead of the app half-working. Reach is all-or-nothing per record — no half-grants.DIAL_RESOURCE_DEPS_AUDITstream: one grant line per run, one denial line (unreachable or unconsented), one runtime-fail line when a required record rejects the call.Smoke test proves the v1 happy path end to end: admin-authored app declares a personal prompts folder (
current-user/prompts/dep-smoke/, write, required) → admin grants → user chats → the app's key writes inside the declared folder (200) and is refused outside it (403).Acceptance criteria
requiredrecord failing reach/consent: the call fails with an explicit error + runtime-fail audit line.Blocked by
#1933