What
Command::Grant's parent scope is always the vault root, so a folder that already sits inside a scope this vault granted cannot be granted on.
Mechanism
Engine::grant (crates/engine/src/facade.rs, landed by #1346) builds its ParentScopePlan from vault_root_scope() and resolves that root's direct_child_scope_index. A folder inside an already-granted scope has a different parent scope root, whose commitment, ledger, seeds and index are the ones the grant must re-seal — and whose own record carries an ascent link, so its re-seal owes one (ScopeRootIdentity::owes_ascent_link is the caller's to get right, per ParentScopePlan's own contract).
The rotation and revoke arms already reach an interior scope root: Engine::owner_scope takes its target from the vault root's index and carries the ancestor node seed a gated read of one needs. The grant arm needs the same treatment on the parent side, one level deeper.
Acceptance
- A grant on a folder inside an already-granted scope resolves that scope as its parent, re-seals it with its ascent link intact, and inserts the new child into its direct-child-scope index rather than the vault root's.
- A parent scope this session holds no seed for is a fail-closed refusal, never a grant minted under a fabricated derivation.
- The existing
grant-target-already-names-a-scope refusal still fires: this widens which parents are reachable, never which nodes may be re-minted.
Part of #635
What
Command::Grant's parent scope is always the vault root, so a folder that already sits inside a scope this vault granted cannot be granted on.Mechanism
Engine::grant(crates/engine/src/facade.rs, landed by #1346) builds itsParentScopePlanfromvault_root_scope()and resolves that root'sdirect_child_scope_index. A folder inside an already-granted scope has a different parent scope root, whose commitment, ledger, seeds and index are the ones the grant must re-seal — and whose own record carries an ascent link, so its re-seal owes one (ScopeRootIdentity::owes_ascent_linkis the caller's to get right, perParentScopePlan's own contract).The rotation and revoke arms already reach an interior scope root:
Engine::owner_scopetakes its target from the vault root's index and carries the ancestor node seed a gated read of one needs. The grant arm needs the same treatment on the parent side, one level deeper.Acceptance
grant-target-already-names-a-scoperefusal still fires: this widens which parents are reachable, never which nodes may be re-minted.Part of #635