Parent: #1930
What to build
Two halves, deliberately shipped as one story — reviewers should treat the mixed diff as intentional:
Half 1 — mint-site coverage on the conversation APIs. ResolveResourceDependenciesFn now runs in the same chain position on the Anthropic Messages (MessagesBaseController.prepareUpstreamRoute) and Responses (ResponsesController.handleRequestBody) entry points, completing the v1 conversation mint-site coverage (route/feature/deploy sites remain a documented v1 limitation — an app invoked through those surfaces gets no dependency grants in v1).
An e2e suite (ResourceDependencyApiTest) covers the v1 checklist as test cases: the consented flow, required-without-consent failing the call explicitly, optional-without-consent degrading silently (target stays off-limits), withdrawal stopping the run immediately, declaration change invalidating the grant (content binding), public-target grants following the originating user's own reach exactly (pointer semantics), and the audit events carrying app, targets, user and trace id (the grant line also carries access types).
Half 2 — whole-branch review hardening (found by the whole-branch security/review pass; its commit is chronologically adjacent and folds in here):
- Root-call-only guard semantics. The timing guard threw on every hop that arrives with a per-request key — the normal state for an interceptor's final call back to a declaring app (breaking even the root user request) and for every chained call into one. Resolution is now honestly root-call-only: hops under a per-request key are skipped — callable, just without grants — because the originating user's reach is not evaluable under a key context. Chained composition is recorded as phase-2 machinery.
- Internal-type ban. The placeholder type segment enforces the validator's vocabulary:
ResourceTypes.of() maps internal engine types, so a current-user/credentials/ declaration from a config-file or admin-authored app could otherwise resolve into the user's secret-bearing blobs.
- Union grants. Two records sharing a target combine rights instead of overwriting each other; malformed concrete paths are caught (unresolvable, never a crash); target permission lookups are batched into one rule-chain walk; consent is checked before any resolution work; the audit's access-type join tolerates null elements. The
perRequestReceivers[app] half of the bake — which never propagated on plain chained calls and served no reachable flow — is removed; the grant lives solely in the key's own shared map.
New e2e: a chained call into a declaring app stays callable without receiving grants (the case the old guard broke).
Acceptance criteria
Blocked by
#1934
Parent: #1930
What to build
Two halves, deliberately shipped as one story — reviewers should treat the mixed diff as intentional:
Half 1 — mint-site coverage on the conversation APIs.
ResolveResourceDependenciesFnnow runs in the same chain position on the Anthropic Messages (MessagesBaseController.prepareUpstreamRoute) and Responses (ResponsesController.handleRequestBody) entry points, completing the v1 conversation mint-site coverage (route/feature/deploy sites remain a documented v1 limitation — an app invoked through those surfaces gets no dependency grants in v1).An e2e suite (
ResourceDependencyApiTest) covers the v1 checklist as test cases: the consented flow, required-without-consent failing the call explicitly, optional-without-consent degrading silently (target stays off-limits), withdrawal stopping the run immediately, declaration change invalidating the grant (content binding), public-target grants following the originating user's own reach exactly (pointer semantics), and the audit events carrying app, targets, user and trace id (the grant line also carries access types).Half 2 — whole-branch review hardening (found by the whole-branch security/review pass; its commit is chronologically adjacent and folds in here):
ResourceTypes.of()maps internal engine types, so acurrent-user/credentials/declaration from a config-file or admin-authored app could otherwise resolve into the user's secret-bearing blobs.perRequestReceivers[app]half of the bake — which never propagated on plain chained calls and served no reachable flow — is removed; the grant lives solely in the key's own shared map.New e2e: a chained call into a declaring app stays callable without receiving grants (the case the old guard broke).
Acceptance criteria
current-user/credentials/(internal types) never resolve; records sharing a target combine rights (union), never overwrite.:server:testgreen on CI Linux (modulo the documented pre-existing Windows-environmental failures that fail identically on pristinedevelopment).Blocked by
#1934