Files
notarius/docs/roadmap/implementation.md

11 KiB

D&D Shared Helper Refactor Implementation Plan

Summary

Implement the target state defined in D&D Shared Module Helper Roadmap. This is an internal refactor: do not change module keys, prompt IDs, prompt versions, Scriptorium-visible message order, response schema IDs or names, CLI/config semantics, manifest shape, diagnostics redaction policy, or D&D scene/spell interpretation behavior.

The intended package split is:

  • internal/modules/sharedassets: generic prompt filesystem composition and non-domain-specific asset plumbing only;
  • internal/modules/sharedassets/dnd: reusable D&D prompt assets, prompt input helpers, reference slot helpers, reference rendering, and D&D shared prompt hash parts;
  • concrete D&D modules: stage contracts, module registration, module-local prompt definitions, response schemas, validators, and response interpretation.

Stage 1: Add Generic Defensive-Copy Helper

  • Add CloneReferenceSlots to internal/framework/contracts.
  • The helper must:
    • return nil for an empty input slice;
    • allocate a new slot slice;
    • deep-copy each AcceptedMediaTypes slice;
    • preserve all other ReferenceSlot fields exactly.
  • Add focused tests in internal/framework/contracts proving nil/empty behavior, deep-copy behavior, and full field preservation.
  • Do not import any concrete module package from contracts.

Stage 2: Make Shared Prompt FS Composition Domain-Neutral

  • Refactor internal/modules/sharedassets.ModulePromptFS so the parent package no longer knows about D&D prompt filenames or embedded D&D assets.
  • Add a generic shared prompt file descriptor:
type SharedPromptFile struct {
    Name string
    FS   fs.FS
    Path string
}
  • Change ModulePromptFS to this shape:
func ModulePromptFS(
    moduleDir string,
    moduleFS fs.FS,
    files []ModulePromptFile,
    sharedFiles ...SharedPromptFile,
) (fs.FS, error)
  • Keep existing ModulePromptFile semantics:
    • Name is mounted directly under assets/prompts/<moduleDir>/;
    • Path is read from the module embedded filesystem.
  • Mount each SharedPromptFile under assets/prompts/<moduleDir>/sharedassets/<Name>.
  • Validate all names and paths with contextual errors:
    • moduleDir must be a valid non-root fs path;
    • module prompt Name must be a single filename with no path separators;
    • shared prompt Name must be a single filename with no path separators;
    • moduleFS and each shared file FS must be non-nil;
    • missing module or shared files must return errors naming the source path.
  • Preserve the in-memory FS behavior currently covered by tests, including readable intermediate directories and root . support.
  • Update internal/modules/sharedassets tests so they use test-owned shared prompt files from fstest.MapFS, not production D&D prompt assets.
  • Remove D&D-specific helpers, constants, and embedded asset dependencies from the parent sharedassets package.

Stage 3: Create sharedassets/dnd

  • Create internal/modules/sharedassets/dnd with package name dnd.
  • Move the current D&D shared prompt files into:
internal/modules/sharedassets/dnd/assets/prompts/common-dnd-system.md
internal/modules/sharedassets/dnd/assets/prompts/common-dnd-transcript.md
internal/modules/sharedassets/dnd/assets/prompts/common-dnd-references.md
  • Add assets.go in the D&D package with go:embed assets/prompts/*.md.
  • Expose D&D shared asset helpers:
func SharedPromptFiles() []sharedassets.SharedPromptFile
func CommonHashParts() []llm.AssetHashPart
func ReferenceHashParts() []llm.AssetHashPart
func ModulePromptFS(moduleDir string, moduleFS fs.FS, files []sharedassets.ModulePromptFile) (fs.FS, error)
  • SharedPromptFiles must return a new slice each call.
  • CommonHashParts must include common-dnd-system.md and common-dnd-transcript.md.
  • ReferenceHashParts must include common-dnd-references.md.
  • ModulePromptFS must call sharedassets.ModulePromptFS with SharedPromptFiles()....
  • Do not expose a D&D package registration function unless a concrete caller needs top-level D&D shared prompt files outside module-local prompt composition. The D&D prompt definitions should continue to load shared files through ./sharedassets/... inside each module prompt directory.

Stage 4: Add D&D Reference And Prompt Input Helpers

  • In internal/modules/sharedassets/dnd, add D&D reference helpers:
type ReferenceSlotDescriptions struct {
    Glossary string
    Party    string
    Players  string
    Roster   string
}

func ReferenceMediaTypes() []string
func ReferenceSlots(descriptions ReferenceSlotDescriptions) []contracts.ReferenceSlot
  • ReferenceMediaTypes must return a defensive copy of:

    • application/json
    • application/x-yaml
    • application/yaml
    • text/markdown
    • text/plain
  • ReferenceSlots must return slots in the same order currently exposed by both D&D modules: glossary, party, players, roster.

  • Slot names and alias semantics must remain unchanged.

  • Slot descriptions should be supplied by each concrete module through ReferenceSlotDescriptions so current module metadata text can be preserved.

  • ReferenceSlots must use contracts.CloneReferenceSlots or equivalent defensive-copy behavior before returning.

  • Add prompt input helpers:

func PromptInputs(sourceInput contracts.LLMInputMaterial, references contracts.ReferenceSet) contracts.LLMInputSet
func TranscriptPromptMaterial(material contracts.LLMInputMaterial) contracts.LLMInputMaterial
func ReferencePromptMaterial(name string, slot contracts.ResolvedReferenceSlot) contracts.LLMInputMaterial
func ReferencePromptInput(slot contracts.ResolvedReferenceSlot) []byte
  • PromptInputs must return inputs named transcript, players, party, and glossary.

  • If party has no items and roster has items, PromptInputs must use the roster slot content for the party input.

  • PromptInputs must not include a roster prompt input.

  • TranscriptPromptMaterial must clone the source input and set Name to transcript.

  • ReferencePromptMaterial must use media type text/plain; for a single reference item it must propagate that item's digest and origin URI.

  • ReferencePromptInput behavior must match current D&D module behavior:

    • empty slot renders as a single space;
    • one item renders as raw item content;
    • multiple items are copied, sorted deterministically by origin URI, digest, then content, and rendered with the same heading/metadata format currently used by dnd/scenes and dnd/spells.
  • Add focused tests under internal/modules/sharedassets/dnd for all helper behavior. These tests may assert rendering of helper-owned fixture inputs, but must not assert exact production embedded prompt prose.

Stage 5: Update D&D Modules To Use Shared Helpers

  • Update internal/modules/chunk/dnd/scenes:
    • import internal/modules/sharedassets/dnd;
    • replace local accepted media type and slot cloning logic with dnd.ReferenceSlots;
    • preserve existing scene-specific slot descriptions;
    • replace local prompt input/reference rendering helpers with dnd.PromptInputs;
    • replace sharedassets.ModulePromptFS with dnd.ModulePromptFS;
    • replace sharedassets.CommonHashParts and ReferenceHashParts with the D&D package equivalents.
  • Update internal/modules/extract/dnd/spells the same way, preserving current extractor-specific slot descriptions.
  • Delete local duplicated helper functions that become unused:
    • acceptedReferenceMediaTypes;
    • cloneReferenceSlots;
    • transcriptPromptInput;
    • referencePromptMaterial;
    • referencePromptInput.
  • Keep module-local schema loading, prompt IDs, prompt versions, validators, request validation, response conversion, and manifest metadata ownership in each concrete module.

Stage 6: Update Asset Registration And Tests

  • Update production prompt asset registration in internal/cli as needed:
    • remove any call that registers D&D shared prompt files from the generic sharedassets package;
    • ensure dnd/scenes and dnd/spells prompt registration still makes their module-local ./sharedassets/common-dnd-*.md files available.
  • Update production prompt asset tests so they assert module-local shared asset paths such as:
    • dnd.scenes/sharedassets/common-dnd-system.md
    • dnd.spells/sharedassets/common-dnd-system.md
  • Remove expectations for top-level common-dnd-*.md prompt files unless a genuine top-level registration remains necessary.
  • Move reference rendering tests from concrete D&D modules into internal/modules/sharedassets/dnd.
  • Keep concrete D&D module tests focused on:
    • module specs and registry behavior;
    • LLM request prompt ID/version/profile/session fields;
    • transcript and reference input propagation;
    • Scriptorium prompt preparation;
    • diagnostics redaction;
    • manifest metadata;
    • scene/spell response interpretation.
  • Follow the prompt asset testing policy in docs/policy/development.md: do not assert exact production embedded prompt prose.

Stage 7: Update Documentation

  • Update canonical internal docs for implemented behavior:
    • docs/internal/modules.md: mention that common D&D prompt/reference helper behavior lives under internal/modules/sharedassets/dnd;
    • docs/internal/overview.md: mention the package only if the overview lists shared module-support packages.
  • Do not add user-facing documentation unless visible behavior changes.
  • Update docs/roadmap/dnd.md after implementation so it no longer presents this refactor as future work.
  • Replace this file with a concise completed-note document once the work is implemented.

Validation

Run focused tests after each meaningful stage:

go test ./internal/framework/contracts
go test ./internal/modules/sharedassets
go test ./internal/modules/sharedassets/dnd
go test ./internal/modules/chunk/dnd/scenes
go test ./internal/modules/extract/dnd/spells
go test ./internal/cli

Run full validation before completion:

go test ./...
go vet ./...
go build ./cmd/notarius

Run inspection searches:

rg -n "common-dnd|CommonHashParts|ReferenceHashParts|referencePromptInput|cloneReferenceSlots|acceptedReferenceMediaTypes" internal/modules internal/framework
rg -n "Divide the provided transcript|Extract Dungeons & Dragons spell-cast artifacts|A transcript of a Dungeons & Dragons gameplay session|Roster reference|Glossary reference" internal/**/*_test.go

Expected inspection outcomes:

  • D&D shared prompt files and hash helpers are owned by internal/modules/sharedassets/dnd;
  • parent sharedassets has no hard-coded common-dnd-* knowledge;
  • concrete D&D modules no longer carry duplicated reference rendering logic;
  • tests do not assert exact production embedded prompt prose.

Non-Goals

  • Do not change D&D prompt wording except for file moves required by this refactor.
  • Do not change prompt IDs, prompt versions, profile defaults, Scriptorium message order, schema files, schema metadata, module keys, capabilities, manifest shapes, or diagnostics policy.
  • Do not move scene boundary logic, spell artifact logic, validators, or schema loading into the shared D&D package.
  • Do not make framework, core, or CLI packages depend on internal/modules/sharedassets/dnd.