Add shared resource naming primitives
This commit is contained in:
@@ -19,7 +19,7 @@ contributor workflow and validation.
|
||||
| `internal/catalog` | Selects released keyed catalog assets and PromptKit-owned profile definitions as ConfigKit underlays; effective validation belongs to the configuration loader. | [Catalog adapter](../../internal/catalog/catalog.go), [internal sources](sources.md#profiles-and-built-ins) |
|
||||
| `internal/configuration` | Loads keyed backend, profile, and prompt configuration through ConfigKit; records effective field presence, validates final values including optional diagnostic metadata, captures indexed prompt definitions, referenced message content, compiled templates, input metadata snapshots, sorted identity-only profile and prompt inventories, backend and presence-aware profile snapshots, and an explicit read-only inspection view. | [Typed loader](../../internal/configuration/load.go), [identity listing](../../internal/configuration/listing.go), [internal sources](sources.md#typed-configuration-loading) |
|
||||
| `internal/capacity` | Owns engine-local bounded execution admission and FIFO model-generation permits for limited backend IDs, including cancellation-safe waiter removal and client wrapping. | [Internal capacity management](capacity.md) |
|
||||
| `internal/domain` | Defines internal framework values for requests, artifacts, prompt definitions, requirement inspection, profiles, execution targets, rendering, generation, validation, and source-neutral smoke diagnostics, and owns source-neutral invariants for shared execution settings, OpenAI-compatible base endpoints, session identifiers, and output contracts. Source parsing, required fields, other source-specific normalization, defaulting, and boundary-specific error classification remain with their callers. | [Domain declarations](../../internal/domain/domain.go), [requirement values](../../internal/domain/requirements.go), [endpoint invariant](../../internal/domain/endpoint.go) |
|
||||
| `internal/domain` | Defines internal framework values for requests, artifacts, prompt definitions, requirement inspection, profiles, execution targets, rendering, generation, validation, and source-neutral smoke diagnostics, and owns source-neutral invariants for shared execution settings, OpenAI-compatible base endpoints, session identifiers, output contracts, and resource-identity syntax and qualification. Source parsing, required fields, other source-specific normalization, defaulting, and boundary-specific error classification remain with their callers. | [Domain declarations](../../internal/domain/domain.go), [requirement values](../../internal/domain/requirements.go), [endpoint invariant](../../internal/domain/endpoint.go) |
|
||||
| `internal/engine` | Assembles selected catalog and caller configuration into one snapshot, then constructs shared capacity, rendering, validation, generation, repair, the ordinary runner, and the smoke tester for each engine. Built-in engines provide the diagnostic HTTP collaborator; injected-client engines do not. It delegates identity listing directly to the captured configuration inventory rather than the runner. | [Engine assembly](../../internal/engine/engine.go), [internal sources](sources.md#profiles-and-built-ins), [internal runner](runner.md) |
|
||||
| `internal/generation` | Coordinates frozen built-in HTTP attempts and one-shot injected calls through active backend permits, including bounded status retries, cancellable backoff, ordinary attempt observations, and invocation-local evidence for diagnostics. | [Internal generation coordination](generation.md) |
|
||||
| `internal/defaults` | Defines application-neutral framework constants and constructs the default execution target. It contains no CLI, server, or inbound HTTP limits. | [Framework defaults](../../internal/defaults/defaults.go) |
|
||||
|
||||
@@ -97,7 +97,8 @@ on consumers or on Scriptorium.
|
||||
`internal/domain` owns source-neutral invariants for values shared across
|
||||
multiple input and execution boundaries, including execution-setting bounds,
|
||||
OpenAI-compatible base endpoints, session identifiers, and output-contract
|
||||
legality. Callers retain source parsing, required-field rules, other
|
||||
legality, plus shared resource-identity syntax and qualification. Callers retain
|
||||
source parsing, required-field rules, other
|
||||
source-specific normalization, defaulting, error classification, and policy
|
||||
specific to their own boundary.
|
||||
|
||||
|
||||
@@ -334,6 +334,11 @@ Scope: small private domain helpers and focused behavior tests.
|
||||
Validation: `GOWORK=off go test ./internal/domain`. Test selected naming behavior,
|
||||
not a helper call sequence or invented character restrictions.
|
||||
|
||||
Completion: private resource-ID, namespace, local-key, qualification, and
|
||||
namespace-extraction helpers are implemented with focused grammar and spelling
|
||||
tests. Validation passed: `GOWORK=off go test ./internal/domain` and
|
||||
`git diff --check`.
|
||||
|
||||
## Stage 3: Make Configuration Processing Path-Aware
|
||||
|
||||
Scope: preparation refactor inside `internal/configuration`; current format stays.
|
||||
|
||||
40
internal/domain/resource_identity.go
Normal file
40
internal/domain/resource_identity.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package domain
|
||||
|
||||
import "strings"
|
||||
|
||||
func validResourceID(id string) bool {
|
||||
if !strings.Contains(id, "/") {
|
||||
return strings.TrimSpace(id) != ""
|
||||
}
|
||||
if strings.Count(id, "/") != 1 {
|
||||
return false
|
||||
}
|
||||
namespace, localID, _ := strings.Cut(id, "/")
|
||||
return namespace != "" && localID != ""
|
||||
}
|
||||
|
||||
func validResourceNamespace(namespace string) bool {
|
||||
return strings.TrimSpace(namespace) != "" && !strings.Contains(namespace, "/")
|
||||
}
|
||||
|
||||
func validLocalResourceKey(key string) bool {
|
||||
return strings.TrimSpace(key) != "" && !strings.Contains(key, "/")
|
||||
}
|
||||
|
||||
func qualifyResourceID(id, namespace string) (string, bool) {
|
||||
if !validResourceID(id) || (namespace != "" && !validResourceNamespace(namespace)) {
|
||||
return "", false
|
||||
}
|
||||
if strings.Contains(id, "/") || namespace == "" {
|
||||
return id, true
|
||||
}
|
||||
return namespace + "/" + id, true
|
||||
}
|
||||
|
||||
func resourceNamespaceFromID(id string) (string, bool) {
|
||||
if !validResourceID(id) || !strings.Contains(id, "/") {
|
||||
return "", false
|
||||
}
|
||||
namespace, _, _ := strings.Cut(id, "/")
|
||||
return namespace, true
|
||||
}
|
||||
110
internal/domain/resource_identity_test.go
Normal file
110
internal/domain/resource_identity_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidResourceID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
want bool
|
||||
}{
|
||||
{name: "ordinary", id: "draft", want: true},
|
||||
{name: "dotted", id: "group.prompt.v2", want: true},
|
||||
{name: "preserved surrounding whitespace", id: " draft ", want: true},
|
||||
{name: "qualified", id: "group/draft", want: true},
|
||||
{name: "qualified components preserve spelling", id: " Group / draft ", want: true},
|
||||
{name: "empty", id: "", want: false},
|
||||
{name: "blank", id: " \t", want: false},
|
||||
{name: "missing namespace", id: "/draft", want: false},
|
||||
{name: "missing local id", id: "group/", want: false},
|
||||
{name: "multiple separators", id: "group/subgroup/draft", want: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := validResourceID(test.id); got != test.want {
|
||||
t.Fatalf("validResourceID(%q) = %t, want %t", test.id, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidResourceNamespace(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespace string
|
||||
want bool
|
||||
}{
|
||||
{name: "ordinary", namespace: "group", want: true},
|
||||
{name: "dotted", namespace: "group.v2", want: true},
|
||||
{name: "preserved whitespace", namespace: " group ", want: true},
|
||||
{name: "empty", namespace: "", want: false},
|
||||
{name: "blank", namespace: " \t", want: false},
|
||||
{name: "slash", namespace: "group/subgroup", want: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := validResourceNamespace(test.namespace); got != test.want {
|
||||
t.Fatalf("validResourceNamespace(%q) = %t, want %t", test.namespace, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidLocalResourceKey(t *testing.T) {
|
||||
if !validLocalResourceKey("draft.v2") || !validLocalResourceKey(" draft ") {
|
||||
t.Fatal("expected nonblank slash-free keys to be valid")
|
||||
}
|
||||
for _, key := range []string{"", " \t", "group/draft"} {
|
||||
if validLocalResourceKey(key) {
|
||||
t.Errorf("validLocalResourceKey(%q) = true, want false", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQualifyResourceID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
namespace string
|
||||
want string
|
||||
wantOK bool
|
||||
}{
|
||||
{name: "without scope", id: "draft", want: "draft", wantOK: true},
|
||||
{name: "with scope", id: "draft", namespace: "group", want: "group/draft", wantOK: true},
|
||||
{name: "qualified bypass", id: "other/draft", namespace: "group", want: "other/draft", wantOK: true},
|
||||
{name: "preserve spelling", id: " draft ", namespace: " Group ", want: " Group / draft ", wantOK: true},
|
||||
{name: "omission remains caller policy", id: "", wantOK: false},
|
||||
{name: "malformed ID", id: "group/draft/extra", namespace: "group", wantOK: false},
|
||||
{name: "malformed scope", id: "draft", namespace: "group/subgroup", wantOK: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, ok := qualifyResourceID(test.id, test.namespace)
|
||||
if got != test.want || ok != test.wantOK {
|
||||
t.Fatalf("qualifyResourceID(%q, %q) = (%q, %t), want (%q, %t)", test.id, test.namespace, got, ok, test.want, test.wantOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceNamespaceFromID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
namespace string
|
||||
wantOK bool
|
||||
}{
|
||||
{name: "qualified", id: "team/item", namespace: "team", wantOK: true},
|
||||
{name: "same-prefix namespace stays distinct", id: "team-extra/item", namespace: "team-extra", wantOK: true},
|
||||
{name: "unqualified", id: "item", wantOK: false},
|
||||
{name: "malformed", id: "team/sub/item", wantOK: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, ok := resourceNamespaceFromID(test.id)
|
||||
if got != test.namespace || ok != test.wantOK {
|
||||
t.Fatalf("resourceNamespaceFromID(%q) = (%q, %t), want (%q, %t)", test.id, got, ok, test.namespace, test.wantOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user