Improve semantic reconciliation retries

This commit is contained in:
2026-08-28 18:25:00 +00:00
parent 5cab4e512e
commit 4da9360d74
25 changed files with 771 additions and 69 deletions

View File

@@ -0,0 +1,76 @@
# ADR-0016: Use feedback-aware module-requested retries
**Status:** Accepted
**Date:** 2026-08-28
## Context
An LLM-backed module can discover that a structurally valid model response is
unsafe while translating it into a typed candidate, before the ordinary
validator chain runs. Semantic registry reconciliation is the first such case:
the shared core can identify invalid duplicate-group proposals, and typed
application can reject a domain-incompatible group while preserving a safe
partial result. Repeating the original request without the rejected response or
corrective explanation gives the model no information with which to improve.
The existing feedback-aware validation mechanism already establishes the safe
correction protocol, but module-owned retry directives also carry internal
reason codes, operator messages, and fallback diagnostics. Those values are not
model instructions. Some module retry conditions, including exhausted
structured-output repair, also have no valid exact response to append.
## Decision
An LLM-backed normalizer may attach optional, bounded model-facing correction
guidance to a module-requested retry. Guidance is a separate contract field; the
framework never derives it from a reason code, operator message, diagnostic, or
error. A feedback-capable directive must include the exact model candidate that
controlled the safe fallback and must use `single_response_v1`.
The artifact-neutral producer-attempt state machine constructs the correction
from that exact latest response and the supplied guidance. The producer rebuilds
its complete ordinary request and appends the response as one assistant message
and the guidance as one user message. Earlier attempts do not accumulate, and
the attempt consumes the same configured stage retry budget as operational,
structural, validation, and feedback-free module retries.
A module retry without guidance remains valid and starts a fresh attempt. This
is the required behavior when no valid model candidate exists, including after
structured-output repair is exhausted. If feedback is supplied without a valid
supported candidate, the framework fails the module contract rather than
retrying blindly or inventing guidance.
After retry exhaustion, the normalizer's safe fallback continues through its
validator chain. Safe semantic groups may remain applied, unsafe groups remain
separate, and bounded fallback diagnostics may explain the process degradation.
Exact responses and correction text remain attempt-local and are excluded from
ordinary errors, warnings, manifests, receipts, caches, and checkpoints.
This decision extends, rather than supersedes,
[ADR-0014](0014-feedback-aware-validation-retries.md): both correction sources
use the same transport-neutral payload, replacement-request semantics, outer
retry budget, and sensitive-data boundary.
## Alternatives considered
- Continue blind module retries. This preserves a smaller contract but wastes
the module's deterministic diagnosis and commonly repeats the same defect.
- Convert module safety checks into validators. Typed reconciliation must apply
only safe proposal groups and retain a fallback before validation; moving
artifact-owned translation and application policy into validators would blur
stage ownership.
- Copy the retry reason or operator message into the model request. Those values
are written for provenance and humans, can contain opaque internal labels,
and do not reliably describe a correct replacement.
- Require feedback for every module retry. Structural failures may have no
valid exact candidate, so this would either prevent useful fresh retries or
fabricate prior-response material.
## Consequences
The normalize retry contract and generic producer-attempt directive gain an
optional correction-guidance field and candidate-pairing validation. Modules
that use it must provide semantically meaningful bounded prose and an exact
candidate. Registry reconciliation maintains separate operator and model
renderers, and policy fingerprints change so checkpoints created under blind
retry behavior are not reused.

View File

@@ -29,13 +29,17 @@ The D&D registrar registers the familys artifact codecs, extractors, typed
append-order mergers, normalizers, validators, prompt assets, fallback LLM
profile asset, and default validator chains. Each extractor and normalizer has
a stable module spec, explicit execution class, strict option decoding, and a
typed builder. Scene chunking, every extractor, and NPC, location, and item-registry
normalization are registered as `llm_backed`; the remaining current D&D mergers
and normalizers are `deterministic`. The metadata is available to catalog inspection and
resolved-pipeline debug data and determines which selected bindings inherit the
pipeline profile. The registry normalizers use `single_response_v1`, forwarding
corrections to their reconciliation completion and retaining the accepted raw
proposal only as an owned model candidate. Configuration remains the canonical owner of the exact keys,
typed builder. Scene chunking, every extractor, and NPC, location, and
item-registry normalization are registered as `llm_backed`; the remaining
current D&D mergers and normalizers are `deterministic`. The metadata is
available to catalog inspection and resolved-pipeline debug data and determines
which selected bindings inherit the pipeline profile. The registry normalizers
use `single_response_v1`, forwarding corrections to their reconciliation
completion and retaining the accepted raw proposal only as an owned model
candidate. When deterministic proposal safety or typed application rejects a
group, they provide separate model-facing prose for a corrective module retry;
internal issue categories, reason codes, and operator messages remain
diagnostic-only. Configuration remains the canonical owner of the exact keys,
profile precedence, and validator order.
Private structured-LLM response schemas are deliberately minimal. They reject
@@ -117,6 +121,17 @@ presentation, and final ephemeral generic transcript windows. These orders and
cache controls are prompt behavior; change them only through the owning
manifest and prompt declaration.
NPC, item, and location registry reconciliation translate shared proposal
safety categories into bounded prose that references only the response-local
duplicate-group ordinals and candidate handles. Item reconciliation appends its
typed rule that currency may be consolidated only with aliases of the same
denomination and never with non-currency items. The next normalize attempt
receives that prose with the exact defective proposal under the shared
replacement-request protocol. Structurally invalid output has no valid proposal
candidate and receives a fresh feedback-free attempt instead. If the stage
budget is exhausted, safe groups stay applied, unsafe groups stay separate, and
one fallback warning summarizes the final defect without raw model content.
## Evidence, Candidates, And Normalization
The current transcript is the only durable evidence source. Extractors assign

View File

@@ -247,8 +247,12 @@ redaction boundary.
Structural repair does not replace pipeline retry behavior: a binding's
configured retry count reruns its complete stage attempt after an operational
or structural error, module-requested retry, or actionable semantic rejection.
The pipeline owns attempt lifecycle, validation chains, and retry diagnostics;
see [Pipeline Internals](pipeline.md#validation-retries-and-output) and the
An actionable module-requested retry and a validator rejection both use the
same correction payload when the producer exposes an exact latest response;
feedback-free module retries reconstruct the ordinary request without appended
messages. The pipeline owns attempt lifecycle, validation chains, and retry
diagnostics; see
[Pipeline Internals](pipeline.md#validation-retries-and-output) and the
[binding reference](../config.md#module-bindings-and-validators).
## Timeout Ownership

View File

@@ -115,6 +115,16 @@ eligible producers. Deterministic skip, limit, and fallback outcomes carry no
model candidate, so a later rejection applies terminal policy without spending
an ineffective semantic retry.
When proposal assessment or typed group application rejects a structurally
valid group, the normalizer may return its safe partial value with a
module-requested retry. A feedback-capable directive supplies bounded
model-facing correction guidance separately from operator diagnostics and
retains the exact proposal response as its candidate. The shared stage retry
mechanism appends that response and guidance to a fresh complete request. A
structurally invalid completion has no valid candidate and therefore requests a
feedback-free fresh attempt. On exhaustion, only the final safe fallback and
its bounded process diagnostic advance to validation.
The core supplies a conservative generic prompt and the single private
response schema. A domain prompt may substitute its semantic instructions but
mounts the core-owned protocol and candidate/transcript presentation assets.

View File

@@ -154,18 +154,25 @@ complete validation chain. It preserves terminal diagnostics only from the final
or rejected attempt, plus one fixed validation-incomplete warning per validator whose execution
budget was exhausted under `warn_continue`. Cancellation stops retries.
Normalizer-specific retry directives consume this same budget and validate any
final safe fallback through the normalizer chain.
final safe fallback through the normalizer chain. A directive may carry bounded
correction guidance only when it also exposes the exact latest
`single_response_v1` candidate. The state machine then uses the same replacement
request shape as validator correction. A directive without guidance clears any
prior correction and starts a fresh attempt, which preserves structural retry
behavior when no valid response exists.
The artifact-neutral producer-attempt state machine owns that shared budget,
attempt provenance, semantic-correction material, and terminal-policy
selection. It accepts producer and complete-validation closures, so artifact
materialization, cache handling, checkpoints, and debug output stay at the
operation boundary. It distinguishes operational, structural, module-requested,
and semantic retries. A semantic retry is available only for a valid latest
`single_response_v1` candidate; a deterministic or no-model rejection instead
settles the semantic policy immediately. Structural-output errors alone use the
structural policy, and validation failure without rejection settles the
validator-failure policy without regenerating the producer.
and validator-semantic retries. Model feedback from either semantic source is
available only for a valid latest `single_response_v1` candidate. A
deterministic or no-model validator rejection instead settles the semantic
policy immediately, while a feedback-free module directive remains an ordinary
fresh retry. Structural-output errors alone use the structural policy, and
validation failure without rejection settles the validator-failure policy
without regenerating the producer.
Chunk planning uses this state machine for generated plans. A rejected or
validation-incomplete automatic cache hit is not model material and therefore

View File

@@ -201,6 +201,13 @@ they are not model instructions. The framework constructs model-facing retry
text only from the semantic guidance and fails the contract rather than
inventing or truncating missing guidance.
An LLM-backed module may also request a feedback-aware retry when its own
deterministic translation or typed safety policy rejects a structurally valid
model response. It must supply model-facing guidance separately from its
reason code, operator message, and fallback diagnostics, together with the
exact `single_response_v1` candidate. A feedback-free module retry remains
valid when no exact candidate exists.
Default validator chains are production composition policy and are registered
centrally by stage and module. Configuration may replace a stage-local default,
including with an explicitly empty chain. Configured validator order is
@@ -222,12 +229,14 @@ the two budgets must remain separate.
An LLM-backed producer can participate in semantic correction only when it
declares `single_response_v1` and returns the exact one response that directly
controlled its candidate. On an actionable rejection, the framework rebuilds
the ordinary request and appends only the latest defective response as an
`assistant` message plus one aggregated `user` correction message. This is a
fresh replacement request, not a growing conversation. The retry budgets,
terminal policy, and sensitive-data rationale are recorded in
[ADR-0014](../adr/0014-feedback-aware-validation-retries.md).
controlled its candidate. On an actionable validator rejection or
feedback-capable module retry, the framework rebuilds the ordinary request and
appends only the latest defective response as an `assistant` message plus one
aggregated `user` correction message. This is a fresh replacement request, not
a growing conversation. The retry budgets, terminal policy, and sensitive-data
rationale are recorded in
[ADR-0014](../adr/0014-feedback-aware-validation-retries.md) and
[ADR-0016](../adr/0016-feedback-aware-module-requested-retries.md).
When a model selects an application entity, callers must supply a contextual
selection and deterministically attach the opaque application identity whenever

View File

@@ -15,7 +15,7 @@ diagnostics. The remaining near-term work applies those completed foundations
to domain review and empirical evaluation.
The active D&D reliability work is defined by
[D&D Item-Occurrence Holder Reliability](item-occurrence-holder-reliability.md).
[Feedback-Aware Semantic-Reconciliation Retries](semantic-reconciliation-retry-feedback.md).
## Near-Term D&D Pipeline

View File

@@ -0,0 +1,117 @@
# Feedback-Aware Semantic-Reconciliation Retries
## Purpose
Improve semantic registry reconciliation for smaller models by turning the
existing module-requested retry into a bounded corrective exchange. Preserve
the current deterministic safety checks, shared retry budget, and safe fallback
when correction is unsuccessful.
## Problem
NPC, item, and location registry normalizers can reject unsafe duplicate-group
proposals and retain a safe partial result. They currently ask the pipeline for
another attempt, but the next attempt receives the original prompt without the
defective response or an explanation of the defect. The normalizers also build
their retry messages from internal issue-category strings, which are suitable
for diagnostics but not useful model instructions. If all attempts are
exhausted, the warning reports the omitted-group count without explaining the
final defect or confirming that affected candidates were preserved separately.
## Target State
- A normalizer retry directive may carry model-facing correction guidance
separately from its stable reason code, operator message, and fallback
diagnostics.
- When the directive includes correction guidance and a valid
`single_response_v1` model candidate, the shared producer-attempt state
machine reconstructs the complete original prompt and appends the exact
defective response as an assistant message followed by one user correction
message. Earlier failed attempts do not accumulate.
- This correction consumes the existing normalize-stage retry budget. It does
not add a module-local loop or alter PromptKit's independent structured-output
repair budget.
- A retry caused by structurally invalid output remains a fresh stage attempt
without semantic feedback when no valid model candidate is available.
- Internal issue categories remain available for operator and debug
diagnostics, but are never sent to a model. A shared renderer translates
every known category into concise prose that explains the defect and the
required valid shape.
- Item reconciliation appends its domain rule when a proposal attempts to
combine different currency denominations or currency with a non-currency
item. NPC and location reconciliation use only the shared protocol guidance.
- On final exhaustion, safe proposal groups remain applied, unsafe groups
remain separate, and one process warning gives a bounded summary of the final
defect and explicitly states that the affected candidates were preserved
separately.
## Required Work
1. Extend `contracts.NormalizeRetry` and its clone, validation, and debug paths
with an optional, bounded model-facing correction-guidance field. Keep
reason codes and operator messages out of model input. Require guidance to
be paired with a valid `single_response_v1` model candidate; preserve the
existing feedback-free directive for structural retry cases.
2. Extend the generic producer-attempt state machine so a feedback-capable
module retry constructs `SemanticCorrection` from the latest exact response
and the supplied guidance. Preserve attempt provenance, cancellation,
terminal policy, diagnostic handling, byte-faithful response provenance,
and the rule that only the latest response is included.
3. Add a model-facing renderer to `internal/framework/semanticreconcile`.
Implement it with a typed lookup table keyed by `IssueCategory` and an
authoritative list of all categories, rather than a positional array.
Render response-local duplicate-group numbers in human-friendly form,
de-duplicate repeated guidance, enforce a size bound, and return an error for
any unmapped category instead of exposing its raw value. Retain
`IssueDetails` for operator/debug diagnostics.
4. Update the NPC, item, and location registry normalizers to populate the new
correction guidance for semantically invalid proposals. Add the item-only
currency guidance after the shared prose when typed application rejects a
group. Do not add guidance to the invalid-structured-output path when no
exact valid candidate exists.
5. Improve exhausted-fallback diagnostics across all three registries. Describe
the final proposal failure accurately, state that affected candidates were
preserved separately, and keep warning samples bounded and free of raw model
responses.
6. Update the shared semantic-reconciliation and affected normalizer policy
fingerprints so checkpoints produced under blind retry behavior are not
reused. Record the durable module-retry correction decision in a new ADR,
and update the architecture, pipeline, LLM-runtime, and D&D internal docs in
their canonical scopes when the behavior is implemented.
7. Add lean offline behavioral coverage for corrective module retries, exact
latest-response use, feedback-free structural retries, contract validation,
complete category-to-prose coverage, domain-specific item guidance, bounded
messages, safe final fallback, and warning content. Avoid exact prose
snapshots, prompt-length assertions, live providers, and redundant tests of
PromptKit internals.
## Non-Goals
- Changing normalize retry counts, PromptKit repair settings, or pipeline
terminal policies.
- Changing the reconciliation prompt protocol, response schema, registry
artifact schemas, or request-local candidate IDs.
- Reintroducing provider-sensitive schema constraints such as `uniqueItems`.
- Automatically applying an unsafe duplicate proposal or treating a fallback
warning as an extraction-quality warning.
- Persisting raw model responses or correction text in ordinary warnings,
receipts, manifests, caches, or checkpoints.
## Acceptance Criteria
- A semantic proposal rejected by an NPC, item, or location registry normalizer
receives the exact defective response and actionable prose on the next
normalize attempt, using only the configured stage retry budget.
- Every `IssueCategory` has non-empty model-facing guidance, no raw category or
reason-code string reaches the model, and an unmapped future category fails
closed.
- An invalid item consolidation additionally tells the model to keep currency
denominations distinct and not mix currency with non-currency items.
- Structural-output failures without a valid model candidate retain the current
fresh-retry behavior.
- After retry exhaustion, safe consolidations remain applied, affected
candidates remain separate, and the single fallback warning explains both
the final cause and preservation behavior without including raw model output.
- Checkpoint identities change where policy changed, canonical documentation
and a new ADR describe the implemented behavior, and focused tests plus
`go test ./...`, `go vet ./...`, and `go build ./cmd/notarius` pass.