Parent: #1930
What to build
The declared-dependency target grammar now matches DIAL's real resource addressing, {type}/{bucket}/{path…}, uniformly: the current-user placeholder moves from the type slot (current-user/{type}/…) into the bucket slot ({type}/{current-user}/…), written literally with braces. Braces are collision-proof (INVALID_FILE_NAME_CHARS bans them in every path element) and fail closed if the branch is ever missed (a raw brace throws in fromAnyUrl's strict decode).
GLOBAL_VIEW_ROOTS and PERSONAL_TYPED_ROOTS collapse into one DECLARABLE_TYPE_ROOTS set (segment 0, both forms) with the dead public entry dropped — public is a bucket value, never a type, so target.path: "public/somefile" previously passed write-time validation and was silently unresolvable at every request.
- The old early return that let a recognized placeholder skip the root-vocabulary check is deleted, not moved: the segment-0 vocabulary check now runs first, unconditionally, for both forms — otherwise
credentials/{current-user}/… would have been accepted at write time. The same vocabulary check is now also enforced on the read side for concrete (non-placeholder) paths, closing a gap for config-file apps that bypass write-time validation.
validateUserAuthored degenerates to the allowUserResourceDependencies flag check alone: shape validation already runs first and requires segment 0 to be a declarable type, so a root-level "write everything personal" declaration is not expressible under the new grammar at all.
- No back-compat or migration work: this grammar has never shipped to a real deployment. The old spelling is now a 400 at write time — flagged for review.
Acceptance criteria
Blocked by
#1936
Parent: #1930
What to build
The declared-dependency target grammar now matches DIAL's real resource addressing,
{type}/{bucket}/{path…}, uniformly: the current-user placeholder moves from the type slot (current-user/{type}/…) into the bucket slot ({type}/{current-user}/…), written literally with braces. Braces are collision-proof (INVALID_FILE_NAME_CHARSbans them in every path element) and fail closed if the branch is ever missed (a raw brace throws infromAnyUrl's strict decode).GLOBAL_VIEW_ROOTSandPERSONAL_TYPED_ROOTScollapse into oneDECLARABLE_TYPE_ROOTSset (segment 0, both forms) with the deadpublicentry dropped —publicis a bucket value, never a type, sotarget.path: "public/somefile"previously passed write-time validation and was silently unresolvable at every request.credentials/{current-user}/…would have been accepted at write time. The same vocabulary check is now also enforced on the read side for concrete (non-placeholder) paths, closing a gap for config-file apps that bypass write-time validation.validateUserAuthoreddegenerates to theallowUserResourceDependenciesflag check alone: shape validation already runs first and requires segment 0 to be a declarable type, so a root-level "write everything personal" declaration is not expressible under the new grammar at all.Acceptance criteria
skills/{current-user}/andfiles/{current-user}/notes/validate and resolve to the originating user's bucket, keeping type and relative path.current-user/skills/— the previously shipped spelling — is now a 400 at write time.public/somefileis a 400 at write time (was accepted-and-dead).credentials/{current-user}/,keys/{current-user}/, and concretecredentials/<bucket>/…are rejected at write time AND unresolvable at read time.pathIssueschecks segment 0, with no conditional above it;validateUserAuthoredcontains no path inspection.current-userstring remains in main code; in test code the only bare occurrences are deliberate fixtures — the old-spelling regression guards asserting 400, a bare mid-path word pinning it is an ordinary legal folder name, and the config module's inert-parse round-trip fixtures.Blocked by
#1936