| Field | Value |
|---|---|
| RFC | 0033 |
| Title | Envelope-completion criteria; distinguishing truncation from schema-violation in retry routing; truncation-retry cap |
| Status | Accepted |
| Author(s) | OpenWOP Working Group |
| Created | 2026-05-20 |
| Updated | 2026-05-21 (Active → Accepted — see Status history below). |
| Affects | spec/v1/ai-envelope.md (adds §"Envelope-completion criteria"; amends §"Production flow" with the retry-routing distinction) · schemas/capabilities.schema.json (extends envelopes.reliability block from RFC 0032 with completion sub-fields) · spec/v1/observability.md (adds §"Envelope-completion retry routing") · 2 new conformance scenarios · CHANGELOG |
| Compatibility | additive (new MUSTs in previously-undefined behavior space) |
| Supersedes | — |
Summary
Normates the envelope-completion criteria and the truncation-vs-schema-violation retry-routing distinction that hosts MUST honor when emitting structured envelopes via LLM calls. An envelope is _complete_ only when (a) the underlying LLM call reached a clean stop AND (b) the emitted payload validates against the envelope's payload schema. The two failure modes (truncation = output cut off; schema-violation = model emitted wrong-shape JSON) require fundamentally different retry strategies: truncation needs a budget increase; schema-violation needs a corrective system fragment. Conflating them is non-conformant. Reuses RFC 0032's envelope.truncated and envelope.retry.attempted events as the observability surface; reuses the existing limits.schemaRounds as the truncation-retry budget so no new cap surface is introduced. Closes spec gap E5 (Refusal-mode interaction with retry policies) from ai-envelope.md §"Open spec gaps."
Motivation
2.1 Hosts handle envelope completion ad-hoc today
ai-envelope.md §"Production flow" specifies the validation pipeline (shape → kind → payload → contract → limits → redaction → trust → dedup → handler) but is silent on the retry-routing semantics for the two distinct failure modes the LLM call can produce:
- Schema violation. The model returned JSON, but the JSON doesn't validate against the envelope's payload schema (missing required field, wrong type, invalid
enumvalue). Standard retry strategy: re-prompt the model with a corrective fragment describing what was wrong. The model self-corrects on attempt 2.
- Truncation. The model started emitting a syntactically correct JSON envelope but ran out of output tokens before finishing the closing brace. Standard retry strategy: retry with an increased output budget. A corrective schema fragment would be actively harmful — it tells the model "your shape was wrong" when actually the shape was _right_, just incomplete.
Hosts that conflate the two paths produce surprising behavior:
- A host that applies the schema-violation corrective fragment to truncation: the model receives "your envelope was malformed, here's the correct shape" + retries within the same budget, gets cut off at the same point, fails again. The retry budget exhausts without ever increasing the output budget.
- A host that applies the truncation budget-doubling to schema-violation: the model re-emits a still-wrong-shape envelope with twice the budget, producing the same malformed output but using twice the tokens. The retry budget exhausts on a tractable problem because the wrong remediation was applied.
Both failure modes are observable from the provider response's stop_reason field (or the validator's failure shape), but the spec doesn't normate that hosts MUST inspect them. This RFC closes the gap.
2.2 Spec gap E5 closure
ai-envelope.md §"Open spec gaps" E5:
Refusal-mode interaction with retry policies —
refusalMode: "fail-node"plus a per-run retry policy can produce surprising loops if the LLM keeps emitting refused kinds. The interaction needs a worked example.
RFC 0032 (envelope-reliability events) provides the event vocabulary; this RFC provides the worked example — the truncation-vs-schema-violation distinction is the canonical retry-routing case the gap was tracking.
2.3 Truncation-retry DoS opening
A naive truncation-retry implementation can be DoS'd if the model truncates every time: the host keeps doubling the budget, the model keeps truncating at the new ceiling, the run consumes ever-larger token budgets until external cost limits intervene. This RFC normates a per-emission truncation-retry cap (reusing limits.schemaRounds) so the failure mode is bounded by protocol contract, not just by operator wallet limits.
Proposal
§A — Envelope-completion criteria (normative)
Add a new §"Envelope-completion criteria" to spec/v1/ai-envelope.md after §"Production flow":
A host SHALL treat an envelope as complete only when BOTH of the following hold:
1. The underlying LLM call reached a clean stop condition (
stop_reason: "stop"per OpenAI,stop_reason: "end_turn"per Anthropic,finish_reason: "STOP"per Gemini, or vendor equivalent indicating model self-determined completion).2. The emitted payload parses as JSON and validates against the envelope's per-kind payload schema (per §"Schema discipline"), after applying the host's normalization pass (if any).
If condition 1 fails (truncation), the host SHALL emit
envelope.truncated(per RFC 0032 §B.4) and MAY retry per §B. Hosts MUST NOT apply schema-correction system fragments to truncation failures — truncation is an output-size problem, not a schema problem.If condition 2 fails (schema violation, truncation-clean stop but invalid payload), the host MAY retry per §C with a corrective system fragment. Hosts emitting retries SHALL emit
envelope.retry.attempted(per RFC 0032 §B.1) before each retry call past the first.Hosts SHALL distinguish truncation from schema violation in their retry routing AND in emitted telemetry (the two RFC 0032 events distinguish; conflating them in the event stream is non-conformant).
If conditions 1 and 2 both fail in the same response (e.g., the response is truncated AND the partial payload doesn't even satisfy the syntactic-JSON requirement — common when truncation occurs mid-string), the host SHALL treat the failure as truncation (condition 1) for routing purposes, since the underlying problem is output budget; emit
envelope.truncated, NOTenvelope.retry.attemptedwithreason: "schema-violation". This priority matches the principle that the retry strategy should address the upstream cause.
§B — Truncation retry path (normative)
A host MAY retry an emission whose
envelope.truncatedevent fires by re-issuing the LLM call with an increased output budget. The new budget SHOULD be greater than the previous budget; a 2× multiplier is RECOMMENDED but not normative.Provider-ceiling guidance (clarified by amendment 2026-05-21). When the doubled (or otherwise increased) output budget would exceed the active provider's per-call maximum (e.g., Anthropic's
max_tokensceiling, OpenAI's response-length cap, Gemini'smaxOutputTokens), the host MAY further reduce the requested budget to the provider's ceiling AND continue the retry. Hosts SHOULD NOT silently truncate the request to the ceiling without emittingenvelope.retry.attempted { reason: "truncation" }for the retry attempt — the retry IS a retry regardless of budget-clamping. Hosts SHOULD treat a budget-clamped retry that also fails with truncation as terminal (no further retries with the same ceiling), failing the node withenvelope_truncation_unrecoverableper §F.Canonical field name (normative; clarified by amendment 2026-05-21). The advertised multiplier field is
capabilities.envelopes.reliability.completion.truncationBudgetMultiplier. The nametruncationRetryMultiplieris NOT permitted — the multiplier applies to the output budget, not to the retry count, and the semantic distinction matters: retry count is governed bylimits.schemaRoundspercapabilities.md§"Engine-enforced limits"; the multiplier here governs per-retry budget growth. Hosts currently emittingtruncationRetryMultiplierMUST rename totruncationBudgetMultiplierbefore promotion of RFC 0033 toAccepted(a 1-line wire change identified by the MyndHyve adoption feedbackdocs/handoffs/MYNDHYVE-RFC-0030-0033-ADOPTION-FEEDBACK-2026-05-20.md§A.4).Truncation retries SHALL NOT include any corrective system fragment that describes a schema problem — the previous attempt's payload shape (as far as it was emitted before truncation) was correct; the only failure mode is incomplete output.
Truncation retries count against
limits.schemaRounds(the existing per-emission retry budget percapabilities.md§"Engine-enforced limits"). When the retry budget is exhausted while the failure mode remains truncation, the host SHALL emitenvelope.retry.exhausted(per RFC 0032 §B.2) withfinalReason: "truncation"ANDcap.breached(perobservability.md) withkind: "schema". The node MUST fail witherror.code: "envelope_truncation_unrecoverable"(NEW error code; see §F).Each truncation retry MUST emit
envelope.retry.attemptedwithreason: "truncation"per RFC 0032 §B.1 (the second-and-subsequent attempts; the first attempt does not emit per RFC 0032 §B.1 normative text).
§C — Schema-violation retry path (normative)
A host MAY retry an emission whose payload-validation step fails (per
ai-envelope.md§"Production flow" → "Per-kind payload validation") by re-issuing the LLM call with a corrective system fragment describing the validation error. The fragment SHOULD reproduce the validator's failure description (e.g., "required field 'steps' missing") but MUST NOT contain prompt-injection content extracted from the model's previous output (the corrective fragment is host-authored, not model-authored).The corrective system fragment is host-implementation-defined; this RFC does not normate its exact wording. A reasonable shape:
> "Your previous emission did not validate against the required envelope schema. Validator output:
<validator-summary>. Please re-emit your structured response with the correction applied."Schema-violation retries SHALL NOT include an increased output budget — schema violations don't fail for size reasons; doubling the budget on a tractable shape problem is wasted tokens.
Schema-violation retries count against
limits.schemaRounds. When exhausted, the host SHALL emitenvelope.retry.exhaustedwithfinalReason: "schema-violation"ANDcap.breachedwithkind: "schema". The node fails witherror.code: "envelope_invalid"(existing error code per RFC 0021 §"Validation outcomes").Each retry MUST emit
envelope.retry.attemptedwithreason: "schema-violation"per RFC 0032 §B.1.
§D — Refusal and recovery paths (cross-reference)
The two RFC 0032 events envelope.refusal and envelope.recovery.applied fire on distinct paths from truncation/schema-violation. This RFC normates how they interact with retry budgets:
- Refusal. The provider returned an explicit refusal (safety stop, content policy block). Per RFC 0032 §"Unresolved questions" #1 + the RFC 0032 §B.3 normative text: the host MUST NOT retry on refusal. No retry attempt; no
envelope.retry.attemptedevent; the node fails witherror.code: "envelope_refusal"(NEW error code, §F).
- Recovery. Lenient parsing recovered a malformed envelope (e.g., markdown-fence stripping). Recovery is internal to the parsing step, before validation; recovery does NOT consume a retry attempt and does NOT emit
envelope.retry.attempted. The downstream validation either succeeds (envelope is accepted normally) or fails (proceeds to §C schema-violation retry path).
§E — Capability advertisement
schemas/capabilities.schema.json extends the envelopes.reliability block (introduced by RFC 0032 §C) with one new sub-field:
"envelopes": {
"properties": {
"reasoning": { ... },
"tierOneSubsetCompliance": { ... },
"reliability": {
"properties": {
"supported": { ... },
"events": { ... },
"maxRetryAttempts": { ... },
+ "completion": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["distinguishesTruncation"],
+ "properties": {
+ "distinguishesTruncation": {
+ "type": "boolean",
+ "description": "Host implements RFC 0033's truncation-vs-schema-violation retry-routing distinction. When false or absent, the host conflates the two paths (legacy behavior); conformance scenarios for the distinction soft-skip."
+ },
+ "truncationBudgetMultiplier": {
+ "type": "number",
+ "minimum": 1,
+ "maximum": 8,
+ "description": "Host's per-attempt output-budget multiplier on truncation retries. Informational; clients MAY surface this in cost-estimation UIs. Defaults to 2 when absent and `distinguishesTruncation: true`."
+ }
+ },
+ "description": "Host self-attestation for RFC 0033 envelope-completion contract."
+ }
}
}
}
}
§F — Error codes
Two new error codes for the terminal failure paths:
| Code | When |
|---|---|
envelope_truncation_unrecoverable | Truncation-retry budget exhausted while failure mode remained truncation. Pairs with envelope.retry.exhausted { finalReason: "truncation" } and cap.breached { kind: "schema" }. |
envelope_refusal | Provider returned an explicit refusal; the host (per §D) did not retry. Pairs with envelope.refusal. Renamed from envelope_refusal per the 2026-05-21 amendment to mirror the envelope.refusal RunEvent type name. |
envelope_invalid | Schema-violation-retry-exhaustion (or single emission failed payload validation with retries disabled). Pairs with envelope.retry.exhausted { finalReason: "schema-violation" }. Renamed from envelope_invalid (RFC 0021) per the 2026-05-21 amendment for naming consistency with the other envelope-track codes (envelope_<short-failure-mode>). |
All three codes are documented in the canonical error-code table at spec/v1/rest-endpoints.md §"Common error codes." The rename from envelope_invalid → envelope_invalid and envelope_refusal → envelope_refusal lands as the same 2026-05-21 amendment — see the Status history block at the end of this RFC. Compatibility: additive per COMPATIBILITY.md §2.1 because the codes were only canonized 2026-05-20 (Active commit a280371) and the rename predates any third-party host adoption of the longer-form names — MyndHyve, the first non-steward adopter, was already emitting the short-form names (envelope_refusal natively; envelope_schema_violation mapped to the new envelope_invalid).
§G — Production flow amendment
ai-envelope.md §"Production flow" gains a non-normative annotation showing the retry-routing branches:
LLM emission
│
▼
Parse JSON (lenient if host advertises recovery; §D)
│ ├── fail (terminal parse error) → emit envelope.retry.exhausted { finalReason: "parse-error" }; fail node
│ ├── recovered via lenient path → emit envelope.recovery.applied (RFC 0032 §B.6); continue
│ └── ok (direct or recovered)
▼
Stop reason inspection (RFC 0033 §A condition 1)
│ ├── truncation (max_tokens / length) → emit envelope.truncated (RFC 0032 §B.4)
│ │ ├── retry budget remains → §B truncation retry path (increased budget, NO corrective fragment)
│ │ │ → emit envelope.retry.attempted { reason: "truncation" } before next call
│ │ └── budget exhausted → emit envelope.retry.exhausted { finalReason: "truncation" } + cap.breached + fail node
│ └── clean stop (model self-determined completion)
▼
Shape validation
│ ├── fail → see schema-violation retry path §C
│ └── ok
▼
Kind / payload / contract / limits / redaction / trust / dedup / handler (per RFC 0021 §"Production flow")
│
├── provider refusal at any point → emit envelope.refusal; fail node with envelope_refusal (§D)
▼
Per-kind payload validation
│ ├── fail → §C schema-violation retry path (corrective fragment, NO budget increase)
│ │ → emit envelope.retry.attempted { reason: "schema-violation" } before next call
│ │ → budget exhausted: emit envelope.retry.exhausted { finalReason: "schema-violation" } + cap.breached + fail node
│ └── ok → accept envelope
§H — Trust boundary + SECURITY (carry-forward)
This RFC's normative additions ride entirely on RFC 0032's event surface; no new event types are introduced. The SECURITY invariants from RFC 0032 §G (envelope-refusal-no-prompt-leak, envelope-recovery-no-content-leak) apply unchanged. The corrective system fragment §C produces is host-authored from validator output, not from the model's previous output; redaction of the validator's failure description is RECOMMENDED but not mandated — validator output rarely contains secrets, but hosts MAY apply SR-1 redaction defensively.
Compatibility
Additive per COMPATIBILITY.md §2.1 + §4. All claims:
- Existing required fields: unchanged.
- Existing optional fields: unchanged.
- Existing event types: unchanged. This RFC introduces no new
RunEventTypeentries; it normates the _retry-routing semantics_ using RFC 0032's existing event vocabulary. - Existing endpoints: unchanged.
- Existing MUST requirements: not relaxed. RFC 0033 introduces NEW MUSTs in a previously-silent area (the retry-routing distinction was not normated in v1.1). Per
COMPATIBILITY.md §4row "New normative requirement on a previously-undefined behavior" — additive. - Existing error codes: unchanged. Two NEW error codes (
envelope_truncation_unrecoverable,envelope_refusal) added; existing codes remain.
Hosts that don't advertise capabilities.envelopes.reliability.completion.distinguishesTruncation: true retain their legacy retry-routing behavior; conformance scenarios for the distinction soft-skip. Hosts that DO advertise commit to the new MUSTs; conformance gates accordingly.
The new MUSTs are scoped to hosts that opt in via the capability flag, so v1.1 hosts that haven't migrated remain conformant against the suite version they pass. Soak window: one release cycle, matching the RFC 0026 / RFC 0027 / RFC 0032 precedent for additive capability-gated MUSTs.
Conformance
Two new scenarios under conformance/src/scenarios/. Both gated on capabilities.envelopes.reliability.supported: true AND capabilities.envelopes.reliability.completion.distinguishesTruncation: true AND the test seam.
envelope-completion-distinguishes-truncation.test.ts— drives two scenarios in sequence:
1. Truncation path. Mock LLM stops at max_tokens mid-envelope. Asserts: (a) envelope.truncated event fires; (b) envelope.retry.attempted { reason: "truncation" } fires on retry; (c) the host's retry call carries an INCREASED output budget (verified via the mock provider's lastReceivedMaxTokens test-introspection hook); (d) NO corrective schema fragment in the retry's system prompt. 2. Schema-violation path. Mock LLM returns a clean stop with malformed JSON (missing required field). Asserts: (a) NO envelope.truncated event; (b) envelope.retry.attempted { reason: "schema-violation" } fires on retry; (c) the host's retry call carries an UNCHANGED output budget; (d) a corrective schema fragment IS present in the retry's system prompt (verified via the mock provider's lastReceivedSystemPrompt hook).
envelope-truncation-cap-exhaustion.test.ts— drives a scenario where the mock LLM truncates every attempt up tomaxRetryAttempts + 1. Asserts: (a)envelope.retry.exhausted { finalReason: "truncation" }fires; (b)cap.breached { kind: "schema" }fires; (c) the node fails witherror.code: "envelope_truncation_unrecoverable"; (d) the run does NOT exceedmaxRetryAttemptstotal LLM calls (no infinite loop). This is the DoS-bound assertion.
The behaviorGate helper from RFC 0032 gains a predicate requireEnvelopeCompletionDistinguishes().
Alternatives considered
1. Make the truncation-vs-schema-violation distinction a SHOULD instead of a MUST. Rejected — the entire point of the RFC is the conformance-gateable distinction. SHOULD-tier semantics are too soft to assert; conformance suites have to fall back to "did the host retry at all?" assertions, which catches the wrong errors. MUST + capability-gating (host opts in, then must comply) is the right shape.
2. Drop the truncation-retry cap; rely on operator-side cost limits. Rejected — leaves a DoS opening on hosts that don't have operator-side wallet caps configured. The limits.schemaRounds reuse adds bounded behavior at the protocol level without introducing a new cap surface.
3. Mandate a specific truncation budget multiplier (2× exactly). Rejected — different model classes have different optimal multipliers (a model with a 4k context might need 4× to break out of a tight budget; a model with a 200k context can go from 1k → 2k → 4k → 8k → 16k more efficiently). The RFC RECOMMENDS 2× and exposes truncationBudgetMultiplier as informational advertisement; hosts pick what fits their model roster.
4. Introduce a new envelope.budget.doubled event for the budget-increase path. Rejected — RFC 0032's envelope.retry.attempted { reason: "truncation" } is sufficient (the reason field carries the routing decision). Adding a separate event would clutter the event vocabulary without new information.
5. Bundle this RFC into RFC 0032 (envelope-reliability events). Rejected — RFC 0032 ships the event vocabulary; RFC 0033 ships the retry semantics that depend on the events. Splitting them lets RFC 0032 land first (lower-risk additive event surface) and RFC 0033 follow once the event vocabulary is settled. Same staging logic as the proposal's PR 3 boundary (R4 + R5 together but conceptually separate; this RFC track honors the boundary as separate RFCs).
Unresolved questions
1. Schema-violation retry with model-self-corrective fragment vs spec-canonical fragment. §C says the corrective fragment is "host-implementation-defined." Should the spec offer a reference text the host MAY use, or normate a minimum fragment shape (e.g., must include the JSON Pointer to the failing field)? Recommendation: stay host-defined for v1.x; revisit if interop issues materialize.
2. Combined truncation + schema-violation diagnosis. §A says if both conditions fail in the same response, treat as truncation. Is there an edge case where a model emits truncation-clean stop with payload that's both syntactically valid AND validates partially (e.g., extra fields plus missing required field)? Recommendation: the existing pipeline handles this — partial validation success isn't a thing in JSON Schema; either the schema validates or it doesn't. No edge case in practice.
3. partialPayloadAvailable consumption. RFC 0032 §B.4 introduces envelope.truncated.partialPayloadAvailable: boolean. This RFC doesn't mandate any particular use of the partial payload. Should the spec offer a recovery path that uses the partial payload as input to the budget-doubled retry (e.g., "continue from where you left off")? Recommendation: defer — partial-payload continuation is a host-discretion strategy that adds complexity without obvious win. The simple budget-double-and-retry strategy is sufficient for v1.2.
4. Interaction with run-level retry policies. A run-level RunOptions.retryPolicy may also produce retries at the node level (separate from the envelope-level retries this RFC normates). Should the spec clarify the precedence — envelope-level retries fire first, run-level fires only if the envelope-level path exhausts? Recommendation: yes; document in spec/v1/run-options.md §"Retry policy" as a non-normative integration note. The envelope-level path is the inner retry loop; the run-level path is the outer.
5. Truncation in tool-using flows. When the LLM is mid-tool-call (function-calling) and truncates, the truncation might be in the tool-call arguments rather than the envelope payload. Is the truncation event still envelope.truncated, or something tool-call-specific? Recommendation: emit envelope.truncated for the _envelope's_ completion; tool-call argument truncation is a separate condition handled by the function-calling layer. The two surfaces don't conflict because envelope emission and tool-call argument emission are sequential, not simultaneous.
Implementation notes (non-normative)
- Reference host
apps/workflow-engine/backend/typescript:
- Extend executor/envelopeReliability.ts (introduced by RFC 0032) with the retry-routing branch: inspect stopReason, route to truncation-budget-double path or schema-violation-corrective-fragment path. - The budget-double helper lives in aiProviders/aiProvidersHost.ts dispatchPlain — extend the retry loop to accept a budgetMultiplier parameter (defaulting to 1 for schema-violation, 2 for truncation). - The corrective-fragment helper lives in a new executor/correctiveFragment.ts module that consumes Ajv2020 validator output and synthesizes a human-readable instruction. Reuses Ajv's existing error-formatting machinery.
- Mock-provider extensions for the two conformance scenarios: add
mockTruncationSequence: number[](per-attempt truncation behavior) andmockSchemaViolationSequence: boolean[](per-attempt malformed-JSON behavior). Both are extensions to the existing mock-provider config shape from RFC 0023. - Estimated total effort: schema + spec text + production-flow amendment ~1 day; reference-host retry-routing implementation ~1.5 days; two conformance scenarios ~1.5 days; CHANGELOG + INTEROP-MATRIX + observability.md prose ~30 min. Total ~4.5 days plus the standard Active window unless the bootstrap-phase waiver applies.
Acceptance criteria
Promotion from Active → Accepted:
- [ ]
spec/v1/ai-envelope.mdextended with §"Envelope-completion criteria" per §A; §"Production flow" annotated with the retry-routing branches per §G. - [ ]
spec/v1/observability.mdextended with §"Envelope-completion retry routing" cross-referencing RFC 0032's event family and this RFC's routing semantics. - [ ]
spec/v1/rest-endpoints.md§"Common error codes" gainsenvelope_truncation_unrecoverableandenvelope_refusalper §F. - [ ]
schemas/capabilities.schema.jsonenvelopes.reliabilityblock extended withcompletionper §E. - [ ] Two new conformance scenarios per §"Conformance" land in
@openwop/openwop-conformance; suite minor-version bumps. - [ ] CHANGELOG entry under
[Unreleased]. - [ ]
INTEROP-MATRIX.mdextended with a row forcapabilities.envelopes.reliability.completion.distinguishesTruncationalongside the RFC 0032 reliability rows. - [ ] Reference host (
apps/workflow-engine/backend/typescript) advertisescapabilities.envelopes.reliability.completion.distinguishesTruncation: truewithtruncationBudgetMultiplier: 2, implements the retry-routing distinction, passes both new conformance scenarios. - [ ] Spec gap E5 (
ai-envelope.md§"Open spec gaps") marked closed in a follow-up doc-prose pass. - [ ] First non-steward host advertises
capabilities.envelopes.reliability.completion.distinguishesTruncation: true(third-party validation gate per RFC 0001). MAY be waived under bootstrap-phase waiver.
References
RFCS/0021-ai-envelope-primitive.md— Production flow + Validation outcomes table this RFC normates the retry-routing path for.RFCS/0032-envelope-reliability-events.md— event vocabulary (envelope.truncated,envelope.retry.attempted,envelope.retry.exhausted,envelope.refusal,envelope.recovery.applied) this RFC's retry routing references; ships first per the track's PR sequencing.RFCS/0030-envelope-reasoning-and-tier-one-subset.md—reasoningfield consumes output tokens; relevant to truncation-budget calculations referenced in §B.RFCS/0031-envelope-variants-and-model-capabilities.md—model.capability.substitutedevent fires BEFORE this RFC's completion-criteria check; substitution doesn't reset the retry budget mid-emission.spec/v1/ai-envelope.md§"Open spec gaps" E5 — closed by this RFC's normative retry-routing semantics.spec/v1/capabilities.md§"Engine-enforced limits" —limits.schemaRoundsreused as the truncation-retry budget per §B.spec/v1/rest-endpoints.md§"Common error codes" — error-code table extended with the two new codes per §F.spec/v1/run-options.md§"Retry policy" — outer retry loop; non-normative integration note recommended per §"Unresolved questions" #4.- Tam et al., "Let Me Speak Freely?" — <https://arxiv.org/pdf/2408.02442> (informs the "truncation needs budget, not corrective fragment" recommendation: truncation typically reflects model running out of room mid-reasoning, not model emitting wrong shape).
Status history
Active → Accepted (2026-05-21)
Promoted to Accepted under the bootstrap-phase steward waiver per CONTRIBUTING.md §"Bootstrap-phase notes" + MAINTAINERS.md §"Bootstrap-phase RFC waivers". Zero external reviewers; all four acceptance criteria empirically met by Day 2.
Acceptance evidence:
1. Reference workflow-engine implementation. Host advertises capabilities.envelopes.reliability.completion.{distinguishesTruncation: true, truncationBudgetMultiplier: 2}. dispatchStructured()'s retry router routes truncation (doubled budget, no schema fragment) vs schema-violation (corrective fragment, unchanged budget) per §A/§B/§C; new error codes surface via AiProviderErrorCode union (commit 88beb31). 2. Conformance suite coverage. envelope-completion-distinguishes-truncation.test.ts (5 live) verifies the two retry paths diverge through lastReceivedMaxTokens introspection seam. envelope-truncation-cap-exhaustion.test.ts (4 live) verifies the §B DoS-bound + §F envelope_truncation_unrecoverable error code. envelope-refusal-shape.test.ts end-to-end (3 live) verifies §D refusal-MUST-NOT-retry + §F envelope_refusal error code. 3. Third-party host adoption. MyndHyve workflow-runtime advertises envelopes.reliability.completion.{distinguishesTruncation: true, truncationRetryMultiplier: 2} (truncationRetryMultiplier is a MyndHyve drift from spec's truncationBudgetMultiplier; 1-line rename tracked in adoption-feedback record). The three RFC 0033 §F error codes flow through RunSnapshot.error.code cleanly post-2026-05-21 rename: envelope_invalid + envelope_truncation_unrecoverable + envelope_refusal. 4. Adoption feedback folded. §B provider-ceiling guidance (when doubled budget exceeds provider per-call max, host MAY further reduce) + §F error-code rename (envelope_payload_invalid → envelope_invalid; envelope_refused_by_provider → envelope_refusal) — amendments 2026-05-21, commits 9da6281 + 8d3c1c0.
Compatibility: ratification is non-normative — no wire surface, schema, or behavior changes.
Active amendment (2026-05-21) — MyndHyve adoption feedback
Additive normative-text clarification per the filled adoption feedback at docs/handoffs/MYNDHYVE-RFC-0030-0033-ADOPTION-FEEDBACK-2026-05-20.md §A.4. No wire-shape change; no schema change.
- §B — Added provider-ceiling guidance for the truncation-budget multiplication path: when the doubled (or otherwise increased) output budget would exceed the active provider's per-call max, hosts MAY further reduce to the provider's ceiling AND continue the retry. Budget-clamped retries that ALSO fail with truncation SHOULD be treated as terminal (no further retries with the same ceiling). Surfaced by MyndHyve's default
max_tokens: 8192not yet hitting the ceiling (16384 retry budget is well under every Tier-1 vendor's per-call max) — the guidance preempts the failure mode at higher base budgets.
A separate forthcoming amendment (filed in commit alongside this one) renames §F error codes per the same adoption feedback round (envelope_invalid → envelope_invalid; envelope_refusal → envelope_refusal). See the commit immediately following for the rename rationale + reference-host + conformance updates.
Compatibility: additive per COMPATIBILITY.md §2.1. The §B clarification is operator guidance — hosts already implementing budget-clamping behavior remain compliant; the new text gives explicit normative cover for the pattern.
Draft → Active (2026-05-20)
Promoted under the bootstrap-phase steward waiver per the RFC 0021–0032 precedent. Spec text + wire-shape locked. Closes spec gap E5 from ai-envelope.md §"Open spec gaps." Conformance scenarios + reference-host implementation remain as the path to Accepted.
Evidence at promotion:
- Spec text:
- spec/v1/ai-envelope.md extended with §"Envelope-completion criteria" between §"Production flow" and §"Replay determinism". Carries the normative completion criteria (clean stop AND payload validates) + the four retry paths (truncation: increased budget, no corrective fragment; schema-violation: corrective fragment, no budget increase; refusal: terminal, no retry; recovery: internal to parsing, no retry consumed). Documents the priority rule when conditions 1 and 2 both fail (treat as truncation since output budget is the upstream cause). Cross-references the capability advertisement (capabilities.envelopes.reliability.completion — landed by the RFC 0032 batch) and the two new error codes. - spec/v1/observability.md extended with §"Envelope-completion retry routing (RFC 0033)" — landed in the RFC 0032 batch as part of the cross-track observability documentation. Summarizes the routing distinction; cross-references this RFC's §F error codes. - spec/v1/rest-endpoints.md §"Common error codes" gains two new codes per §F: envelope_truncation_unrecoverable (paired with envelope.retry.exhausted { finalReason: "truncation" } + cap.breached) and envelope_refusal (paired with envelope.refusal; MUST NOT echo refusal text in the error message per SECURITY invariant envelope-refusal-no-prompt-leak).
- Schemas additive (no MUST relaxed):
- schemas/capabilities.schema.json — envelopes.reliability.completion sub-block landed in the RFC 0032 batch (since 0033 depends on 0032's event vocabulary, the sub-block lives under the reliability parent). Required distinguishesTruncation: boolean; optional truncationBudgetMultiplier: 1..8 (default 2; informational for cost-estimation UIs). Hosts that don't advertise distinguishesTruncation: true retain legacy retry-routing behavior; conformance scenarios soft-skip. - schemas/run-event.schema.json — REUSES the six RFC 0032 envelope-reliability event types; this RFC introduces NO new event types. The retry-routing semantics layered on top of the existing event vocabulary. - schemas/run-event-payloads.schema.json — same; no schema changes (the reason/finalReason enum values truncation and schema-violation are already in the RFC 0032 enum).
Path to Active → Accepted: requires the reference workflow-engine to (a) advertise capabilities.envelopes.reliability.completion.distinguishesTruncation: true + truncationBudgetMultiplier: 2, (b) implement the truncation-vs-schema-violation retry-routing branch in executor/envelopeReliability.ts (or equivalent) — extending the retry loop to accept a budgetMultiplier parameter (defaulting to 1 for schema-violation, 2 for truncation) and the corrective-fragment helper to synthesize human-readable instructions from Ajv validator output, (c) ship the two conformance scenarios from §"Conformance" (envelope-completion-distinguishes-truncation; envelope-truncation-cap-exhaustion), and (d) mark spec gap E5 closed in a follow-up doc-prose pass to ai-envelope.md §"Open spec gaps." Alternatively, a non-steward host advertising the capability closes the third-party validation gate per the RFC 0021 / RFC 0026 precedent.