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 underdocs/policy/, the feature roadmap, and the focused implementation/tests named by the stage before editing. - Preserve the fixed pipeline, typed artifact boundaries, root
assetscontent-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_refsbecome 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_patchfor edits,gofmtchanged 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
namehas one comparison-identity match in the supplied registry,registry_refsmust be empty and name resolution selects that record. - When multiple registry records share the comparison identity,
registry_refsmust equal one record's complete canonically ordered source ranges withsource_idremoved. - The model-facing registry projection uses the same
nameplusregistry_refsselector and adds a requiredcontextarray. Unique-name records project emptyregistry_refsandcontextarrays. The private response does not reproducecontext. - 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) andtext(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_refsnever flow into the durable occurrence'ssource_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
- 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.
- Change
dnd_npc_occurrences_llm.v1.jsonso every occurrence requires onlyname,kind, andsource_refs, continues to reject unknown fields, and no longer declaresnpc_id. - 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.
- Remove
NPCIDfrom the privateoccurrenceResponse. After canonicalizing response evidence, resolve every response name with the existingnpcregistry.Registry.Lookupcomparison-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 exactIDand canonicalName. - Change
mappingPolicytodnd.npc_occurrences.extract_mapping.v3. - Stop passing
IdentityPromptInput()to the LLM; use the existing names-onlyPromptInput(). - 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 asIdentityDigest() string, removeIdentityPromptInput(), and update NPC-occurrence extractor, normalizer, invariant-validator, and registry-validator fingerprints to useIdentityDigest(). The digest must still distinguish ID/name identity from the names-only prompt projection. - 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
- Inspect
assets/dnd/item-occurrences/, the item occurrence extractor, the item registry, the item occurrence normalizer and registry validator, and their focused tests. - Change
dnd_item_occurrences_llm.v1.jsonto removeitem_idfrom required fields and properties. Preserve requiredname,kind,quantity,from,to, andsource_refs, all current enums/nullability, and strict unknown field rejection. - 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.
- 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 defensiveLookup(name) (dnd.Item, bool)analogous to the NPC registry. Retain exactLookupIDfor durable normalizers and validators. Because item IDs are derived from the item comparison identity, the names-onlyProjectionDigestremains sufficient for model input and existing checkpoint consumers. - Remove
ItemIDfrom 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 finaldnd.ItemOccurrence. Unknown selections fail the complete extraction; do not alter evidence or nullable-field validation ownership. - Change
mappingPolicytodnd.item_occurrences.extract_mapping.v2. - 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
- Inspect the location registry, location identity and source-reference helpers, the generic source document index, occurrence checkpoint consumers, and their focused tests.
- 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
nameandregistry_refs; - a registry projection entry with exactly
name,registry_refs, andcontext; - a source-free range with exactly
start_unit_idandend_unit_id; and - a context unit with exactly
unit_idandtext. All fields are required in their private JSON shapes, and constructors and accessors must make defensive copies.
- a returned selector with exactly
- Add an operation-scoped immutable grounding type constructed from a resolved
registry and the current
*source.SourceDocument. Its API must provide:- a cloned
contracts.LLMInputMaterialfor thelocation_registryslot; - deterministic resolution of a returned selector to one cloned
dnd.Location; and - the digest of the exact model projection.
- a cloned
- 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, andcontext; - comparison-unique entries use empty
registry_refsandcontextarrays; - every same-name entry uses its complete canonical source ranges stripped
of
source_idand includes ordered context units covered by those ranges; - each context unit contains exactly required integer
unit_idand stringtextfields, and units are deduplicated in source order; and - same-name ranges must have
SourceID == doc.IDand passsource.DocumentIndex.ValidateRef.
- every projection entry has exactly the required fields
- 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.
- 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. - 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 owns the model projection digest. 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. - 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
- Inspect
assets/dnd/location-occurrences/, the location occurrence model, schema loader, extractor, canonicalization, prompt tests, and Stage 3 grounding tests. - Change
dnd_location_occurrences_llm.v1.jsonso each occurrence requires exactlyname,registry_refs,kind, andsource_refs; removelocation_id. Keep all four occurrence kinds. Makeregistry_refsa required array, including an empty array, of strict required positive integer ranges. Keep occurrencesource_refsseparate and unchanged. - Rewrite
location-registry.mdand 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. - Change the private response type to
Name,RegistryRefs,Kind, andSourceRefs. Do not reusesource.SourceReffor the source-free registry range type. - In
Extract, construct operation grounding from the resolved registry andreq.Sourcebefore calling the LLM, put its projection in thelocation_registryinput, 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-sourcesource_refsas evidence. - 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.
- Change
mappingPolicytodnd.location_occurrences.extract_mapping.v2. - Remove the legacy ID-bearing registry
PromptInputand its model-projection digest once the extractor uses operation grounding. Update the occurrence checkpoint to combineIdentityDigest()with the new grounding projection digest, prompt/schema fingerprint, mapping policy, and its existing inputs; do not retain dead compatibility aliases. - 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
- 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.
- Introduce one exported, defensively copied contextual selector type in
entityreconcilewith JSONnameandsource_refs, plus a strict source-free range type. Use it for candidate input views and forDuplicateGroup.Membersand.Canonical. - Keep deterministic candidate keys only inside
Materials. DuringBuildContext, validate and canonicalize candidate references as today, serialize candidate views withoutkey, 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. - Update
Materials.Assessto 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.SafeGroupmay continue returning internal candidate keys so the three domain normalizers retain their position mapping; those keys are not model-facing. - Rewrite
dnd_entity_reconcile_llm.v1.jsonso members and canonical are strict selector objects. Require non-emptynamestructurally where the current schemas do so, requiresource_refs, and make each range strict with required positive integer endpoints. Preserveduplicate_groupsand the existing semantic assessment of minimum group size, membership, duplicates, eligibility, and overlap rather than moving every semantic failure into JSON Schema. - 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.
- Change registry normalization policy identifiers to:
dnd.npc_registry.normalize.v4;dnd.item_registry.normalize.v2; anddnd.location_registry.normalize.v2.
- 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
- 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. - Add
docs/adr/0012-resolve-opaque-entity-identifiers-deterministically.mdin the repository's Nygard ADR format with statusAcceptedand 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. - 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. - Update
docs/internal/dnd.mdto 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. - 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. - 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; anddocs/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.
- 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. - 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
- 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. - Trace all former APIs and fields, including
IdentityPromptInput, ID-bearing item/location prompt projections, privateNPCID/ItemID/LocationIDresponse 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. - 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.
- 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
v1codecs. - 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.
- Review the feature roadmap acceptance criteria one by one. Do not delete
contextual-entity-grounding.mdor 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.