Files
notarius/docs/roadmap/implementation.md

31 KiB

Contextual Entity Grounding Implementation Plan

Objective

Implement Contextual Entity Grounding so D&D LLM prompts return evidence-grounded contextual selectors while Notarius owns canonical entity IDs and referential integrity. Preserve every durable D&D artifact contract and remove opaque IDs only from model-visible inputs and private model responses.

This plan is written for a gpt-5.6-terra coding agent. Implement the stages in numeric order. Each stage is intentionally scoped to one implementation prompt and must leave the repository buildable and its focused tests passing before the next stage begins.

Plan-Wide Decisions

Apply these decisions throughout every stage:

  • Read docs/development.md, all files under docs/policy/, the feature roadmap, and the focused implementation/tests named by the stage before editing.
  • Preserve the fixed pipeline, typed artifact boundaries, root assets content-only rule, module ownership, PromptKit boundary, and evidence rules.
  • Do not change the durable NPC-, item-, location-registry, or occurrence Go types, JSON schemas, schema IDs, schema versions, media types, reference-slot contracts, categories, or generated-reference compatibility.
  • Keep the affected prompt and private response-schema identities at v1. They are private pre-release transport contracts; their changed content hashes provide the required compatibility boundary.
  • Advance semantic policy identifiers exactly as directed in each stage. Do not bump unrelated policy identifiers.
  • A contextual name match uses the entity family's existing comparison policy, never fuzzy matching. A model selection must resolve to exactly one supplied record before a durable ID is attached.
  • An invalid NPC, item, or location selection invalidates the complete extraction operation. Do not silently drop one response record, accept a partial artifact, or defer a known mapping failure to a later validator.
  • Registry provenance remains grounding only. Only the current extraction chunk's source_refs become occurrence evidence.
  • Preserve prompt message order and cache controls unless a stage explicitly directs otherwise. Edit only the selected module or shared assets; do not rewrite unrelated shared prompt bytes.
  • Preserve internal opaque IDs where deterministic code needs them. The rule applies to material shown to the model or requested from it, not to maps, fingerprints, checkpoints, diagnostics, or durable artifacts.
  • Follow docs/policy/testing.md: test package-level behavior and meaningful failure modes, not prompt prose, exact message counts, private helper structure, or a repository-wide string-scanning change detector. All tests remain deterministic, offline, and credential-free.
  • Use apply_patch for edits, gofmt changed Go files, and preserve unrelated worktree changes.

Final Private Selector Contracts

These shapes are implementation requirements, not public artifact schemas.

NPC occurrence response

Each response record contains exactly the required fields name, kind, and source_refs. It does not contain npc_id. Notarius resolves name through the normalized NPC registry and writes the matched registry record's ID and canonical Name into the durable occurrence.

Item occurrence response

Each response record contains the existing required name, kind, quantity, from, to, and source_refs fields. It does not contain item_id. Retain the current nullable representation and kind-specific semantics. Notarius resolves name through the normalized item registry and adds the matched ID and canonical Name.

Location occurrence response

Each response record contains exactly the required fields name, registry_refs, kind, and source_refs. registry_refs is always an array of strict objects containing required integer start_unit_id and end_unit_id; it may be empty.

  • When name has one comparison-identity match in the supplied registry, registry_refs must be empty and name resolution selects that record.
  • When multiple registry records share the comparison identity, registry_refs must equal one record's complete canonically ordered source ranges with source_id removed.
  • The model-facing registry projection uses the same name plus registry_refs selector and adds a required context array. Unique-name records project empty registry_refs and context arrays. The private response does not reproduce context.
  • Same-name records receive bounded identity context consisting of the ordered source units covered by their registry ranges. Each context element is a strict object with exactly the required fields unit_id (integer) and text (string); do not expose the durable location ID, source ID, digest, or a replacement token.
  • Building same-name grounding validates that every registry range belongs to and resolves against the current source. If two records still produce the same contextual selector, grounding construction fails before the LLM call.
  • registry_refs never flow into the durable occurrence's source_refs.

Entity-reconciliation response

The shared response remains an object with required duplicate_groups. Every group has required members and canonical. A member and the canonical selection are strict contextual objects containing:

{
  "name": "Mira Thorn",
  "source_refs": [
    {"start_unit_id": 12, "end_unit_id": 12}
  ]
}

The candidate prompt input uses the same descriptor and contains no key. source_refs is required and non-empty for every eligible candidate. The shared helper may retain its existing candidate-000001-style keys strictly inside Go state to preserve input-position mapping; those keys must never be serialized into prompt input or accepted in the private response.

If two candidates produce an identical contextual descriptor, neither is eligible for model-assisted reconciliation because the model cannot identify them independently. Otherwise the helper converts returned descriptors to its internal candidate keys before applying all existing unknown-member, ineligible-member, duplicate-member, canonical-membership, overlap, retry, and fallback rules.

Stage 1: Convert NPC Occurrences To Name-Based Resolution

Goal

Remove durable NPC IDs from the NPC-occurrence prompt and private response, then resolve the model's contextual name deterministically without weakening checkpoint identity or downstream validation.

Work

  1. Inspect:
    • assets/dnd/npc-occurrences/;
    • internal/modules/dnd/extract/npcoccurrences/;
    • internal/modules/dnd/npcs/registry/;
    • NPC-occurrence normalizer and validator checkpoint fingerprints; and
    • their focused tests.
  2. Change dnd_npc_occurrences_llm.v1.json so every occurrence requires only name, kind, and source_refs, continues to reject unknown fields, and no longer declares npc_id.
  3. Revise the NPC-occurrence instructions to require a supplied canonical NPC name and current-chunk evidence, with no instruction to copy or invent an ID. Continue using the existing shared names-only NPC registry fragment and preserve manifest order/cache controls.
  4. Remove NPCID from the private occurrenceResponse. After canonicalizing response evidence, resolve every response name with the existing npcregistry.Registry.Lookup comparison-key lookup. On the first unknown or non-unique selection, return an extractor-scoped mapping error and no value. For a match, construct the durable occurrence with the registry record's exact ID and canonical Name.
  5. Change mappingPolicy to dnd.npc_occurrences.extract_mapping.v3.
  6. Stop passing IdentityPromptInput() to the LLM; use the existing names-only PromptInput().
  7. Replace the misleading exported model-input API used only for identity fingerprints: retain the unexported ordered {id,name} projection and its digest, expose that value as IdentityDigest() string, remove IdentityPromptInput(), and update NPC-occurrence extractor, normalizer, invariant-validator, and registry-validator fingerprints to use IdentityDigest(). The digest must still distinguish ID/name identity from the names-only prompt projection.
  8. Rewrite existing focused tests around observable behavior: rendered NPC registry input is names-only; the private schema rejects npc_id; valid names acquire the registry ID; comparison-equivalent names canonicalize; unknown names fail the whole extraction; registry identity fingerprints remain distinct and defensive; empty registries accept only empty model results. Remove tests whose only purpose was requiring the model to return exact ID/name pairs.

Acceptance Criteria

  • No NPC-occurrence LLM input or private response contains a durable NPC ID.
  • Durable NPC occurrences still contain the exact registry ID/name pair.
  • Mapping failures remain extractor failures eligible for the configured pipeline retry behavior.
  • Deterministic consumers still fingerprint the ordered registry identity, while spells, combat turns, enemy events, and NPC occurrences share the names-only model projection.

Validation

go fmt ./internal/modules/dnd/npcs/registry ./internal/modules/dnd/extract/npcoccurrences ./internal/modules/dnd/normalize/npcoccurrences ./internal/modules/dnd/validate/npcoccurrences/...
go test ./internal/modules/dnd/npcs/registry ./internal/modules/dnd/extract/npcoccurrences ./internal/modules/dnd/normalize/npcoccurrences ./internal/modules/dnd/validate/npcoccurrences/...

This stage is suitable for one gpt-5.6-terra prompt.

Stage 2: Convert Item Occurrences To Name-Based Resolution

Goal

Give item occurrences the same contextual-name/deterministic-ID boundary while preserving item-specific nullable fields and kind rules.

Work

  1. Inspect assets/dnd/item-occurrences/, the item occurrence extractor, the item registry, the item occurrence normalizer and registry validator, and their focused tests.
  2. Change dnd_item_occurrences_llm.v1.json to remove item_id from required fields and properties. Preserve required name, kind, quantity, from, to, and source_refs, all current enums/nullability, and strict unknown field rejection.
  3. Rewrite the item registry fragment and module instructions to require the supplied canonical name and current-chunk evidence without mentioning an ID. Preserve prompt order and cache controls.
  4. Change the item registry's model projection from ordered {id,name} pairs to ordered names-only objects, add a comparison-key index, and expose a defensive Lookup(name) (dnd.Item, bool) analogous to the NPC registry. Retain exact LookupID for durable normalizers and validators. Because item IDs are derived from the item comparison identity, the names-only ProjectionDigest remains sufficient for model input and existing checkpoint consumers.
  5. Remove ItemID from the private response. During response canonicalization, resolve every contextual name, replace it with the registry record's canonical name, and attach its durable ID when constructing the final dnd.ItemOccurrence. Unknown selections fail the complete extraction; do not alter evidence or nullable-field validation ownership.
  6. Change mappingPolicy to dnd.item_occurrences.extract_mapping.v2.
  7. Update focused tests to cover names-only projection, defensive comparison lookup, schema rejection of item_id, deterministic durable mapping, unknown-name failure after an otherwise valid record, empty registry/result behavior, and preservation of nullable/kind-specific fields.

Acceptance Criteria

  • Model-visible item registry and response content contain no item hash.
  • Every accepted durable occurrence has the matched registry ID and canonical name.
  • Invalid selection remains all-or-nothing, and existing normalizer/validator defense in depth remains unchanged.

Validation

go fmt ./internal/modules/dnd/items/registry ./internal/modules/dnd/extract/itemoccurrences
go test ./internal/modules/dnd/items/registry ./internal/modules/dnd/extract/itemoccurrences ./internal/modules/dnd/normalize/itemoccurrences ./internal/modules/dnd/validate/itemoccurrences/...

This stage is suitable for one gpt-5.6-terra prompt.

Stage 3: Add Contextual Location Grounding

Goal

Replace the location registry's ID/name prompt projection with an immutable, source-aware grounding object that can represent same-name locations safely. Introduce the new path alongside the old occurrence input so this stage remains buildable; Stage 4 performs the atomic extractor cutover and removes the old path.

Work

  1. Inspect the location registry, location identity and source-reference helpers, the generic source document index, occurrence checkpoint consumers, and their focused tests.
  2. In internal/modules/dnd/locations/registry, define the private-model types needed by both grounding and the location occurrence extractor:
    • a returned selector with exactly name and registry_refs;
    • a registry projection entry with exactly name, registry_refs, and context;
    • a source-free range with exactly start_unit_id and end_unit_id; and
    • a context unit with exactly unit_id and text. All fields are required in their private JSON shapes, and constructors and accessors must make defensive copies.
  3. Add an operation-scoped immutable grounding type constructed from a resolved registry and the current *source.SourceDocument. Its API must provide:
    • a cloned contracts.LLMInputMaterial for the location_registry slot;
    • deterministic resolution of a returned selector to one cloned dnd.Location. The prompt material's existing Digest field owns the digest of the exact model projection; do not expose a second grounding-specific digest API.
  4. Construct the projection in registry order. Group entries by the existing location comparison key:
    • every projection entry has exactly the required fields name, registry_refs, and context;
    • comparison-unique entries use empty registry_refs and context arrays;
    • every same-name entry uses its complete canonical source ranges stripped of source_id and includes ordered context units covered by those ranges;
    • each context unit contains exactly required integer unit_id and string text fields, and units are deduplicated in source order; and
    • same-name ranges must have SourceID == doc.ID and pass source.DocumentIndex.ValidateRef.
  5. Fail grounding construction with a bounded, content-safe error if the source is nil, a same-name range is invalid or belongs to another source, a comparison key is empty, or two records produce the same selector. Do not expose transcript text in the error.
  6. Resolution uses the existing comparison key. A unique-name selector is accepted only with empty registry_refs; a same-name selector is accepted only on an exact canonical range match. Reject unknown names, a non-empty range list for a unique name, an empty/partial/reordered range list for an ambiguous name, or any selector not present in the grounding.
  7. Separate deterministic identity fingerprinting from LLM material. Add IdentityDigest() over the registry's ordered {id,name} identity projection, and update the location normalizer and registry-validator checkpoint consumers to use it. The new operation grounding carries the model projection digest in its LLMInputMaterial. Retain the old ID-bearing prompt accessor only as a documented transitional dependency of the still-unchanged location occurrence extractor; do not add new callers.
  8. Add focused tests for unique names, same-name context and selectors, canonical range order, deterministic projection/digest, defensive copies, exact selector resolution, nil/foreign/invalid references, selector collisions, empty registries, and identity fingerprint stability. Do not assert large rendered prompt strings; decode the JSON projection and assert its semantic shape.

Acceptance Criteria

  • The location package can build and resolve contextual selectors without exposing location_id, source_id, digests, or replacement labels.
  • Same-name locations remain distinct and receive meaningful bounded context.
  • Deterministic checkpoint consumers retain an ID-sensitive fingerprint.
  • Only the existing occurrence extractor remains wired to the legacy ID-bearing prompt path until Stage 4; the repository compiles and tests pass.

Validation

go fmt ./internal/modules/dnd/locations/registry ./internal/modules/dnd/normalize/locationoccurrences ./internal/modules/dnd/validate/locationoccurrences/...
go test ./internal/modules/dnd/locations/registry ./internal/modules/dnd/normalize/locationoccurrences ./internal/modules/dnd/validate/locationoccurrences/...

This stage is suitable for one gpt-5.6-terra prompt.

Stage 4: Convert Location Occurrences To Contextual Resolution

Goal

Wire the Stage 3 grounding object into location occurrence extraction and remove durable location IDs from the prompt and private response.

Work

  1. Inspect assets/dnd/location-occurrences/, the location occurrence model, schema loader, extractor, canonicalization, prompt tests, and Stage 3 grounding tests.
  2. Change dnd_location_occurrences_llm.v1.json so each occurrence requires exactly name, registry_refs, kind, and source_refs; remove location_id. Keep all four occurrence kinds. Make registry_refs a required array, including an empty array, of strict required positive integer ranges. Keep occurrence source_refs separate and unchanged.
  3. Rewrite location-registry.md and module instructions to explain the two selector cases, require exact supplied contextual selectors, prohibit invented locations, and state that registry ranges/context are identity grounding rather than occurrence evidence. Preserve manifest order and cache controls.
  4. Change the private response type to Name, RegistryRefs, Kind, and SourceRefs. Do not reuse source.SourceRef for the source-free registry range type.
  5. In Extract, construct operation grounding from the resolved registry and req.Source before calling the LLM, put its projection in the location_registry input, and resolve every returned selector after completion. Attach the selected registry record's exact ID and canonical name to the durable occurrence while retaining only the response's current-source source_refs as evidence.
  6. Fail the whole extraction on grounding-construction failure or the first unknown, malformed, mismatched, or ambiguous selector. This replaces the current behavior that can preserve unknown ID/name pairs for later validators. Keep later normalizer and validator checks as defense in depth for artifacts entering other boundaries.
  7. Change mappingPolicy to dnd.location_occurrences.extract_mapping.v2.
  8. Remove the legacy ID-bearing registry PromptInput and its model-projection digest once the extractor uses operation grounding. Keep the occurrence's static module fingerprint based on IdentityDigest(), prompt/schema fingerprints, and mapping policy. The operation-scoped projection is already covered by source/chunk identity and configured or generated reference dependencies, while its LLMInputMaterial.Digest identifies the exact model input; do not add a second digest API or operation-aware static fingerprint.
  9. Update focused schema, prompt, extractor, canonicalization, checkpoint, and generated-reference tests. Cover unique-name empty selectors, successful same-name selection, failure for an unsupported ambiguous mention, partial/reordered ranges, no registry-to-occurrence evidence leakage, all-or-nothing failure, empty registry/result behavior, and unchanged durable ordering/deduplication.

Acceptance Criteria

  • The location prompt and private response contain no durable location ID.
  • Unique and same-name records resolve according to the final selector contract.
  • Accepted durable output is unchanged in shape and still contains an exact location ID/name pair.
  • Registry context cannot become durable occurrence evidence.

Validation

go fmt ./internal/modules/dnd/extract/locationoccurrences ./internal/modules/dnd/locations/registry
go test ./internal/modules/dnd/locations/registry ./internal/modules/dnd/extract/locationoccurrences ./internal/modules/dnd/normalize/locationoccurrences ./internal/modules/dnd/validate/locationoccurrences/...

This stage is suitable for one gpt-5.6-terra prompt.

Stage 5: Replace Reconciliation Keys With Contextual Descriptors

Goal

Change the shared NPC/item/location registry-normalization proposal contract so opaque candidate keys remain internal and the model sees and returns only names plus evidence coordinates.

Work

  1. Inspect:
    • internal/modules/dnd/shared/entityreconcile/;
    • assets/dnd/shared/prompts/common-dnd-entity-reconciliation.md;
    • assets/dnd/entity-reconciliation/schemas/;
    • all three registry normalization manifests and prompt tests; and
    • the NPC, item, and location registry normalizers and reconciliation tests.
  2. Introduce one exported, defensively copied contextual selector type in entityreconcile with JSON name and source_refs, plus a strict source-free range type. Use it for candidate input views and for DuplicateGroup.Members and .Canonical.
  3. Keep deterministic candidate keys only inside Materials. During BuildContext, validate and canonicalize candidate references as today, serialize candidate views without key, derive a stable internal lookup from canonical selector JSON to the corresponding internal candidate key, and detect descriptor collisions before eligibility is established. Colliding candidates must not appear in the prompt input or become eligible; their records remain in deterministic normalization output.
  4. Update Materials.Assess to resolve every returned selector through that internal lookup before running the existing group assessment. Preserve existing issue categories where their meaning still applies. Treat an unknown or collided descriptor as an unknown/ineligible selection, discard only the affected group, and retain existing overlap handling. SafeGroup may continue returning internal candidate keys so the three domain normalizers retain their position mapping; those keys are not model-facing.
  5. Rewrite dnd_entity_reconcile_llm.v1.json so members and canonical are strict selector objects. Require non-empty name structurally where the current schemas do so, require source_refs, and make each range strict with required positive integer endpoints. Preserve duplicate_groups and the existing semantic assessment of minimum group size, membership, duplicates, eligibility, and overlap rather than moving every semantic failure into JSON Schema.
  6. Rewrite the shared reconciliation fragment to tell the model to return supplied contextual descriptors and never invent names or ranges. Remove every instruction about opaque keys. Preserve all three manifests' message ordering and cache controls.
  7. Change registry normalization policy identifiers to:
    • dnd.npc_registry.normalize.v4;
    • dnd.item_registry.normalize.v2; and
    • dnd.location_registry.normalize.v2.
  8. Update shared and domain tests to cover candidate JSON without keys, contextual proposal decoding, valid selector-to-internal-key mapping, equal names with different evidence, descriptor collision exclusion, unknown/partial/reordered descriptors, overlapping groups, canonical membership, invalid structured-output fallback, currency safety, and preservation of every non-applied deterministic candidate. Update prompt asset fixtures to the new selector schema; do not snapshot prompt prose.

Acceptance Criteria

  • No registry normalization prompt input or private response contains a candidate-* key.
  • Internal keys remain inaccessible to the model but may still support safe deterministic position mapping.
  • All existing normalizer safety, retry, fallback, warning, currency, and same-name-location policies remain intact.
  • Identical contextual descriptors cannot be arbitrarily reconciled.

Validation

go fmt ./internal/modules/dnd/shared/entityreconcile ./internal/modules/dnd/normalize/npcregistry ./internal/modules/dnd/normalize/itemregistry ./internal/modules/dnd/normalize/locationregistry
go test ./internal/modules/dnd/shared/entityreconcile ./internal/modules/dnd/normalize/npcregistry ./internal/modules/dnd/normalize/itemregistry ./internal/modules/dnd/normalize/locationregistry

This is the largest stage, but it is one cohesive shared-contract migration and is suitable for one gpt-5.6-terra prompt when implemented exactly within the listed packages. Do not combine it with occurrence or documentation work.

Stage 6: Record The Decision And Update Canonical Documentation

Goal

Document the implemented policy in its durable architectural, internal, and integration homes without duplicating volatile details or presenting roadmap work as current behavior prematurely.

Work

  1. Re-read docs/policy/documentation.md, ADR-0003, ADR-0009, ADR-0011, docs/internal/dnd.md, docs/internal/llm.md, and the six affected registry and occurrence integration documents. Verify the code before describing it.
  2. Add docs/adr/0012-resolve-opaque-entity-identifiers-deterministically.md in the repository's Nygard ADR format with status Accepted and the actual implementation date. Record the model-semantic/deterministic-identity boundary, source-coordinate allowance, request-local-label exception, alternatives, ambiguity behavior, and consequences. Link ADR-0003 and ADR-0009 rather than repeating their complete decisions.
  3. Add a concise normative invariant under the LLM boundary in docs/policy/architecture.md: callers use contextual model selections and attach opaque application identities deterministically when possible. Link ADR-0012 for rationale.
  4. Update docs/internal/dnd.md to replace exact model-facing {id,name} claims with the implemented NPC/item names-only and location contextual selector behavior. Document reconciliation descriptors, internal-only keys, all-or-nothing occurrence mapping failures, normalization fallback, and the separation between registry and occurrence evidence. Do not duplicate the private JSON schemas.
  5. Add only a short ownership clarification to docs/internal/llm.md: the calling module resolves contextual selections; PromptKit and its adapter do not own entity identity.
  6. Update these durable integration contracts while preserving their public ID-bearing wire examples and schema statements:
    • docs/integrations/dnd-npc-registry-artifacts.md;
    • docs/integrations/dnd-npc-occurrence-artifacts.md;
    • docs/integrations/dnd-item-registry-artifacts.md;
    • docs/integrations/dnd-item-occurrence-artifacts.md;
    • docs/integrations/dnd-location-registry-artifacts.md; and
    • docs/integrations/dnd-location-occurrence-artifacts.md. Remove claims that LLM consumers receive {id,name} or that raw model output supplies an ID. State that Notarius maps contextual output into the unchanged exact durable pair.
  7. Revise the generic LLM-assisted deduplication entry in docs/roadmap/future.md: stable unique IDs remain internal deterministic state, while a future model proposal uses contextual descriptors or a specifically justified request-local short label.
  8. Do not change README, CLI, configuration, operations, examples, or public schema files; this feature has no user-selectable surface or public wire change.

Acceptance Criteria

  • ADR-0012 owns rationale; architecture owns the normative boundary; internal docs own mechanics; integration docs own unchanged durable contracts; and the future roadmap no longer proposes durable IDs as the default model selector.
  • No current-behavior document claims that a model copies hash-based entity IDs or opaque reconciliation keys.
  • Documentation does not duplicate private schemas or implementation history.

Validation

git diff --check
rg -n '\{id,name\}|ID/name grounding|Candidate keys are opaque|candidate-[0-9]' docs assets/dnd

Review every search result semantically; durable wire-contract ID/name requirements and internal test fixtures are not automatically errors.

This stage is suitable for one gpt-5.6-terra prompt.

Stage 7: Integration Audit And Final Verification

Goal

Verify the assembled D&D family, remove obsolete identity-copy paths, and finish with a clean, policy-compliant implementation.

Work

  1. Audit every maintained D&D prompt manifest, selected fragment, private schema, and constructed prompt projection. Confirm that no model is asked to reproduce npc:sha256:..., item:sha256:..., location:sha256:..., candidate-*, a UUID, a digest, or another opaque entity handle. Do not confuse runtime metadata or durable output contracts with model-visible material.
  2. Trace all former APIs and fields, including IdentityPromptInput, ID-bearing item/location prompt projections, private NPCID/ItemID/ LocationID response fields, and model-visible candidate keys. Remove dead code, obsolete comments, stale test names, and unused assets. Retain identity-only digests and exact durable lookup APIs used by deterministic consumers.
  3. Review prompt fingerprint registration and checkpoint fingerprints. Confirm that each affected prompt/schema/policy/projection change invalidates the relevant operation and that unrelated D&D lanes retain their existing fingerprints.
  4. Run representative production registration and multi-step pipeline tests using existing fakes. Update only tests whose stable behavior changed. Confirm generated NPC/item/location registry handoffs still prepare and that final durable occurrences encode and validate under their existing v1 codecs.
  5. Run formatting, focused suites, full tests, vet, build, and documentation whitespace checks. Fix only failures caused by this feature. Report any unrelated pre-existing failure without broadening scope.
  6. Review the feature roadmap acceptance criteria one by one. Do not delete contextual-entity-grounding.md or this implementation plan in this stage; roadmap retirement is a separate maintainer action after review.

Acceptance Criteria

  • All feature-roadmap acceptance criteria are met.
  • The repository contains no obsolete model-facing opaque-identity path.
  • Public artifacts and generated handoffs remain compatible.
  • Tests are focused on behavior rather than prose or implementation shape.
  • The worktree contains only intentional feature and documentation changes.

Validation

go fmt ./internal/modules/dnd/...
go test ./internal/modules/dnd/...
go test ./internal/modules/integration/...
go test ./...
go vet ./...
go build ./cmd/notarius
git diff --check
git status --short

This stage is suitable for one gpt-5.6-terra prompt.