Ground location occurrences with contextual selectors

This commit is contained in:
2026-08-08 14:56:43 +00:00
parent fc76805075
commit 51d62de1f3
13 changed files with 181 additions and 129 deletions

View File

@@ -18,22 +18,20 @@ import (
)
const (
ReferenceSlot = "location_registry"
MaxBytes = 1048576
emptyPrompt = `{"locations":[]}`
ReferenceSlot = "location_registry"
MaxBytes = 1048576
emptyRegistryContent = `{"locations":[]}`
)
// Registry is an immutable, validated location registry prepared for prompt
// grounding. All accessors return defensive copies.
type Registry struct {
bound bool
list dnd.LocationRegistry
canonical []byte
digest string
projectionDigest string
identityDigest string
promptInput contracts.LLMInputMaterial
lookupByID map[string]int
bound bool
list dnd.LocationRegistry
canonical []byte
digest string
identityDigest string
lookupByID map[string]int
}
// Resolver selects and memoizes immutable location registry views.
@@ -100,15 +98,13 @@ func locationReferenceSpec() registryresolver.ReferenceSpec {
}
func emptyRegistry() *Registry {
content := []byte(emptyPrompt)
projectionDigest := semanticDigest(content)
content := []byte(emptyRegistryContent)
identityDigest := semanticDigest(content)
return &Registry{
list: dnd.LocationRegistry{Locations: []dnd.Location{}},
canonical: append([]byte(nil), content...),
projectionDigest: projectionDigest,
identityDigest: projectionDigest,
promptInput: contracts.NewLLMInputMaterial(ReferenceSlot, locationcodec.MediaType, content, projectionDigest, ""),
lookupByID: map[string]int{},
list: dnd.LocationRegistry{Locations: []dnd.Location{}},
canonical: append([]byte(nil), content...),
identityDigest: identityDigest,
lookupByID: map[string]int{},
}
}
@@ -136,16 +132,14 @@ func loadRegistry(referenceContent []byte) (*Registry, error) {
return nil, fmt.Errorf("encode location prompt projection: %w", err)
}
digest := semanticDigest(content)
projectionDigest := semanticDigest(projection)
identityDigest := semanticDigest(projection)
return &Registry{
bound: true,
list: list,
canonical: append([]byte(nil), content...),
digest: digest,
projectionDigest: projectionDigest,
identityDigest: projectionDigest,
promptInput: contracts.NewLLMInputMaterial(ReferenceSlot, locationcodec.MediaType, projection, projectionDigest, ""),
lookupByID: lookupByID,
bound: true,
list: list,
canonical: append([]byte(nil), content...),
digest: digest,
identityDigest: identityDigest,
lookupByID: lookupByID,
}, nil
}
@@ -185,15 +179,6 @@ func (r *Registry) Digest() string {
return r.digest
}
// ProjectionDigest returns the digest of the exact source-free prompt
// projection, including for an unbound or empty registry.
func (r *Registry) ProjectionDigest() string {
if r == nil {
return ""
}
return r.projectionDigest
}
// IdentityDigest returns the digest of the ordered ID/name identity projection
// used by deterministic consumers.
func (r *Registry) IdentityDigest() string {
@@ -211,16 +196,6 @@ func (r *Registry) Count() int {
return len(r.list.Locations)
}
// PromptInput returns the ordered ID-and-name projection without evidence or
// reference provenance. It remains available only while the location occurrence
// extractor transitions to contextual grounding.
func (r *Registry) PromptInput() contracts.LLMInputMaterial {
if r == nil {
return contracts.LLMInputMaterial{}
}
return r.promptInput.Clone()
}
// Lookup returns the canonical location for an exact durable location ID.
func (r *Registry) Lookup(id string) (dnd.Location, bool) {
if r == nil {

View File

@@ -14,36 +14,26 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/locations/identity"
)
func TestResolveUnboundRegistryHasEmptyProjection(t *testing.T) {
func TestResolveUnboundRegistryHasEmptyIdentity(t *testing.T) {
registry, err := Resolve(contracts.ReferenceSet{})
if err != nil {
t.Fatal(err)
}
input := registry.PromptInput()
if registry.Bound() || registry.Digest() != "" || registry.Count() != 0 || string(input.Content) != emptyPrompt {
t.Fatalf("unbound registry = %#v, input = %#v", registry, input)
if registry.Bound() || registry.Digest() != "" || registry.Count() != 0 || string(registry.CanonicalBytes()) != emptyRegistryContent {
t.Fatalf("unbound registry = %#v", registry)
}
if registry.ProjectionDigest() == "" || input.Digest != registry.ProjectionDigest() || input.OriginURI != "" {
t.Fatalf("projection digest/input = %q/%#v", registry.ProjectionDigest(), input)
if registry.IdentityDigest() == "" {
t.Fatal("unbound registry identity digest is empty")
}
}
func TestResolveProjectsOrderedLocationsWithoutEvidence(t *testing.T) {
func TestResolveRetainsSeparateDurableAndIdentityDigests(t *testing.T) {
registry := resolveList(t, registryFixture())
if !registry.Bound() || registry.Count() != 2 || registry.Digest() == "" {
t.Fatalf("registry identity = bound %t count %d digest %q", registry.Bound(), registry.Count(), registry.Digest())
}
projection := string(registry.PromptInput().Content)
if !strings.Contains(projection, `"locations":[{"id":`) || !strings.Contains(projection, `"name":"The Tavern"`) || !strings.Contains(projection, `"name":"The Tavern"},{"id":`) {
t.Fatalf("projection ordering = %s", projection)
}
for _, forbidden := range []string{"source_refs", "source_id", "session-alpha"} {
if strings.Contains(projection, forbidden) {
t.Fatalf("projection leaked %q: %s", forbidden, projection)
}
}
if registry.PromptInput().Digest != registry.ProjectionDigest() || registry.Digest() == registry.ProjectionDigest() {
t.Fatalf("full/projection digests = %q/%q", registry.Digest(), registry.ProjectionDigest())
if registry.IdentityDigest() == "" || registry.Digest() == registry.IdentityDigest() {
t.Fatalf("full/identity digests = %q/%q", registry.Digest(), registry.IdentityDigest())
}
}
@@ -62,12 +52,10 @@ func TestRegistryLookupUsesIDAndReturnsDefensiveCopies(t *testing.T) {
locations[0].SourceRefs[0].SourceID = "changed"
canonical := registry.CanonicalBytes()
canonical[0] = '['
input := registry.PromptInput()
input.Content[0] = '['
if next, ok := registry.Lookup(first.ID); !ok || next.Name != first.Name || next.SourceRefs[0].SourceID != "session-alpha" {
t.Fatalf("registry mutated through accessor: %#v, %t", next, ok)
}
if registry.CanonicalBytes()[0] != '{' || registry.PromptInput().Content[0] != '{' {
if registry.CanonicalBytes()[0] != '{' {
t.Fatal("registry bytes mutated through accessor")
}
}