Add complete D&D example transcript

This commit is contained in:
2026-07-26 13:07:59 +00:00
parent 5a968b64eb
commit 46761706a2
6 changed files with 1147 additions and 8 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
{
"metadata": {
"id": "session-ravenfall",
"title": "The Ravenfall Watchtower"
},
"segments": [
{
"id": 1,
"start": 0,
"end": 14,
"speaker": "DM",
"text": "Recap: last session, the party learned that Elder Rowan vanished near the Ravenfall watchtower."
},
{
"id": 2,
"start": 14,
"end": 25,
"speaker": "Player",
"text": "Out of character, we agree to investigate the watchtower before the next game."
},
{
"id": 3,
"start": 25,
"end": 39,
"speaker": "DM",
"text": "Aria and Borin arrive at the ruined Ravenfall watchtower as dusk settles over the road."
},
{
"id": 4,
"start": 39,
"end": 55,
"speaker": "Mira Thorn",
"text": "Mira Thorn steps from the doorway and says, \"Elder Rowan warned me that Kesh would return for the relic.\""
},
{
"id": 5,
"start": 55,
"end": 70,
"speaker": "DM",
"text": "Mira leads the party to a hidden cache. The party discovers a moonblade and acquires 20 silver pieces."
},
{
"id": 6,
"start": 70,
"end": 83,
"speaker": "Aria",
"text": "Aria hands her healing potion to Borin so he can carry it into the tower."
},
{
"id": 7,
"start": 83,
"end": 96,
"speaker": "DM",
"text": "Kesh, the goblin captain, orders the raiders to attack. Roll initiative."
},
{
"id": 8,
"start": 96,
"end": 110,
"speaker": "DM",
"text": "On Kesh's turn, he strikes Borin with his scimitar. Borin drinks the healing potion on his turn."
},
{
"id": 9,
"start": 110,
"end": 124,
"speaker": "Aria",
"text": "Aria casts Cure Wounds on Borin, then invokes Aegis of Emberfall as Kesh closes in."
},
{
"id": 10,
"start": 124,
"end": 137,
"speaker": "DM",
"text": "Kesh casts Shield as a reaction against Borin's counterattack, but the party drives the raiders away."
},
{
"id": 11,
"start": 137,
"end": 150,
"speaker": "DM",
"text": "After the battle, Aria pays 5 silver pieces to repair the watchtower gate."
}
]
}

View File

@@ -1,2 +1,8 @@
Cure Wounds: healing spell cast by touch.
Shield: defensive reaction spell.
Ravenfall watchtower: a ruined watchtower near the party's current route.
Mira Thorn: the watchtower's keeper.
Elder Rowan: a missing local scholar.
Kesh: a goblin captain leading raiders.
Moonblade: a blade found in the watchtower's hidden cache.
Cure Wounds: a healing spell.
Shield: a defensive reaction spell.
Aegis of Emberfall: a campaign spell recorded in the supplied catalog overlay.

View File

@@ -1,3 +1,2 @@
Aria: party cleric and recurring healer.
Borin: fighter ally.
Bandit mage: hostile spellcaster.

View File

@@ -1,6 +1,7 @@
package cli
import (
"context"
"encoding/json"
"os"
"path/filepath"
@@ -11,9 +12,11 @@ import (
"gitea.maximumdirect.net/eric/notarius/internal/core/artifacts"
"gitea.maximumdirect.net/eric/notarius/internal/core/config"
"gitea.maximumdirect.net/eric/notarius/internal/core/debugbundle"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells"
"gitea.maximumdirect.net/eric/notarius/internal/modules/seriatim/input/transcript"
)
func TestMaintainedExamplesLoadResolveAndList(t *testing.T) {
@@ -21,6 +24,20 @@ func TestMaintainedExamplesLoadResolveAndList(t *testing.T) {
for _, example := range maintainedExampleFiles(t) {
t.Run(example.name, func(t *testing.T) {
cfg := loadMaintainedExample(t, example.path)
raw, err := os.ReadFile(example.transcriptPath)
if err != nil {
t.Fatalf("read maintained transcript %q: %v", example.transcriptPath, err)
}
document, err := transcript.New().Parse(context.Background(), contracts.ParseRequest{
Path: example.transcriptPath,
Raw: raw,
})
if err != nil {
t.Fatalf("parse maintained transcript %q: %v", example.transcriptPath, err)
}
if len(document.Units) == 0 {
t.Fatalf("maintained transcript %q has no parsed units", example.transcriptPath)
}
for _, pipelineID := range example.pipelineIDs {
effective, err := cfg.Resolve(resolveInputForMaintainedExample(components, pipelineID))
if err != nil {

View File

@@ -567,16 +567,17 @@ func TestProductionSceneRunRecordsAnnotationFreeChunkPlanAndProvenance(t *testin
}
type maintainedExample struct {
name string
path string
pipelineIDs []string
name string
path string
transcriptPath string
pipelineIDs []string
}
func maintainedExampleFiles(t *testing.T) []maintainedExample {
t.Helper()
return []maintainedExample{
{name: "minimal", path: repositoryPath("examples", "dnd-minimal.config.yml"), pipelineIDs: []string{"dnd-session"}},
{name: "complete", path: repositoryPath("examples", "dnd-complete.config.yml"), pipelineIDs: []string{"dnd-session"}},
{name: "minimal", path: repositoryPath("examples", "dnd-minimal.config.yml"), transcriptPath: repositoryPath("examples", "seriatim-minimal-transcript.json"), pipelineIDs: []string{"dnd-session"}},
{name: "complete", path: repositoryPath("examples", "dnd-complete.config.yml"), transcriptPath: repositoryPath("examples", "dnd-complete-transcript.json"), pipelineIDs: []string{"dnd-session"}},
}
}