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
|
campaign-reference messages are ephemeral in the extraction prompts; the scene
|
||||||
prompt marks transcript and campaign references ephemeral, and the NPC registry
|
prompt marks transcript and campaign references ephemeral, and the NPC registry
|
||||||
is ephemeral where spell and combat prompts use it. Unused shared assets are
|
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,
|
Schema helpers load embedded JSON Schema with identity and digest metadata,
|
||||||
return defensive copies, and expose a diagnostics map that omits schema bytes.
|
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.
|
Unmatched actors and targets remain permitted.
|
||||||
|
|
||||||
For every factual detail in a turn, cite all supporting transcript units in the
|
Place all supporting transcript ranges for a turn in its turn-level source_refs
|
||||||
turn-level source_refs collection.
|
collection.
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ Relationships must be stated or directly demonstrated by cited transcript
|
|||||||
units, not inferred from game lore.
|
units, not inferred from game lore.
|
||||||
|
|
||||||
Return aliases and relationships as arrays, including empty arrays when there
|
Return aliases and relationships as arrays, including empty arrays when there
|
||||||
are none. Return only NPC records supported by the transcript and preserve
|
are none. Preserve observed display spelling.
|
||||||
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,
|
Exclude human players, transcript speakers, and the GM as out-of-world people,
|
||||||
player characters identified by the player or party references, incidental or
|
player characters identified by the player or party references, incidental or
|
||||||
hypothetical name drops, corrected transcription mistakes, characters mentioned
|
hypothetical name drops, corrected transcription mistakes, indistinguishable
|
||||||
only by reference material, indistinguishable crowds or groups, and temporary
|
crowds or groups, and temporary summoned creatures or spell effects without a
|
||||||
summoned creatures or spell effects without a persistent individual identity.
|
persistent individual identity.
|
||||||
|
|
||||||
Keep each description concise and limited to facts established by the
|
Keep each description concise and limited to facts established by the
|
||||||
transcript. Include only explicitly supported aliases and relationships.
|
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
|
spell name absent from that catalog, even when it is suggested by general D&D
|
||||||
knowledge or reference material.
|
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.
|
Effects and narrative descriptions are session records, not rules summaries.
|
||||||
Report only mechanics, explanations, and outcomes established by the cited
|
Report only mechanics, explanations, and outcomes established by the cited
|
||||||
transcript units. Preserve the table's observed resolution without silently
|
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 Dungeons & Dragons spell-cast artifacts from the provided transcript.
|
||||||
|
|
||||||
Extract only spell casts that are supported by the transcript. Do not infer
|
Do not infer a spell cast from general D&D knowledge or from table chatter that
|
||||||
spells from general D&D knowledge or from table chatter that does not identify a
|
does not identify a spell being cast.
|
||||||
spell being cast.
|
|
||||||
|
|
||||||
Describe the session as it was played and adjudicated. The transcript is
|
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
|
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
|
transcription errors, repeated lines, incomplete sentences, and misheard proper
|
||||||
nouns. Reference material, when present, is supporting context only and must not
|
nouns. Reference material, when present, is supporting context only and must not
|
||||||
be treated as a source of extracted events by itself.
|
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 {
|
if items[i].Digest != items[j].Digest {
|
||||||
return 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 {
|
if len(items) == 1 {
|
||||||
return append([]byte(nil), items[0].Content...)
|
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 {
|
func slotWithContent(name string, content string) contracts.ResolvedReferenceSlot {
|
||||||
return contracts.ResolvedReferenceSlot{
|
return contracts.ResolvedReferenceSlot{
|
||||||
Slot: contracts.ReferenceSlot{Name: name},
|
Slot: contracts.ReferenceSlot{Name: name},
|
||||||
@@ -208,3 +249,10 @@ func referenceItem(slotName, uri, digest, content string) contracts.ReferenceIte
|
|||||||
Origin: contracts.ReferenceOrigin{URI: uri},
|
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 (
|
const (
|
||||||
Key = "extract/dnd/combat-turns/source_relatedness"
|
Key = "extract/dnd/combat-turns/source_relatedness"
|
||||||
WarningReasonCode = "combat_turn_not_near_source"
|
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{}
|
type Options struct{}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func TestValidatorIgnoresReferenceMaterialAndRegistersPolicy(t *testing.T) {
|
|||||||
if err != nil || !result.Approved || len(result.Warnings) != 1 {
|
if err != nil || !result.Approved || len(result.Warnings) != 1 {
|
||||||
t.Fatalf("reference-only relatedness = %#v, %v; want warning from transcript-only evidence", result, err)
|
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)
|
t.Fatalf("CheckpointFingerprints() = %#v, want relatedness policy", got)
|
||||||
}
|
}
|
||||||
if Spec().Key != Key || Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
if Spec().Key != Key || Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
Key = "extract/dnd/npcs/source_relatedness"
|
Key = "extract/dnd/npcs/source_relatedness"
|
||||||
WarningReasonCode = "npc_not_near_source"
|
WarningReasonCode = "npc_not_near_source"
|
||||||
policy = "dnd.npcs.validator.source_relatedness.v1"
|
policy = "dnd.npcs.validator.source_relatedness.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct{}
|
type Options struct{}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func TestValidatorUsesOnlyTranscriptEvidenceAndRegistersPolicy(t *testing.T) {
|
|||||||
if err != nil || len(result.Warnings) != 1 {
|
if err != nil || len(result.Warnings) != 1 {
|
||||||
t.Fatalf("reference-only relatedness = %#v, %v; want warning", result, err)
|
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)
|
t.Fatalf("CheckpointFingerprints() = %#v, want local policy", got)
|
||||||
}
|
}
|
||||||
if Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
if Spec().ExecutionClass != contracts.ExecutionClassDeterministic {
|
||||||
|
|||||||
Reference in New Issue
Block a user