From 1516631a306c2e4e8faa31eb2113838bce5ee9e6 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 13 Oct 2026 22:47:21 +0000 Subject: [PATCH] Record accepted namespace scope and filtering decisions --- docs/roadmap/namespaces.md | 186 +++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 82 deletions(-) diff --git a/docs/roadmap/namespaces.md b/docs/roadmap/namespaces.md index 98464d8..51d1117 100644 --- a/docs/roadmap/namespaces.md +++ b/docs/roadmap/namespaces.md @@ -1,8 +1,10 @@ # Namespaces And Scoped Resource Lookup -Status: feature roadmap; not implemented. The reserved-slash naming convention -and reference-resolution rules are accepted target behavior. The public scope -API, discovery surface, and migration details remain open as identified below. +Status: feature roadmap; not implemented. The reserved-slash naming convention, +reference-resolution rules, and decisions 1–3 (scope shape, validation, and +integrated discovery filtering) are accepted target behavior. Decision 4, +catalog migration, remains open. Exact Go names and option types remain delivery +details within the accepted semantics. ## Purpose @@ -81,11 +83,11 @@ those outside the new grammar require migration. This intentional format break is accepted in favor of unambiguous resolution; the migration work is described below. -## Proposed Consumer Scope +## Accepted Consumer Scope -Prefer a lightweight, immutable view over an existing engine. The following -sketch illustrates the consumer experience only; it does not settle a method -name, return type, error behavior, or final signature: +Provide a lightweight, immutable view over an existing engine with independent +prompt, profile, and backend scopes. The following sketch illustrates the +accepted shape; method/type names and final signatures remain provisional: ```go weather, err := engine.WithNamespaces(Namespaces{ @@ -95,8 +97,8 @@ weather, err := engine.WithNamespaces(Namespaces{ // Handle err, then use local prompt and profile names through weather. ``` -This sketch follows the recommendation in open decision 1; these declarations -do not exist yet. +This sketch follows [decision 1](#1-public-scope-shape-accepted); these +declarations do not exist yet. The view would share the engine's captured configuration, clients, and execution machinery. Selecting a namespace would not rebuild the engine or duplicate its @@ -146,29 +148,20 @@ applying a scope. No escape syntax for unqualified IDs is introduced here. ### Separate Resource Scopes -A consumer may want prompt shorthand under `weatherreporter` and profile -shorthand under `openrouter` in the same request. Evaluate independently -selectable prompt, profile, and backend scopes instead of assuming one namespace -must apply to every field. A prompt's configured default profile needs no caller -profile scope because the stored reference is already exact. - -Independent scopes are recommended where the consumer needs shorthand, but the -scope options and the meaning of a convenience `InNamespace` operation are -still open. Define which scopes apply to unqualified names in each request -field; qualified references always bypass them. Include any directly supplied -backend selection in this decision. +A consumer can select prompt shorthand under `weatherreporter` and profile +shorthand under `openrouter` in the same request. Each resource type has an +independent scope, as defined in [decision 1](#1-public-scope-shape-accepted). +A prompt's configured default profile needs no caller profile scope because +the stored reference is already exact. Scope syntax and existence handling are +defined in [decision 2](#2-scope-validation-accepted). ## Discovery And Collection Organization -Namespace-aware discovery is supporting work: consumers should be able to -identify available namespaces and list their resources without each application -inventing parsing and filtering rules. Keep this proportionate to the primary -scoped-lookup feature. - -Decide whether scoped listing returns full identities, local names alongside -full identities, or another explicit shape. A caller must be able to tell how -to pass a listed name back to a scoped operation without double qualification. -Retain the existing unscoped listing contracts. +Namespace enumeration and filtering are integrated into PromptKit's listing +APIs. Consumers select a filter and receive matching full resource identities; +they do not need to fetch all resources and filter them themselves. Defaults, +explicit filter overrides, and result semantics are defined in +[decision 3](#3-discovery-accepted). Derive namespace membership from the qualified identifier's namespace component: `weather` must not include `weatherreporter/...`. Nested slash-separated @@ -232,19 +225,19 @@ if a concrete consumer need emerges. ## Delivery Outline -1. Settle the open API decisions below and audit existing identifier usage - against the accepted grammar. Record explicit mappings and coordinated - migration work. +1. Resolve decision 4 and audit existing identifier usage against the accepted + grammar. Record explicit mappings and coordinated migration work. Finalize + Go names and option types that express the accepted decisions 1–3. 2. Implement the smallest public scope surface with shared name qualification and existing engine collaborators. Cover supported operations consistently. -3. Add the selected discovery behavior and representative consumer examples. +3. Add integrated namespace filtering and representative consumer examples. 4. Verify compatibility and scoped resolution through focused offline tests, following the [testing policy](../policy/testing.md). Update GoDoc, the format reference, consumer guide, and relevant internal documentation as behavior lands; retire this roadmap when its work is complete. -Acceptance cases for the settled rules, with scope/discovery details subject to -the selected API contract: +Acceptance cases for the settled rules; migration-specific cases follow the +eventual decision 4: - Two prompt scopes resolve the same local name to their respective prompts; a missing name never resolves from the other scope or an unqualified entry. @@ -259,21 +252,32 @@ the selected API contract: stored default references and omitted selections retain their meaning. - Scoped views coexist concurrently and share backend capacity with ordinary calls on the underlying engine. +- Scope creation rejects invalid syntax, accepts well-formed absent namespaces, + and treats empty scope fields as no scope. Missing resources fail at lookup; + qualified references remain usable even when the default namespace is absent. +- Listing without an explicit filter uses the resource's view scope, or lists + all resources when no scope is selected. Explicit named, unqualified-only, + and all-namespaces filters override that default for the current listing only. +- A valid namespace with no matches produces an empty listing. Namespace + filtering respects resource type and exact namespace boundaries. - Existing exact-ID calls using valid IDs, intentional overlays, and prompt versions retain their behavior. Migrated catalog references resolve to their intended resources without collisions. -- Listings and reported identities support the chosen local/full-ID round trip. +- Listings preserve full IDs and separate prompt versions. Qualified results + round-trip through either engine or view; unqualified results round-trip + through the engine without a corresponding scope. - Representative validation, inspection, execution, and smoke-test paths use the same qualification rules without duplicating every test at every layer. -## Open Decisions And Recommendations +## Decisions And Remaining Recommendation -Each recommendation below is a proposal for the next decision, not an accepted -contract. Alternatives preserve the settled naming and resolution rules. +Decisions 1–3 are accepted; their alternatives are retained as considered but +not selected. Decision 4 remains open, with its recommendation and alternative +unchanged for further discussion. -### 1. Public Scope Shape +### 1. Public Scope Shape (Accepted) -**Recommended: an immutable view with independent resource scopes.** Provide one +**Accepted: an immutable view with independent resource scopes.** Provide one engine operation that accepts a small value with prompt, profile, and backend namespace fields and returns a scoped view or an error. The illustrative `WithNamespaces` and `Namespaces` names above are provisional. Empty fields mean @@ -295,27 +299,28 @@ same namespace in several fields happens once when creating a view and states exactly which resource types it affects. A convenience operation can follow if that repetition is a demonstrated burden. -**Alternative: one namespace per immutable view, applied to all resource -types.** A one-string operation would scope every unqualified prompt, profile, -and backend selection; callers would use qualified references for resources in -other namespaces. This is smaller and reads naturally for self-contained +**Considered alternative, not selected: one namespace per immutable view, +applied to all resource types.** A one-string operation would scope every +unqualified prompt, profile, and backend selection; callers would use qualified +references for resources in other namespaces. This is smaller and reads naturally for self-contained application collections. It is less convenient when prompts and profiles habitually come from different namespaces, because profile overrides would repeatedly need qualification. Prefer it if consumers primarily use prompts' configured defaults and rarely select profiles themselves. -**Alternative: explicit scope values supplied per operation.** Add scope to -request values and appropriate inspection/listing options instead of returning -a view. This makes every invocation self-contained and suits applications that +**Considered alternative, not selected: explicit scope values supplied per +operation.** Add scope to request values and appropriate inspection/listing +options instead of returning a view. This makes every invocation self-contained +and suits applications that choose a different scope for every request. It spreads scope through more call sites and API shapes, and consumers would need their own reusable wrapper to achieve the requested select-once experience. The immutable view is therefore the better default for the use case discussed so far. -### 2. Scope Validation +### 2. Scope Validation (Accepted) -**Recommended: validate syntax when creating the view; resolve existence when -an operation selects a resource.** In the recommended scope value, the empty +**Accepted: validate syntax when creating the view; resolve existence when +an operation selects a resource.** In the scope value, the empty string means no scope. Reject a nonempty namespace containing `/` or consisting only of whitespace. Otherwise preserve its exact spelling, with no trimming, case folding, or additional character whitelist. This distinguishes omission @@ -334,9 +339,9 @@ successful scope construction as validation of a prompt or execution profile. The tradeoff is that a misspelled but syntactically valid namespace is detected only when listed or used, rather than during view construction. -**Alternative: require each selected namespace to have resources of its -corresponding type when creating the view.** For an immutable assembled engine, -this can catch spelling mistakes immediately and gives startup checks useful +**Considered alternative, not selected: require each selected namespace to have +resources of its corresponding type when creating the view.** For an immutable +assembled engine, this can catch spelling mistakes immediately and gives startup checks useful feedback without attempting execution. However, it rejects empty optional collections and a view intended to use qualified references outside its default namespace. A namespace having one member also does not establish that the @@ -344,36 +349,54 @@ particular required prompt/profile exists. Prefer this approach only if eager inventory validation is itself an intended consumer contract; it should not be an accidental consequence of implementing scope creation. -### 3. Discovery +### 3. Discovery (Accepted) -**Recommended: namespace enumeration and resource listings that retain full -IDs.** Enumerate the distinct named namespaces present in final effective +**Accepted: integrated namespace filtering and full-identity results.** +Enumerate the distinct named namespaces present in final effective prompts, profiles, and backends. Allow filtering that enumeration by resource type so a prompt picker need not show profile-only namespaces. Provide scoped listings for the supported resource types, adding a backend inventory surface -as part of this work if needed. Keep the existing unscoped listing signatures -and their all-resource behavior. +as part of this work if needed. Namespace filtering is an explicit option in +the library listing API, not a consumer-side filtering helper. Preserve existing +unfiltered engine calls and their result contracts; the exact Go option shape +remains to be specified. -A scoped listing filters by the selected scope for that resource type and -returns full IDs, retaining prompt versions as separate identity fields. -Without a scope, it lists all resources of that type. Unqualified resources -remain in unscoped listings; provide an explicit unqualified-only listing filter -so callers can distinguish that group from all namespaces. Do not invent a -named namespace such as `default` for those IDs. Namespace enumeration excludes -that unnamed group. Use deterministic ordering consistent with existing listing -contracts. +| Selection | Listing behavior | +| --- | --- | +| No explicit filter on the engine | All namespaces, including unqualified resources | +| No explicit filter on a scoped view | The selected scope for that resource type; all resources of that type if no scope is selected | +| Explicit named namespace | Resources in exactly that namespace | +| Explicit unqualified-only filter | Resources whose IDs have no namespace | +| Explicit all-namespaces filter | All resources of that type, including unqualified resources | -Full IDs can be passed unchanged to either scoped or unscoped operations, which -avoids double qualification and preserves identity in logs and user interfaces. -A small public name-splitting helper can supply namespace/local components for -display without requiring applications to duplicate parsing rules. This keeps -existing identity shapes useful and avoids introducing collection metadata just -to support filtering. The tradeoff is that consumers wanting separate display -fields perform that explicit decomposition. +An explicit filter replaces the view's default for the current listing; it +does not intersect with that default or mutate the view. A named namespace +filter uses the namespace syntax rules in decision 2. A well-formed namespace +with no matching resources returns an empty listing. Represent default, named, +unqualified-only, and all-namespaces selections distinctly in the API; do not +overload an empty string to mean several of them. -**Alternative: add a richer inventory API returning full ID, namespace, local -name, resource type, and prompt version where applicable.** Preserve existing -list methods and expose the structured form separately. This is attractive for +Return full IDs, retaining prompt versions as separate identity fields. Do not +invent a named namespace such as `default` for unqualified resources. Namespace +enumeration excludes that unnamed group. Use deterministic ordering consistent +with existing listing contracts. + +Qualified result IDs can be passed unchanged to either scoped or unscoped +operations. Unqualified IDs remain literal unqualified identities: when an +explicit all-namespaces or unqualified-only filter exposes one through a scoped +view, use the original engine or a view without that resource scope to select +it exactly. A listing filter does not change subsequent execution scope. This +preserves the accepted lookup rules without introducing an escape syntax. + +Integrated filtering gives consumers a direct way to discover the desired +resources, while full identities keep logs and subsequent selections +unambiguous. Namespace/local-name display decomposition is separate from +filtering; a public splitting helper is not required by this decision. + +**Considered alternative, not selected: add a richer inventory API returning +full ID, namespace, local name, resource type, and prompt version where +applicable.** Preserve existing list methods and expose the structured form +separately. This is attractive for Scriptorium-style library browsers: grouping and display require no parsing, and one inventory operation can cover all resource kinds. It creates another public representation and overlaps existing listing APIs, so its value should @@ -381,7 +404,7 @@ justify that maintenance. Prefer it if downstream clients routinely need a combined catalog view rather than a resource picker or namespace filter. In either approach, the full ID remains the authoritative selection value. -### 4. Catalog Migration +### 4. Catalog Migration (Open) **Recommended: coordinate the grammar change with adoption of provider namespaces for ordinary maintained catalog profiles.** Use provider namespaces @@ -427,7 +450,6 @@ it if the audit reveals that broad catalog renaming would impose substantial downstream disruption relative to current demand. The delimiter, two-component qualified grammar, syntax-based resolution, and -acceptance of the required breaking migration are settled. Resolve the remaining -API and delivery choices around that foundation: consumers select a scope once -and use local or explicitly qualified names predictably within an assembled -library. +decisions 1–3 are settled. The need for a breaking migration is accepted, but +decision 4's migration strategy, exact mappings, and release coordination remain +open for further discussion.