Plan D&D location tracking
This commit is contained in:
649
docs/roadmap/implementation.md
Normal file
649
docs/roadmap/implementation.md
Normal file
@@ -0,0 +1,649 @@
|
||||
# D&D Location Tracking Implementation Plan
|
||||
|
||||
## Objective
|
||||
|
||||
Implement the target state in [D&D Location Tracking](location.md): an
|
||||
evidence-grounded `dnd/locations` registry lane and a dependent
|
||||
`dnd/location-occurrences` lane, including conservative location identity,
|
||||
shared D&D entity-reconciliation infrastructure, production validation,
|
||||
generated-reference wiring, maintained examples, and current documentation.
|
||||
|
||||
This is an ordered implementation plan for a `gpt-5.6-terra` coding agent.
|
||||
Implement one stage per prompt, in order. Finish each stage's tests and leave
|
||||
the repository coherent before proceeding. Do not implement later-stage
|
||||
production registrations early merely to make an incomplete feature selectable.
|
||||
|
||||
All stages must follow:
|
||||
|
||||
- [Architecture Policy](../policy/architecture.md)
|
||||
- [Testing Policy](../policy/testing.md)
|
||||
- [Documentation Policy](../policy/documentation.md)
|
||||
- the D&D conventions in [D&D Module Internals](../internal/dnd.md)
|
||||
- the durable policy decisions in [the feature roadmap](location.md)
|
||||
|
||||
Use behavior-level tests. Do not add tests that merely freeze source layout,
|
||||
exact prompt wording, message counts, shared-prefix length, or other incidental
|
||||
implementation details. Keep tests deterministic, offline, and owned by the
|
||||
component whose behavior they exercise.
|
||||
|
||||
## Stage 1: Define Location Domain Types And Identity
|
||||
|
||||
### Goal
|
||||
|
||||
Establish the in-process contracts and deterministic identity policy on which
|
||||
both lanes depend.
|
||||
|
||||
### Work
|
||||
|
||||
- Extend `internal/modules/dnd/types.go` with:
|
||||
- `LocationListKind` = `dnd/location-list`;
|
||||
- `LocationOccurrenceListKind` = `dnd/location-occurrence-list`;
|
||||
- `LocationList`, `Location`, `LocationOccurrenceList`,
|
||||
`LocationOccurrence`, and `LocationOccurrenceKind`;
|
||||
- exact JSON members and the four occurrence constants specified in
|
||||
`location.md`.
|
||||
- Add `internal/modules/dnd/locations/identity`.
|
||||
- Implement display normalization and comparison normalization consistently
|
||||
with the existing NPC identity policy. Share a lower-level comparison helper
|
||||
only if doing so preserves NPC behavior exactly; otherwise keep the small
|
||||
policy-specific function explicit.
|
||||
- Implement the versioned compact-JSON-array ID derivation contract from
|
||||
`location.md`, including ID syntax checks and immutable list validation.
|
||||
- Identity validation must require correctly derived, unique IDs while allowing
|
||||
two records to have the same comparison name when their evidence anchors
|
||||
differ.
|
||||
- Add focused tests for Unicode normalization, whitespace, apostrophes,
|
||||
deterministic encoding, evidence ordering, same-name/different-anchor IDs,
|
||||
malformed IDs, missing evidence, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- The types compile without production registration.
|
||||
- ID derivation exactly follows the documented five-element compact JSON input.
|
||||
- Same normalized name plus different earliest evidence yields different IDs.
|
||||
- Validation does not reject same-name records solely because their names
|
||||
match, and it reports duplicate or mismatched IDs deterministically.
|
||||
- `go test ./internal/modules/dnd/locations/... ./internal/modules/dnd/...` passes
|
||||
for the packages available at this stage.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Small enough for one implementation prompt.
|
||||
|
||||
## Stage 2: Add Durable Codecs And Schemas
|
||||
|
||||
### Goal
|
||||
|
||||
Create strict durable JSON ownership for both artifact kinds without exposing
|
||||
either lane as a selectable pipeline yet.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/codec/locations` and
|
||||
`internal/modules/dnd/codec/locationoccurrences`, following the existing D&D
|
||||
candidate/approved codec pattern.
|
||||
- Add embedded Draft 2020-12 schemas with the IDs, names, root members, required
|
||||
fields, enums, source-reference shape, and `additionalProperties: false`
|
||||
contracts in `location.md`.
|
||||
- Keep both schemas at `v1`.
|
||||
- Support strict candidate decoding before semantic approval and strict durable
|
||||
encoding/decoding after approval.
|
||||
- Add representative valid fixtures and tests for schema metadata, defensive
|
||||
schema bytes, empty arrays, unknown fields, missing fields, invalid types,
|
||||
invalid enum values, malformed source references, invalid ID syntax, and
|
||||
round trips.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Each codec advertises the correct artifact kind and metadata count.
|
||||
- Candidate decoding preserves semantic mistakes for validators while
|
||||
rejecting structurally invalid JSON.
|
||||
- Approved encoding and decoding enforce the durable shape.
|
||||
- `go test ./internal/modules/dnd/codec/locations/... ./internal/modules/dnd/codec/locationoccurrences/...`
|
||||
passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Small enough for one implementation prompt.
|
||||
|
||||
## Stage 3: Extract Shared Entity-Reconciliation Infrastructure
|
||||
|
||||
### Goal
|
||||
|
||||
Create the D&D-shared, domain-safe proposal machinery needed by both NPC and
|
||||
location normalization, without changing NPC production behavior yet.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/shared/entityreconcile`.
|
||||
- Move or generalize the reusable behavior currently owned by
|
||||
`internal/modules/dnd/normalize/npcs/context_material.go` and `proposal.go`:
|
||||
- assign deterministic opaque candidate keys such as `candidate-000001` in
|
||||
input order;
|
||||
- clone candidate names and source references;
|
||||
- build bounded transcript windows in source-document order;
|
||||
- omit candidates whose references cannot safely produce context;
|
||||
- coalesce overlapping or adjacent windows without mutating the source;
|
||||
- define the private `duplicate_groups` proposal with `members` and
|
||||
`canonical` candidate keys;
|
||||
- reject blank, unknown, repeated, ineligible, overlapping, too-small, or
|
||||
canonical-not-a-member groups; and
|
||||
- return defensive, immutable assessment data identifying only safe groups.
|
||||
- Keep LLM calls, retry decisions, artifact mutation, canonical-name policy,
|
||||
durable ID derivation, and warning wording out of this package.
|
||||
- Add a shared prompt instruction asset that states the key-copying and
|
||||
proposal-safety contract without NPC- or location-specific identity rules.
|
||||
- Add a shared private structured-response schema and loader/registration
|
||||
support with a stable `v1` key, ID, name, and fingerprint. Registering the
|
||||
schema more than once must not be required.
|
||||
- Add table-driven tests for context bounds, ordering, invalid references,
|
||||
coalescing, every unsafe proposal category, non-overlapping safe groups,
|
||||
deterministic keys, defensive copies, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- The package has no dependency on `dnd.NPC`, `dnd.Location`, either
|
||||
normalizer, or a concrete LLM client.
|
||||
- Proposal values can identify duplicate candidates even when display names
|
||||
are equal.
|
||||
- The shared response contract cannot directly supply replacement records or
|
||||
evidence.
|
||||
- Existing NPC packages still compile before their migration.
|
||||
- `go test ./internal/modules/dnd/shared/...` passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium, but coherent and suitable for one implementation prompt. Do not combine
|
||||
it with the NPC migration.
|
||||
|
||||
## Stage 4: Migrate NPC Normalization To The Shared Helper
|
||||
|
||||
### Goal
|
||||
|
||||
Make the existing NPC normalizer the first production consumer of the shared
|
||||
entity-reconciliation contract while retaining its durable behavior.
|
||||
|
||||
### Work
|
||||
|
||||
- Refactor `internal/modules/dnd/normalize/npcs` to use opaque candidate keys,
|
||||
shared context construction, shared proposal assessment, the shared response
|
||||
schema, and the shared generic reconciliation instruction asset.
|
||||
- Retain NPC-owned responsibilities:
|
||||
- comparison-name preparation and deterministic duplicate handling;
|
||||
- the NPC-specific task and canonical display-name rules;
|
||||
- LLM invocation, bounded retry, fallback, warnings, and diagnostics;
|
||||
- application of safe groups, evidence union, NPC ID derivation, and output
|
||||
ordering.
|
||||
- Remove superseded NPC-private context/proposal code and private schema assets
|
||||
once no longer referenced.
|
||||
- Update NPC prompt metadata and checkpoint fingerprints for the intentional
|
||||
prompt/private-schema contract change.
|
||||
- Preserve public module keys, durable NPC schema, identity policy, validator
|
||||
chains, warning bounds, and fallback semantics.
|
||||
- Test equal display names as distinct keyed candidates, alias consolidation,
|
||||
rejected unsafe groups, retry exhaustion, private input ownership, redacted
|
||||
errors, deterministic fallback, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- No durable NPC artifact field or module key changes.
|
||||
- NPC normalization cannot confuse two candidates merely because their display
|
||||
names match.
|
||||
- Unsafe proposals leave a valid deterministic result and follow existing
|
||||
retry/fallback policy.
|
||||
- Obsolete NPC-only reconciliation helpers and schema are removed.
|
||||
- `go test ./internal/modules/dnd/normalize/npcs/... ./internal/modules/dnd/shared/...`
|
||||
passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium-to-large but bounded to one existing module. Suitable for one
|
||||
implementation prompt; do not add location normalization in this stage.
|
||||
|
||||
## Stage 5: Implement Location Extraction
|
||||
|
||||
### Goal
|
||||
|
||||
Add the LLM-backed extractor that produces evidence-grounded location
|
||||
candidates.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/extract/locations` following current D&D extractor
|
||||
conventions: strict empty options, typed builder and registration function,
|
||||
`llm_backed` execution metadata, immutable inputs, redacted errors, prompt
|
||||
and response-schema fingerprints, and bounded diagnostics.
|
||||
- Add a private response schema containing only `name` and source ranges; the
|
||||
model must not produce durable IDs or prose.
|
||||
- Compose the prompt from existing shared D&D system, identity, campaign
|
||||
reference, transcript, and evidence assets plus module-owned task and
|
||||
instructions. Preserve the documented extraction-message ordering and cache
|
||||
controls.
|
||||
- Define physical-place inclusion and conservative omission exactly as in
|
||||
`location.md`, including generic labels, aliases, and nested places.
|
||||
- Map source ranges to the current source ID, canonicalize exact duplicate
|
||||
ranges, derive candidate location IDs in code, preserve semantically invalid
|
||||
candidates for validators where safe, and return deterministic ordering.
|
||||
- Add prompt-asset tests that verify shared asset reuse and rendered inputs by
|
||||
behavior, without asserting exact shared-prefix length or prompt wording.
|
||||
- Add extractor tests for empty output, mapping, evidence ownership, generic
|
||||
same-name locations with different anchors, invalid candidate preservation,
|
||||
client failures, registration metadata, fingerprints, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- The extractor cannot manufacture source identities or accept campaign
|
||||
references as evidence.
|
||||
- The private model response does not contain a durable ID.
|
||||
- Same-name candidates with different evidence survive extraction as distinct
|
||||
candidates.
|
||||
- The package is testable through its local registration but is not yet added
|
||||
to the production D&D registrar.
|
||||
- `go test ./internal/modules/dnd/extract/locations/...` passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Stage 6: Implement Semantic Location Normalization
|
||||
|
||||
### Goal
|
||||
|
||||
Add conservative alias and repeated-place reconciliation without collapsing
|
||||
same-named or nested locations by default.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/normalize/locations` as an `llm_backed` normalizer
|
||||
using the shared entity-reconciliation package and private response schema.
|
||||
- Deterministically clone and prepare the merged candidates first:
|
||||
- normalize display whitespace;
|
||||
- canonicalize and deduplicate source references;
|
||||
- remove only exact duplicates with the same comparison name and exact
|
||||
canonical evidence;
|
||||
- assign opaque reconciliation keys; and
|
||||
- retain same-name records with different evidence.
|
||||
- Use bounded transcript windows and a module-owned location task that permits
|
||||
grouping only when evidence clearly identifies one physical place. Explicitly
|
||||
prohibit grouping solely by equal names, proximity, nesting, or generic
|
||||
labels.
|
||||
- Validate proposals through the shared package. Apply only safe groups in
|
||||
deterministic code, choose the canonical name from the selected existing
|
||||
candidate, union evidence, and derive the final evidence-anchored ID.
|
||||
- Retain the deterministic candidate set on unusable proposals and follow the
|
||||
existing NPC retry/fallback and bounded-warning conventions.
|
||||
- Publish prompt, response-schema, identity-policy, normalization-policy, and
|
||||
semantic-context fingerprints.
|
||||
- Test aliases, repeated appearances, same-name distinct places, parent/child
|
||||
locations, invalid and overlapping proposals, proposal retries, fallback,
|
||||
ordering, ID recomputation, warning bounds, idempotent deterministic
|
||||
application, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- The model proposes groups but cannot directly replace durable locations.
|
||||
- A failed or ambiguous proposal cannot lose a valid candidate.
|
||||
- Same-name locations remain distinct unless an approved evidence-backed group
|
||||
joins them.
|
||||
- Final IDs are derived only after group evidence is unioned.
|
||||
- `go test ./internal/modules/dnd/normalize/locations/...` passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium-to-large but scoped to one normalizer and suitable for one implementation
|
||||
prompt.
|
||||
|
||||
## Stage 7: Add The Immutable Location Registry
|
||||
|
||||
### Goal
|
||||
|
||||
Provide safe generated-reference resolution and an unambiguous prompt
|
||||
projection for downstream occurrence extraction.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/locations/registry`, modeled on the immutable NPC
|
||||
registry and its operation-time resolver.
|
||||
- Define `ReferenceSlot = "locations"`, a 1,048,576-byte limit, and exactly one
|
||||
accepted `application/json` location-list item when bound.
|
||||
- Validate durable decoding and location identity before constructing a
|
||||
registry.
|
||||
- Store canonical durable bytes and semantic digests without retaining mutable
|
||||
caller-owned content. Return defensive copies from all accessors.
|
||||
- Produce a compact, source-free prompt projection containing ordered
|
||||
`{id, name}` pairs. Do not include source references or generated-reference
|
||||
provenance.
|
||||
- Support exact lookup by ID and verify the matching canonical name; do not
|
||||
provide an ambiguous name-only lookup as the occurrence linkage mechanism.
|
||||
- Preserve the established seeded/operation resolver behavior and concurrency-
|
||||
safe semantic caching.
|
||||
- Test absent, empty, malformed, oversized, wrong-media-type, invalid-identity,
|
||||
and valid registries; projections; ID lookup; defensive copies; raw and
|
||||
semantic cache reuse; and concurrent resolution.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Distinct same-name records are both representable and addressable by ID.
|
||||
- Registry evidence cannot appear in the prompt projection.
|
||||
- Malformed static references fail during construction and malformed generated
|
||||
references fail at operation resolution through existing boundaries.
|
||||
- `go test ./internal/modules/dnd/locations/registry/...` passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Stage 8: Implement Location-Occurrence Extraction
|
||||
|
||||
### Goal
|
||||
|
||||
Add the dependent LLM-backed lane that classifies source-grounded location
|
||||
occurrences.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/extract/locationoccurrences` with module key
|
||||
`dnd/location-occurrences`, `llm_backed` execution metadata, strict empty
|
||||
options, typed construction, and a required `locations` reference slot.
|
||||
- Resolve the immutable registry at construction and for each operation using
|
||||
the established generated-reference pattern.
|
||||
- Add a private response schema requiring `location_id`, `name`, `kind`, and
|
||||
source ranges. Restrict kinds to `visited`, `planned`, `recalled`, and
|
||||
`mentioned`.
|
||||
- Reuse the shared D&D extraction prompt assets and ordering. Place the compact
|
||||
location registry after the shared transcript/evidence material and before
|
||||
module task/instructions, consistent with current generated grounding.
|
||||
- Encode the exact classification rules, precedence, multi-fact behavior, and
|
||||
conservative omission policy from `location.md`.
|
||||
- Map evidence only to the current source. Copy candidate IDs and names without
|
||||
silently repairing unknown or mismatched values so deterministic validators
|
||||
retain ownership of those diagnostics.
|
||||
- Canonically order output and exact duplicates without dropping distinct
|
||||
kinds or independent evidence.
|
||||
- Test every kind, precedence, multiple supported facts, no-location and no-
|
||||
occurrence outputs, required registry failures, same-name ID selection,
|
||||
source-free prompt projection, current-transcript evidence, prompt/profile
|
||||
metadata, client failures, non-mutation, and local registration.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Construction and operation specs declare `locations` as required and accept
|
||||
only `dnd/location-list` JSON.
|
||||
- The model sees IDs and names but no registry evidence.
|
||||
- Registry context never becomes occurrence evidence.
|
||||
- The extractor remains locally testable but is not production-selectable yet.
|
||||
- `go test ./internal/modules/dnd/extract/locationoccurrences/...` passes
|
||||
offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Stage 9: Implement Deterministic Occurrence Normalization
|
||||
|
||||
### Goal
|
||||
|
||||
Canonicalize occurrence records against the exact location registry without a
|
||||
second LLM call.
|
||||
|
||||
### Work
|
||||
|
||||
- Add `internal/modules/dnd/normalize/locationoccurrences` as a deterministic
|
||||
normalizer with the same required `locations` slot.
|
||||
- Clone all inputs. Normalize source ranges, names, ordering, and exact
|
||||
duplicates.
|
||||
- For a known `location_id`, replace display-name variation with the registry's
|
||||
exact canonical name. Do not perform a name-only guess.
|
||||
- Preserve an unknown ID or otherwise invalid record for validator diagnostics
|
||||
and emit bounded warnings where current D&D normalizer conventions require
|
||||
them.
|
||||
- Sort using the complete order defined in `location.md`.
|
||||
- Publish normalization and registry-projection fingerprints consistent with
|
||||
the other registry-backed normalizers.
|
||||
- Test all kind values, canonical name replacement, same-name distinct IDs,
|
||||
exact-duplicate removal, distinct evidence retention, stable ordering,
|
||||
unknown IDs, malformed registry resolution, warnings, idempotence, and
|
||||
non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- No LLM client or prompt assets are required.
|
||||
- Canonicalization is exclusively ID-based.
|
||||
- Invalid records are not silently redirected to a different location.
|
||||
- `go test ./internal/modules/dnd/normalize/locationoccurrences/...` passes
|
||||
offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Small enough for one implementation prompt.
|
||||
|
||||
## Stage 10: Add Location Registry Validators
|
||||
|
||||
### Goal
|
||||
|
||||
Give `dnd/location-list` the complete validator ownership expected of a
|
||||
production D&D artifact.
|
||||
|
||||
### Work
|
||||
|
||||
- Add location validator packages under
|
||||
`internal/modules/dnd/validate/locations` for:
|
||||
- extraction shape;
|
||||
- source-reference bounds/current-chunk ownership;
|
||||
- advisory source relatedness; and
|
||||
- normalized identity derivation and ID uniqueness.
|
||||
- Use shared D&D citation, unit-reference, diagnostic, and matching helpers
|
||||
where their contracts apply.
|
||||
- The identity validator must allow repeated comparison names and validate the
|
||||
evidence-anchored derivation policy instead of importing NPC uniqueness
|
||||
assumptions.
|
||||
- Keep diagnostics indexed, aggregated, bounded, stable, and free of raw prompt
|
||||
or reference content.
|
||||
- Add tests for accepted values, every owned failure, same-name distinct
|
||||
locations, malformed/unreadable citations, advisory relatedness, bounds,
|
||||
registration metadata, fingerprints, nil safety where applicable, and
|
||||
non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Validator responsibilities do not overlap merely to increase test coverage.
|
||||
- Relatedness remains advisory and uses only cited current-transcript text.
|
||||
- Validators do not repair or mutate artifacts.
|
||||
- `go test ./internal/modules/dnd/validate/locations/...` passes offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Stage 11: Add Location-Occurrence Validators
|
||||
|
||||
### Goal
|
||||
|
||||
Give `dnd/location-occurrence-list` complete structural, registry, ordering,
|
||||
evidence, and advisory validation.
|
||||
|
||||
### Work
|
||||
|
||||
- Add validator packages under
|
||||
`internal/modules/dnd/validate/locationoccurrences` for:
|
||||
- extraction shape and supported kinds;
|
||||
- required registry membership and exact `location_id`/`name` pairing;
|
||||
- normalized ordering and exact-duplicate invariants;
|
||||
- source-reference bounds/current-chunk ownership; and
|
||||
- advisory source relatedness.
|
||||
- Reuse the immutable location resolver rather than decoding caller-owned
|
||||
references independently in each validator.
|
||||
- Ensure same-name registry records remain distinguishable by ID.
|
||||
- Keep registry context out of evidence checks.
|
||||
- Add focused tests for each kind, unknown IDs, mismatched names, same-name
|
||||
locations, ordering, duplicates, malformed required references, invalid
|
||||
evidence, advisory diagnostics, registration metadata, fingerprints,
|
||||
diagnostic bounds, and non-mutation.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- An ID/name mismatch is rejected even when another registry record has the
|
||||
supplied name.
|
||||
- Missing or malformed required registry references fail at the established
|
||||
boundary.
|
||||
- Validators remain deterministic and do not alter occurrence records or the
|
||||
registry.
|
||||
- `go test ./internal/modules/dnd/validate/locationoccurrences/...` passes
|
||||
offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Stage 12: Compose The Production D&D Family
|
||||
|
||||
### Goal
|
||||
|
||||
Make both lanes selectable as one coherent production addition after all
|
||||
component contracts are present.
|
||||
|
||||
### Work
|
||||
|
||||
- Extend `internal/modules/dnd/register` to register, in dependency-safe order:
|
||||
- both codecs;
|
||||
- both extractors;
|
||||
- typed append-order mergers;
|
||||
- the LLM-backed location normalizer;
|
||||
- the deterministic occurrence normalizer;
|
||||
- all validators;
|
||||
- shared reconciliation schema assets and both new prompt manifests;
|
||||
- evidence projectors; and
|
||||
- extract and normalize default validator chains.
|
||||
- Ensure both LLM-backed modules select the maintained `dnd-extraction`
|
||||
fallback profile and inherit the existing profile policy.
|
||||
- Define chain order consistently with existing D&D artifacts: generic JSON,
|
||||
shape, registry/identity or normalized invariants at the appropriate stage,
|
||||
source references, durable JSON Schema, then advisory relatedness.
|
||||
- Update registrar tests for artifact kinds, keys, execution classes, reference
|
||||
slot requirements, builder construction, assets, profile use, evidence
|
||||
projection, chain contents/order, duplicate registration, and failure
|
||||
propagation.
|
||||
- Update any integration-level artifact-kind allowlists or typed registries
|
||||
required by the framework; do not add module-specific orchestration logic.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- One D&D registration call exposes both complete lanes and no partial
|
||||
registration succeeds silently.
|
||||
- Catalog inspection reports correct artifact kinds, stages, execution classes,
|
||||
reference slots, profiles, and fingerprints.
|
||||
- Both artifact kinds support evidence projection without registry evidence
|
||||
leakage.
|
||||
- `go test ./internal/modules/dnd/register/... ./internal/modules/dnd/...` passes
|
||||
offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium-to-large but limited to composition and suitable for one implementation
|
||||
prompt.
|
||||
|
||||
## Stage 13: Add Maintained Pipeline And Handoff Coverage
|
||||
|
||||
### Goal
|
||||
|
||||
Exercise the feature through real configuration, ordered generated references,
|
||||
chunk operations, acceptance gates, and durable output.
|
||||
|
||||
### Work
|
||||
|
||||
- Update `examples/dnd-complete.config.yml`:
|
||||
- add `locations` to the first descriptive step with extract, append-order
|
||||
merge, and LLM-backed normalize bindings;
|
||||
- add a generated `locations` reference to the next step;
|
||||
- add `location-occurrences` to that step with extract, append-order merge,
|
||||
and deterministic normalize bindings; and
|
||||
- add both lanes to evidence-context output where appropriate.
|
||||
- Keep the minimal example minimal unless its stated purpose requires a
|
||||
location lane; do not turn it into a second complete example.
|
||||
- Extend maintained example-loading/config-validation tests.
|
||||
- Add integration tests that prove:
|
||||
- the normalized accepted registry is handed off in memory;
|
||||
- the occurrence lane cannot run before its producer;
|
||||
- missing, cyclic, wrong-kind, wrong-media-type, rejected, or unaccepted
|
||||
producers are rejected at the existing boundaries;
|
||||
- same-name locations remain distinguishable by ID through the handoff;
|
||||
- registry evidence never becomes occurrence evidence;
|
||||
- retries and checkpoints honor prompt, schema, identity, and generated-
|
||||
reference fingerprints; and
|
||||
- output contains both durable artifact envelopes and evidence context.
|
||||
- Use recording/fake structured clients only; no network-dependent tests.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- The complete example loads through the real config path and exercises all
|
||||
registered D&D lanes.
|
||||
- Ordered handoff failure semantics match the framework's existing fail-whole-
|
||||
run policy.
|
||||
- Integration tests cover behavior rather than duplicating package internals.
|
||||
- `go test ./internal/modules/integration/... ./internal/config/...` and any
|
||||
example-specific test targets pass offline.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium-to-large but coherent as one end-to-end integration prompt.
|
||||
|
||||
## Stage 14: Publish Current Documentation And Perform Final Verification
|
||||
|
||||
### Goal
|
||||
|
||||
Make the implemented feature discoverable and retire fulfilled future-work
|
||||
language without leaving development-history documentation behind.
|
||||
|
||||
### Work
|
||||
|
||||
- Create canonical integration contracts:
|
||||
- `docs/integrations/dnd-location-artifacts.md`;
|
||||
- `docs/integrations/dnd-location-occurrence-artifacts.md`.
|
||||
- Update `docs/config.md` with both selectable keys, the `locations` reference
|
||||
slot and compatibility, execution classes, validators, default chains, and
|
||||
the complete-example link.
|
||||
- Update `docs/internal/dnd.md` with nine-lane composition, shared entity
|
||||
reconciliation, evidence-anchored identity, occurrence grounding, prompt
|
||||
asset reuse, and intentional lane differences. Link to integration contracts
|
||||
instead of duplicating their JSON shapes.
|
||||
- Update `docs/integrations/json-output.md`, `README.md`, and other current
|
||||
canonical inventories only where repository inspection shows that the new
|
||||
artifact kinds or maintained example must be listed.
|
||||
- Remove the fulfilled `Location Extraction` section from
|
||||
`docs/roadmap/future.md`. Keep the generic LLM-assisted deduplication item and
|
||||
clarify only if needed that the new D&D helper does not fulfill that broader
|
||||
feature.
|
||||
- Verify all relative links and search current documentation for stale
|
||||
seven-lane counts, missing keys, obsolete location-planning claims, and
|
||||
accidental claims that references are evidence.
|
||||
- Run formatting, focused tests, the full Go test suite, static analysis, and
|
||||
repository-provided config/example checks. Inspect `git diff --check` and
|
||||
confirm no unrelated files changed.
|
||||
- After implementation and verification are complete, leave `location.md` and
|
||||
this plan in place for the user's separate roadmap-retirement step; do not
|
||||
delete them unless explicitly asked.
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- Current documentation describes the implemented contracts and only
|
||||
implemented behavior outside `docs/roadmap/`.
|
||||
- The roadmap no longer presents completed location tracking as future work.
|
||||
- Links, examples, module inventories, and default-chain tables agree with
|
||||
production registration.
|
||||
- `go test ./internal/modules/dnd/...` passes.
|
||||
- `go test ./internal/modules/integration/...` passes.
|
||||
- `go test ./...` passes.
|
||||
- `go vet ./...` passes.
|
||||
- Repository-provided configuration/example validation passes.
|
||||
- `git diff --check` reports no errors.
|
||||
|
||||
### Prompt Size
|
||||
|
||||
Medium and suitable for one implementation prompt.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. The feature roadmap fixes the artifact shapes, identity scope and
|
||||
derivation, occurrence categories, classification precedence, reference
|
||||
dependency, reconciliation safety boundary, pipeline placement, and non-goals
|
||||
needed to implement every stage without an additional product decision.
|
||||
337
docs/roadmap/location.md
Normal file
337
docs/roadmap/location.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# D&D Location Tracking
|
||||
|
||||
## Purpose
|
||||
|
||||
Add evidence-grounded D&D location tracking without turning a single extractor
|
||||
into both an entity registry and an event classifier. The target design follows
|
||||
the established NPC pattern: one lane identifies canonical location records and
|
||||
a later lane records how the party related to those locations in the transcript.
|
||||
|
||||
This roadmap defines the desired end state and policy choices. The ordered work
|
||||
needed to reach that state is in [the implementation plan](implementation.md).
|
||||
|
||||
## User Intent
|
||||
|
||||
- Record locations the party visits or that the session otherwise discusses.
|
||||
- Distinguish current physical presence from plans, recollections, and ordinary
|
||||
mentions.
|
||||
- Preserve transcript evidence for every durable record.
|
||||
- Reconcile aliases and repeated appearances conservatively.
|
||||
- Keep distinct places separate when they happen to share a generic name.
|
||||
- Keep the schemas minimal. Location description, hierarchy, participants, and
|
||||
narrative analysis belong in other artifacts or deterministic joins.
|
||||
|
||||
## Target Capability
|
||||
|
||||
The D&D module family will have two new lanes:
|
||||
|
||||
1. `dnd/locations` produces a session-scoped registry of physical places.
|
||||
2. `dnd/location-occurrences` consumes the normalized location registry and
|
||||
produces an ordered list of source-grounded relationships between the party
|
||||
and those places.
|
||||
|
||||
The normalized location artifact is handed to the occurrence lane through a
|
||||
required generated reference named `locations`. The occurrence lane must use
|
||||
that registry for identity grounding, but the current transcript remains its
|
||||
only evidence source.
|
||||
|
||||
## Durable Artifact Contracts
|
||||
|
||||
Both contracts remain at `v1`; Notarius is pre-release and does not need a
|
||||
compatibility layer for these new artifacts.
|
||||
|
||||
### Location registry
|
||||
|
||||
The location lane uses:
|
||||
|
||||
- artifact kind: `dnd/location-list`
|
||||
- module key: `dnd/locations`
|
||||
- schema ID: `notarius.dnd.locations`
|
||||
- schema name: `notarius_dnd_locations_v1`
|
||||
- media type: `application/json`
|
||||
- root member: `locations`
|
||||
|
||||
Each location contains exactly:
|
||||
|
||||
| Field | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `id` | string | Deterministic, session-scoped canonical location identity. |
|
||||
| `name` | string | Evidence-grounded display name or transcript-established label. |
|
||||
| `source_refs` | non-empty source-reference array | Current-transcript evidence that identifies the place. |
|
||||
|
||||
Locations are physical or spatial places: planes, regions, settlements,
|
||||
districts, buildings, rooms, landmarks, routes, and geographic features. A
|
||||
generic label such as `the tavern` is permitted only when the transcript uses
|
||||
it for a specific place. The extractor must not invent a qualifier merely to
|
||||
distinguish that place from another place with the same label.
|
||||
|
||||
The registry does not contain type, parent, description, summary, coordinates,
|
||||
participants, visit status, or occurrence data. Parent and child places are
|
||||
separate identities when the transcript identifies both; nesting alone is not
|
||||
a reason to merge them.
|
||||
|
||||
### Location occurrences
|
||||
|
||||
The occurrence lane uses:
|
||||
|
||||
- artifact kind: `dnd/location-occurrence-list`
|
||||
- module key: `dnd/location-occurrences`
|
||||
- schema ID: `notarius.dnd.location_occurrences`
|
||||
- schema name: `notarius_dnd_location_occurrences_v1`
|
||||
- media type: `application/json`
|
||||
- root member: `occurrences`
|
||||
|
||||
Each occurrence contains exactly:
|
||||
|
||||
| Field | Type | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `location_id` | string | An exact ID from the consumed normalized location registry. |
|
||||
| `name` | string | The canonical display name associated with `location_id`. |
|
||||
| `kind` | enum | `visited`, `planned`, `recalled`, or `mentioned`. |
|
||||
| `source_refs` | non-empty source-reference array | Current-transcript evidence for both the place and the classified occurrence. |
|
||||
|
||||
`location_id` is required even though existing NPC interactions currently use
|
||||
name-only grounding. Locations can legitimately share the same display name,
|
||||
so a name alone cannot provide an unambiguous cross-artifact link. The name is
|
||||
retained for readable standalone output and must exactly match the registry
|
||||
record selected by the ID after normalization.
|
||||
|
||||
## Identity Policy
|
||||
|
||||
Location identity is conservative and scoped to one source document. It is not
|
||||
a campaign-wide or cross-session world identity.
|
||||
|
||||
Display normalization trims surrounding whitespace and collapses internal
|
||||
Unicode whitespace. Comparison normalization uses the existing D&D entity
|
||||
rules: Unicode NFKC normalization, normalized apostrophes, collapsed
|
||||
whitespace, and Unicode case folding.
|
||||
|
||||
The canonical ID is:
|
||||
|
||||
~~~text
|
||||
location:sha256:<lowercase SHA-256 hex digest>
|
||||
~~~
|
||||
|
||||
The digest input is the UTF-8 encoding of compact JSON for this five-element
|
||||
array:
|
||||
|
||||
~~~text
|
||||
["dnd.locations.identity.v1", comparison_name, source_id, start_unit_id, end_unit_id]
|
||||
~~~
|
||||
|
||||
The source values come from the earliest reference after canonical reference
|
||||
sorting and exact deduplication. Compact JSON array encoding is part of the
|
||||
identity contract: it avoids delimiter ambiguity and must not be replaced
|
||||
without changing the policy version. A blank comparison name or missing valid
|
||||
source reference produces no manufactured ID and remains a validation error.
|
||||
|
||||
Including the evidence anchor prevents two unrelated places called `the
|
||||
tavern` from receiving the same ID. When semantic normalization safely groups
|
||||
aliases or repeated appearances, it first chooses an existing canonical display
|
||||
name and unions the evidence; it then derives the final ID from that name and
|
||||
the earliest unioned reference.
|
||||
|
||||
The normalizer may merge records only when transcript evidence clearly shows
|
||||
that they denote the same physical place. It must not merge records solely
|
||||
because:
|
||||
|
||||
- their comparison names are equal;
|
||||
- they are near one another in the transcript;
|
||||
- one is spatially nested inside the other; or
|
||||
- their labels are both generic.
|
||||
|
||||
Distinct normalized records may therefore have the same comparison name, but
|
||||
their IDs must be unique and correctly derived. Exact duplicates with the same
|
||||
comparison name and canonical evidence may be collapsed deterministically.
|
||||
|
||||
## Occurrence Semantics
|
||||
|
||||
Each occurrence has one kind:
|
||||
|
||||
- `visited`: current-session gameplay establishes that one or more party
|
||||
members are physically present at the location, including an arrival,
|
||||
continuing presence, or departure.
|
||||
- `planned`: the party explicitly proposes, intends, or agrees to future travel
|
||||
to the location. Mere hypotheticals or speculation are not plans.
|
||||
- `recalled`: the transcript explicitly recounts or recaps the party being at
|
||||
the location before the current session's live events.
|
||||
- `mentioned`: the location is explicitly referenced but the occurrence does
|
||||
not meet a stronger definition. This includes lore, directions, third-party
|
||||
activity, non-actionable speculation, and out-of-character discussion.
|
||||
|
||||
An inferred but unstated place produces no location or occurrence. Uncertainty
|
||||
is handled by conservative omission rather than an `uncertain` enum value.
|
||||
|
||||
For one occurrence supported by overlapping evidence, classification precedence
|
||||
is `visited`, then `planned`, then `recalled`, then `mentioned`; `mentioned` is
|
||||
the fallback. A passage may produce multiple records when it independently
|
||||
supports separate facts, such as recalling an earlier visit while planning a
|
||||
return. Exact duplicates with the same ID, kind, and canonical evidence are
|
||||
collapsed. Different kinds or independently supported evidence remain.
|
||||
|
||||
Output is ordered by earliest evidence in source-document order, then by
|
||||
`location_id`, `name`, kind order (`visited`, `planned`, `recalled`,
|
||||
`mentioned`), and the remaining canonical reference sequence.
|
||||
|
||||
## Extraction, Normalization, And Evidence
|
||||
|
||||
### Location registry lane
|
||||
|
||||
The extractor is LLM-backed and follows the shared D&D extraction prompt and
|
||||
input conventions. It emits names and source ranges through a private response
|
||||
schema; deterministic mapping supplies the current source ID and derives
|
||||
candidate IDs. Campaign references may disambiguate terminology but never
|
||||
become durable evidence.
|
||||
|
||||
The merger uses the typed append-order convention. The normalizer is LLM-backed:
|
||||
it deterministically prepares names and evidence, then asks the model only for
|
||||
duplicate groups. The model may identify groups and choose a canonical member,
|
||||
but it may not create, delete, rewrite, or directly replace durable records.
|
||||
Code validates the proposal, applies non-overlapping safe groups, unions
|
||||
evidence, derives final IDs, orders output, and emits bounded warnings.
|
||||
|
||||
Malformed, unknown, overlapping, or ambiguous proposal groups are rejected.
|
||||
The normalizer uses the existing bounded retry behavior and falls back to the
|
||||
safe deterministic candidate set if no usable proposal is obtained.
|
||||
|
||||
### Location occurrence lane
|
||||
|
||||
The extractor is LLM-backed and requires exactly one validated `locations`
|
||||
reference. The prompt projection contains only ordered `{id, name}` pairs; it
|
||||
omits registry evidence and reference provenance. The model must copy both
|
||||
values from one projected record and cite current-transcript source ranges for
|
||||
the occurrence.
|
||||
|
||||
The occurrence normalizer is deterministic. It canonicalizes names by exact
|
||||
registry ID, normalizes evidence and ordering, and removes exact duplicates.
|
||||
Unknown IDs and mismatched ID/name pairs remain inspectable validation failures
|
||||
rather than being guessed or silently reassigned.
|
||||
|
||||
The occurrence lane cannot add a missing location to the registry. If the
|
||||
location extractor omitted a place, the correct behavior is to omit its
|
||||
occurrence and improve the upstream extraction later.
|
||||
|
||||
## Shared Entity Reconciliation
|
||||
|
||||
Adding a second LLM-assisted entity registry demonstrates a concrete shared
|
||||
need in the D&D domain. The existing NPC normalization context-window and
|
||||
proposal-safety logic will move to
|
||||
`internal/modules/dnd/shared/entityreconcile` and serve both NPC and location
|
||||
normalizers.
|
||||
|
||||
The shared package owns:
|
||||
|
||||
- deterministic opaque candidate keys;
|
||||
- source-window construction and canonical prompt materials;
|
||||
- a common private duplicate-group response contract;
|
||||
- validation of unknown, repeated, overlapping, malformed, or ineligible
|
||||
candidate keys; and
|
||||
- immutable assessment results identifying safe groups.
|
||||
|
||||
It does not call the LLM, choose domain-specific canonical names, derive
|
||||
durable IDs, mutate domain artifacts, or format domain warnings. Those
|
||||
responsibilities remain in each normalizer.
|
||||
|
||||
NPC normalization will migrate to the shared key-based proposal contract
|
||||
without changing its durable NPC behavior. Its prompt and private response
|
||||
schema fingerprints are expected to change, so stale NPC normalization
|
||||
checkpoints will invalidate normally.
|
||||
|
||||
The two normalizers will reuse an exactly identical shared reconciliation
|
||||
instruction asset and private response schema. Module-owned task text will
|
||||
continue to define the different NPC and location identity rules. This keeps
|
||||
shared prompt content identical without pretending the two domains have the
|
||||
same semantic merge policy.
|
||||
|
||||
This helper is intentionally D&D-specific. It does not implement the broader
|
||||
domain-neutral replacement-element normalizer still described in
|
||||
[future work](future.md).
|
||||
|
||||
## Reference Contract And Pipeline Placement
|
||||
|
||||
The generated reference slot is named `locations` and accepts exactly one JSON
|
||||
artifact of kind `dnd/location-list`, with the established 1 MiB limit. It is
|
||||
required by both extraction and normalization for
|
||||
`dnd/location-occurrences`. Static file bindings remain valid where the
|
||||
framework permits them, but the maintained complete example uses a generated
|
||||
same-run artifact.
|
||||
|
||||
The complete D&D pipeline places `locations` in the first descriptive step
|
||||
alongside the independent NPC, item-event, and scene-description lanes. It
|
||||
places `location-occurrences` in the next step and binds the accepted normalized
|
||||
location artifact from the first step. The occurrence lane has no mandatory
|
||||
NPC or scene-description dependency.
|
||||
|
||||
No current downstream lane is changed to consume location artifacts. Future
|
||||
narrative reports or joins may use the canonical IDs after defining their own
|
||||
contracts.
|
||||
|
||||
## Validation And Production Defaults
|
||||
|
||||
The location registry receives production validators for:
|
||||
|
||||
- required shape and supported ID syntax;
|
||||
- current-document and current-chunk source ranges;
|
||||
- normalized identity derivation and ID uniqueness; and
|
||||
- advisory source relatedness.
|
||||
|
||||
The location occurrence artifact receives production validators for:
|
||||
|
||||
- required shape and the four supported kinds;
|
||||
- registry membership and exact ID/name correspondence;
|
||||
- normalized ordering and exact-duplicate invariants;
|
||||
- current-document and current-chunk source ranges; and
|
||||
- advisory source relatedness.
|
||||
|
||||
Validators remain immutable and diagnostic. Durable JSON Schema validation
|
||||
stays in the production chains after semantic shape and source-reference
|
||||
checks, consistent with the existing D&D lanes.
|
||||
|
||||
## Documentation End State
|
||||
|
||||
Implementation will add canonical integration documents for both durable
|
||||
artifacts and update current-state documentation to cover:
|
||||
|
||||
- both module and artifact keys;
|
||||
- the `locations` generated-reference slot;
|
||||
- production validators and default chains;
|
||||
- D&D family composition, reconciliation, identity, and grounding behavior;
|
||||
- the complete maintained pipeline example; and
|
||||
- JSON output and evidence-context support.
|
||||
|
||||
After the feature is implemented, the fulfilled Location Extraction section is
|
||||
removed from `future.md`. Historical implementation narration remains in
|
||||
version control rather than current documentation.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Campaign-wide or cross-session canonical location IDs.
|
||||
- A location ontology, hierarchy, map, coordinates, or containment graph.
|
||||
- Location descriptions, summaries, participants, ownership, or encounter
|
||||
analysis.
|
||||
- Inferring a location that the transcript does not identify.
|
||||
- Automatically creating registry records from occurrence output.
|
||||
- Changing NPC-interaction artifacts to use NPC IDs.
|
||||
- Making other lanes consume location references.
|
||||
- A generic domain-neutral LLM deduplication framework.
|
||||
- Long-term artifact-version migration machinery.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Both durable contracts are minimal, strict, versioned, and registered.
|
||||
- Location IDs are deterministic under the documented policy and do not force
|
||||
same-named places to collapse.
|
||||
- Alias and repeat reconciliation is proposal-only, conservatively validated,
|
||||
and safe on retry exhaustion.
|
||||
- Occurrences use one of the four defined kinds and carry an unambiguous
|
||||
registry ID/name pair plus current-transcript evidence.
|
||||
- Missing, malformed, oversized, or incompatible `locations` references fail
|
||||
through the established configuration or operation boundaries.
|
||||
- Both lanes have typed mergers, normalizers, evidence projectors, validators,
|
||||
default chains, prompt/profile metadata, and registration coverage consistent
|
||||
with the D&D family.
|
||||
- NPC normalization retains its durable behavior after adopting the shared
|
||||
reconciliation helper.
|
||||
- The maintained complete example loads and exercises the generated handoff.
|
||||
- Focused D&D and integration tests pass offline, and current documentation
|
||||
describes only implemented behavior once the work is complete.
|
||||
Reference in New Issue
Block a user