Finish the D&D audit fixes
This commit is contained in:
@@ -111,7 +111,10 @@ messages before its task and instructions. Identity, transcript, and
|
||||
campaign-reference messages are ephemeral in the extraction prompts; the scene
|
||||
prompt marks transcript and campaign references ephemeral, and the NPC registry
|
||||
is ephemeral where spell and combat prompts use it. Unused shared assets are
|
||||
neither mounted nor fingerprinted.
|
||||
neither mounted nor fingerprinted. Universal extraction-evidence and output
|
||||
policy lives only in the shared extraction assets; package-owned prompt files
|
||||
retain artifact-specific rules. The scene prompt keeps its separate output rule
|
||||
because it does not render the extraction-evidence asset.
|
||||
|
||||
Schema helpers load embedded JSON Schema with identity and digest metadata,
|
||||
return defensive copies, and expose a diagnostics map that omits schema bytes.
|
||||
|
||||
@@ -15,5 +15,5 @@ attribute relevant nonstandard rulings to the GM or table.
|
||||
|
||||
Unmatched actors and targets remain permitted.
|
||||
|
||||
For every factual detail in a turn, cite all supporting transcript units in the
|
||||
turn-level source_refs collection.
|
||||
Place all supporting transcript ranges for a turn in its turn-level source_refs
|
||||
collection.
|
||||
|
||||
@@ -8,5 +8,4 @@ Relationships must be stated or directly demonstrated by cited transcript
|
||||
units, not inferred from game lore.
|
||||
|
||||
Return aliases and relationships as arrays, including empty arrays when there
|
||||
are none. Return only NPC records supported by the transcript and preserve
|
||||
observed display spelling.
|
||||
are none. Preserve observed display spelling.
|
||||
|
||||
@@ -7,9 +7,9 @@ a stable alias or title, or an individually useful distinguishing description.
|
||||
|
||||
Exclude human players, transcript speakers, and the GM as out-of-world people,
|
||||
player characters identified by the player or party references, incidental or
|
||||
hypothetical name drops, corrected transcription mistakes, characters mentioned
|
||||
only by reference material, indistinguishable crowds or groups, and temporary
|
||||
summoned creatures or spell effects without a persistent individual identity.
|
||||
hypothetical name drops, corrected transcription mistakes, indistinguishable
|
||||
crowds or groups, and temporary summoned creatures or spell effects without a
|
||||
persistent individual identity.
|
||||
|
||||
Keep each description concise and limited to facts established by the
|
||||
transcript. Include only explicitly supported aliases and relationships.
|
||||
|
||||
@@ -21,10 +21,6 @@ Use the canonical spell-name catalog to select spell names. Do not return a
|
||||
spell name absent from that catalog, even when it is suggested by general D&D
|
||||
knowledge or reference material.
|
||||
|
||||
Use player, party, and glossary reference material only to clarify source text.
|
||||
Do not return spells, casters, or effects that are mentioned only in reference
|
||||
material.
|
||||
|
||||
Effects and narrative descriptions are session records, not rules summaries.
|
||||
Report only mechanics, explanations, and outcomes established by the cited
|
||||
transcript units. Preserve the table's observed resolution without silently
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
Extract Dungeons & Dragons spell-cast artifacts from the provided transcript.
|
||||
|
||||
Extract only spell casts that are supported by the transcript. Do not infer
|
||||
spells from general D&D knowledge or from table chatter that does not identify a
|
||||
spell being cast.
|
||||
Do not infer a spell cast from general D&D knowledge or from table chatter that
|
||||
does not identify a spell being cast.
|
||||
|
||||
Describe the session as it was played and adjudicated. The transcript is
|
||||
authoritative for what happened in this session, even when a table ruling may
|
||||
|
||||
@@ -4,5 +4,3 @@ Use only the provided transcript and reference material. Source text may contain
|
||||
transcription errors, repeated lines, incomplete sentences, and misheard proper
|
||||
nouns. Reference material, when present, is supporting context only and must not
|
||||
be treated as a source of extracted events by itself.
|
||||
|
||||
Return only valid JSON matching the configured response schema.
|
||||
|
||||
@@ -50,7 +50,16 @@ func ReferencePromptInput(slot contracts.ResolvedReferenceSlot) []byte {
|
||||
if items[i].Digest != items[j].Digest {
|
||||
return items[i].Digest < items[j].Digest
|
||||
}
|
||||
return string(items[i].Content) < string(items[j].Content)
|
||||
if comparison := bytes.Compare(items[i].Content, items[j].Content); comparison != 0 {
|
||||
return comparison < 0
|
||||
}
|
||||
if items[i].Origin.Type != items[j].Origin.Type {
|
||||
return items[i].Origin.Type < items[j].Origin.Type
|
||||
}
|
||||
if items[i].MediaType != items[j].MediaType {
|
||||
return items[i].MediaType < items[j].MediaType
|
||||
}
|
||||
return items[i].SizeBytes < items[j].SizeBytes
|
||||
})
|
||||
if len(items) == 1 {
|
||||
return append([]byte(nil), items[0].Content...)
|
||||
|
||||
@@ -190,6 +190,47 @@ func TestReferencePromptInputRendering(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePromptInputOrdersRenderedMetadataDeterministically(t *testing.T) {
|
||||
base := contracts.ReferenceItem{
|
||||
SlotName: "party",
|
||||
MediaType: "text/plain",
|
||||
Content: []byte("same content"),
|
||||
Digest: "sha256:same",
|
||||
Origin: contracts.ReferenceOrigin{Type: "file", URI: "file:///same.txt"},
|
||||
SizeBytes: 12,
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
first contracts.ReferenceItem
|
||||
last contracts.ReferenceItem
|
||||
}{
|
||||
{
|
||||
name: "origin type",
|
||||
first: referenceItemWithMetadata(base, "archive", "text/plain", 12),
|
||||
last: referenceItemWithMetadata(base, "file", "text/plain", 12),
|
||||
},
|
||||
{
|
||||
name: "media type",
|
||||
first: referenceItemWithMetadata(base, "file", "application/json", 12),
|
||||
last: referenceItemWithMetadata(base, "file", "text/plain", 12),
|
||||
},
|
||||
{
|
||||
name: "size",
|
||||
first: referenceItemWithMetadata(base, "file", "text/plain", 11),
|
||||
last: referenceItemWithMetadata(base, "file", "text/plain", 12),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
forward := ReferencePromptInput(contracts.ResolvedReferenceSlot{Items: []contracts.ReferenceItem{test.first, test.last}})
|
||||
reversed := ReferencePromptInput(contracts.ResolvedReferenceSlot{Items: []contracts.ReferenceItem{test.last, test.first}})
|
||||
if !reflect.DeepEqual(forward, reversed) {
|
||||
t.Fatalf("rendered bytes depend on insertion order:\nforward=%q\nreversed=%q", forward, reversed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func slotWithContent(name string, content string) contracts.ResolvedReferenceSlot {
|
||||
return contracts.ResolvedReferenceSlot{
|
||||
Slot: contracts.ReferenceSlot{Name: name},
|
||||
@@ -208,3 +249,10 @@ func referenceItem(slotName, uri, digest, content string) contracts.ReferenceIte
|
||||
Origin: contracts.ReferenceOrigin{URI: uri},
|
||||
}
|
||||
}
|
||||
|
||||
func referenceItemWithMetadata(base contracts.ReferenceItem, originType, mediaType string, size int64) contracts.ReferenceItem {
|
||||
base.Origin.Type = originType
|
||||
base.MediaType = mediaType
|
||||
base.SizeBytes = size
|
||||
return base
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
const (
|
||||
Key = "extract/dnd/combat-turns/source_relatedness"
|
||||
WarningReasonCode = "combat_turn_not_near_source"
|
||||
policy = "dnd.combat_turns.validator.source_relatedness.v1"
|
||||
policy = "dnd.combat_turns.validator.source_relatedness.v2"
|
||||
)
|
||||
|
||||
type Options struct{}
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestValidatorIgnoresReferenceMaterialAndRegistersPolicy(t *testing.T) {
|
||||
if err != nil || !result.Approved || len(result.Warnings) != 1 {
|
||||
t.Fatalf("reference-only relatedness = %#v, %v; want warning from transcript-only evidence", result, err)
|
||||
}
|
||||
if got := New(Options{}).CheckpointFingerprints(); len(got) != 1 || got[0].Name != "policy" || got[0].Value != policy {
|
||||
if got := New(Options{}).CheckpointFingerprints(); len(got) != 1 || got[0].Name != "policy" || got[0].Value != "dnd.combat_turns.validator.source_relatedness.v2" {
|
||||
t.Fatalf("CheckpointFingerprints() = %#v, want relatedness policy", got)
|
||||
}
|
||||
if Spec().Key != Key || Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
const (
|
||||
Key = "extract/dnd/npcs/source_relatedness"
|
||||
WarningReasonCode = "npc_not_near_source"
|
||||
policy = "dnd.npcs.validator.source_relatedness.v1"
|
||||
policy = "dnd.npcs.validator.source_relatedness.v2"
|
||||
)
|
||||
|
||||
type Options struct{}
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestValidatorUsesOnlyTranscriptEvidenceAndRegistersPolicy(t *testing.T) {
|
||||
if err != nil || len(result.Warnings) != 1 {
|
||||
t.Fatalf("reference-only relatedness = %#v, %v; want warning", result, err)
|
||||
}
|
||||
if got := New(Options{}).CheckpointFingerprints(); len(got) != 1 || got[0].Name != "policy" || got[0].Value != "dnd.npcs.validator.source_relatedness.v1" {
|
||||
if got := New(Options{}).CheckpointFingerprints(); len(got) != 1 || got[0].Name != "policy" || got[0].Value != "dnd.npcs.validator.source_relatedness.v2" {
|
||||
t.Fatalf("CheckpointFingerprints() = %#v, want local policy", got)
|
||||
}
|
||||
if Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
||||
|
||||
Reference in New Issue
Block a user