Ground item occurrences by canonical names

This commit is contained in:
2026-08-08 14:43:04 +00:00
parent ece1bca460
commit 8e680cf96e
13 changed files with 112 additions and 93 deletions

View File

@@ -185,7 +185,7 @@ func (r *Registry) Digest() string {
return r.digest
}
// ProjectionDigest returns the SHA-256 digest of the exact ID/name prompt
// ProjectionDigest returns the SHA-256 digest of the exact names-only prompt
// projection, including for an unbound or empty registry.
func (r *Registry) ProjectionDigest() string {
if r == nil {
@@ -202,8 +202,8 @@ func (r *Registry) Count() int {
return len(r.list.Items)
}
// PromptInput returns the ordered ID/name registry projection as a content-safe
// prompt input. Evidence and reference provenance are omitted.
// PromptInput returns the ordered names-only registry projection as a
// content-safe prompt input. Durable IDs, evidence, and provenance are omitted.
func (r *Registry) PromptInput() contracts.LLMInputMaterial {
if r == nil {
return contracts.LLMInputMaterial{}
@@ -242,7 +242,6 @@ func semanticDigest(content []byte) string {
}
type projectedItem struct {
ID string `json:"id"`
Name string `json:"name"`
}
@@ -253,7 +252,7 @@ type projectedItemRegistry struct {
func promptProjection(list dnd.ItemRegistry) ([]byte, error) {
projection := projectedItemRegistry{Items: make([]projectedItem, len(list.Items))}
for index, item := range list.Items {
projection.Items[index] = projectedItem{ID: item.ID, Name: item.Name}
projection.Items[index] = projectedItem{Name: item.Name}
}
return json.Marshal(projection)
}

View File

@@ -25,16 +25,16 @@ func TestResolveUnboundRegistryHasExactEmptyProjection(t *testing.T) {
}
}
func TestResolveProjectsOrderedIDsAndNamesWithoutEvidence(t *testing.T) {
func TestResolveProjectsOrderedNamesWithoutEvidence(t *testing.T) {
registry := resolveRegistry(t, fixture())
if !registry.Bound() || registry.Digest() == "" || registry.Count() != 2 {
t.Fatalf("registry identity = bound %t digest %q count %d", registry.Bound(), registry.Digest(), registry.Count())
}
want := `{"items":[{"id":"` + identity.DeriveID("Silver Key") + `","name":"Silver Key"},{"id":"` + identity.DeriveID("Healer's Kit") + `","name":"Healer's Kit"}]}`
want := `{"items":[{"name":"Silver Key"},{"name":"Healer's Kit"}]}`
if got := string(registry.PromptInput().Content); got != want {
t.Fatalf("prompt projection = %s, want %s", got, want)
}
for _, forbidden := range []string{"source_refs", "source_id", "session-alpha"} {
for _, forbidden := range []string{"item:sha256:", "source_refs", "source_id", "session-alpha"} {
if strings.Contains(string(registry.PromptInput().Content), forbidden) {
t.Fatalf("projection leaked %q: %s", forbidden, registry.PromptInput().Content)
}
@@ -52,6 +52,9 @@ func TestRegistryAccessorsAndLookupsAreDefensive(t *testing.T) {
if item, ok := registry.LookupID(identity.DeriveID("Healer's Kit")); !ok || item.Name != "Healer's Kit" {
t.Fatalf("LookupID() = %#v, %t", item, ok)
}
if _, ok := registry.Lookup("Unknown Item"); ok {
t.Fatal("Lookup() accepted an unknown item")
}
items := registry.Items()
items[0].Name = "changed"