ADR-0049: Core Does Not Model Inbound Protocol Error Message Types; No create_inbound_error_followup_tree
Context and Problem Statement
The fuzzer node catalog (FUZZ-08a-bis, notes/bt-fuzzer-nodes-messaging.md)
maps the simulation node
vultron/bt/messaging/inbound/_behaviors/fuzzer.py:FollowUpOnErrorMessage to a
FUTURE: production factory function
vultron.core.behaviors.inbox.create_inbound_error_followup_tree, tracked by
Idea #1254 under Epic #1285 (FUZZ-D). The simulation node is a fallback_node
that, with p=0.5, emits a General Inquiry (EmitGI, MT.GI) follow-up to the
sender when an unexpected or error message type (RE/EE/CE/GE) is received —
modelling "A sends B something malformed; B follows up to ask what went wrong
and how it can help."
Planning #1254 required first checking whether that behavior has a production home. It does not, and the reason is structural, not an oversight:
-
Core cannot send the message this responds to. The simulation's error/inquiry message vocabulary (
MT.{RE, EE, CE, GE, GI, ...}invultron/bt/messaging/outbound/behaviors.py) does not exist in core. A sweep ofvultron/core/andvultron/semantic_registry/forGI/GE/CE/RE/EE/GeneralInquiry/error-message finds nothing. Core emits no error message types and registers no GI semantic. A production subtree that responds to an inbound error message would be responding to a message class core has no notion of — building the responder before the stimulus exists. -
The underlying intent is already served by an existing path. The real production expression of "flag a problem on a case and get a response back to the originator" is the note-to-case flow:
create_note_tree,create_add_note_to_case_received_tree, andadd_note_to_case_trigger_bt(vultron/core/behaviors/note/). A participant postsAdd(Note, target=Case); the Case Owner sees it and responds with anotherAdd(Note, ...)that propagates back to the originator via case ledger sync (SYNC-02-002), not a direct broadcast. Any communication outside that path (e.g. direct actor-to-actor troubleshooting) is out-of-band and out of scope by definition. -
FollowUpOnErrorMessagewas a fuzzer stub, not a modelled protocol exchange. Its own docstring calls it "a stub for following up on an error message" that emits stochastically "to simulate sending a follow-up inquiry." It models the uncertainty of a human analyst deciding to follow up — not a deterministic protocol obligation. There is no acknowledgment message type or negative-acknowledgment obligation in the protocol for it to port to.
The question is therefore: should core introduce inbound error message types
and a create_inbound_error_followup_tree factory to host this call-out point,
or should it record that this simulation node has no production analog?
This follows the pattern of the sibling issue #1253
(create_close_report_tree), which during implementation was found to be a
seam-only stub rather than a workflow (#1855) — the value was in documenting
the non-thing, not in building it.
Decision Drivers
- Core must not carry infrastructure for a stimulus it cannot produce (no RE/EE/CE/GE emit path, no GI semantic).
- BT-16-001: core tree builders must not depend on
vultron/demo/; a factory whose only backend is the stochastic fuzzer stub would violate the intent of the call-out abstraction (a real injection seam, not a tightly-coupled stub — BT-18-004). - The
FUTURE:catalog pointer will cause a future agent to re-plan this exact issue unless the decision is recorded durably. - Scope discipline: the legitimate open question (should core notify a sender when it cannot process their inbound?) is a distinct protocol question, not a port of this node.
Considered Options
-
Record that core does not model error message types; do not build the factory (chosen). Close #1254 with no production code. Flip the catalog's
FUTURE:pointer toRESOLVED:. Capture the sender-notification question as a separate Concern. -
Build
create_inbound_error_followup_treeas a seam-only stub anyway. Introduce a messaging call-out bundle and a factory exposing a Composer seam, unwired, defaulting to a DETERMINISTIC backend. Rejected: it manifests the simulation's structure (ReceiveMessagesBterror arm) that does not exist in core, adds a bundle and factory with no caller and no stimulus, and creates the appearance of a supported behavior that emits nothing. This is dead scaffolding that a later reader must reverse-engineer. -
Introduce error/GI message types into core and wire a full follow-up workflow. Rejected here: this is a large, speculative protocol expansion (a new message-type family plus dispatch, semantics, and use cases) that #1254 does not justify. If a sender-notification requirement is later established, it should be designed on its own merits, not reverse-derived from a fuzzer stub.
Decision Outcome
Chosen option: record the boundary; build nothing (Option 1).
- Core deliberately does not model inbound protocol error message types
(RE/EE/CE/GE) or the General Inquiry (GI) follow-up message. The simulation's
FollowUpOnErrorMessagehas no production analog andcreate_inbound_error_followup_treewill not be created. - The intent it approximated — surfacing and responding to a problem on a case
— is served by the existing note-to-case-via-ledger path
(
vultron/core/behaviors/note/, SYNC-02-002) and by out-of-band communication, which is out of scope. - Today, an inbound object core cannot resolve is dead-lettered
(
create_store_dead_letter_tree,UnresolvableObjectReceivedEvent) and a protocol-invalid payload yields anInboxOutcomeof"rejected"(vultron/core/behaviors/inbox/_process_payload.py). Neither notifies the sender — this is the status quo and is unchanged by this ADR.
Consequences
- Good, because core avoids scaffolding (a messaging call-out bundle, a
factory, a
ReceiveMessagesBt-shaped error arm) for a stimulus it cannot generate. - Good, because the
FUTURE:catalog pointer becomes a resolved decision, preventing re-planning of #1254. - Good, because the boundary "core does not model error message types" is now explicit for future protocol work.
- Neutral, because the deferred question (should core notify a sender on unprocessable inbound?) is preserved as a tracked Concern rather than silently dropped.
- Bad, because a reader expecting one-to-one fuzzer-node-to-factory coverage in
Epic #1285 will find a deliberate gap here; this ADR and the catalog
RESOLVED:note are the explanation.
Validation
- Grep sweep confirming no
RE/EE/CE/GE/GI/GeneralInquirymessage type orcreate_inbound_error_followup_treesymbol exists invultron/core/orvultron/semantic_registry/. notes/bt-fuzzer-nodes-messaging.mdFollowUpOnErrorMessageentry updated fromFUTURE:toRESOLVED:with a pointer to this ADR.- Idea #1254 closed with a resolution comment referencing this ADR.
More Information
- Source: Idea #1254 (reframed to a no-build decision), Epic #1285 (FUZZ-D).
- Precedent: #1253 / #1855 —
create_close_report_treesimilarly collapsed from a "workflow" to a documented non-workflow during planning. - Related open question (filed as a Concern): core silently dead-letters or
rejects unprocessable inbound with no sender notification. Whether the
protocol needs a negative-acknowledgment / error-reply facet is a separate
design question, not a port of
FollowUpOnErrorMessage. Discharged by ADR-0080 (2026-08-31), CONCERN-1880. That Concern is the Concern this ADR asked to be filed, and the deferral is now resolved with a recorded yes: an authenticated sender whose message fails after authentication receivesCreate(ProcessingFault)(ASK-07-001). This ADR's own decision is unchanged — core still does not model the RE/EE/CE/GE/GI message family, andcreate_inbound_error_followup_treeis still not created. One dedicated fault object type carried onCreateis not that family, and it was designed on its own merits (from the need to close an outstanding ask promptly rather than let it expire) rather than reverse-derived fromFollowUpOnErrorMessage, which is what this ADR required of any later design. - BT-18-004 (call-out points are injection seams via backend factories) and
BT-16-001 (core must not import from
vultron/demo/) informed the rejection of the seam-stub option. - Existing production analog:
vultron/core/behaviors/note/note-to-case flow; dead-letter pathvultron/core/behaviors/inbox/dead_letter_tree.py.