1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//! Integration tests asserting that registration derives actually populate the architecture
//! graph with the correct layer and role.
//!
//! These types are declared at module scope so their `inventory::submit!` expansions register
//! them globally; the tests then query `HexGraph::current()` (built by iterating the inventory
//! registry) and assert each component is present. This directly guards the PR-1 fixes:
//! HexDirective previously emitted a bare `inventory::submit!` that broke downstream builds,
//! and HexQuery emitted no submission at all so query components were invisible to the graph.
//!
//! It also guards the three 2026-09-04 fixes, all of which share one shape — a fact the code
//! had already computed, discarded in silence:
//!
//! 1. `HexDomain` declared `attributes(hex)` and never read it, so `#[hex(role = "…")]`
//! compiled clean and registered `Role::Entity`.
//! 2. A registration derive on a GENERIC type dropped the submission, leaving a type that
//! implemented `Registrable`, answered `node_info()`, and was in no graph. That is now a
//! compile error (`tests/ui/fail/generic_domain.rs`); the two remedies its message names
//! are pinned below, because an error naming a remedy that does not work is worse than no
//! error.
//! 3. `hexser::hex_register_component!` (and its `hex_register_domain!` … wrappers) emitted
//! the `Registrable` impl and NO submission, for every type — hexser's own advertised
//! non-derive registration path put nothing in the graph.
//!
//! Revision History
//! - 2026-09-04T00:00:00Z @AI: Add the HexDomain #[hex(role)] falsifier, the HexQuery role
//! override, the template-macro graph-presence falsifier, and replace the generic-derive
//! case (now a compile error) with the two remedies its message names.
//! - 2026-07-20T00:00:00Z @AI: Add graph-registration coverage for HexDomain/HexPort/HexAdapter/HexDirective/HexQuery and #[hex(role)] override.
use *;
// A domain layer is not made only of entities. Before 2026-09-04 this registered as
// `Role::Entity` and said nothing about it.
;
;
;
// HexQuery is the registration-only Application-layer derive (no trait impl of its own), so
// with the role override it is the vehicle for an Application component that is neither a
// directive nor a query.
;
// ── The generic arm ─────────────────────────────────────────────────────────────────────
// `#[derive(HexDomain)] struct RegTestGeneric<T>` is now a COMPILE ERROR — see
// `tests/ui/fail/generic_domain.rs`. The error names two remedies, and both are pinned here.
/// Remedy 1: a non-generic marker struct stands for the component in the graph.
;
/// The real generic type, deliberately underived.
/// Remedy 2: hand-write `Registrable` when a caller needs `node_info()` for a concrete
/// instantiation. A hand-written impl submits nothing, so this type is correctly absent from
/// the graph — implementing `Registrable` and being in the graph are two different facts.
// ── The non-derive registration path ────────────────────────────────────────────────────
// hexser's own `hex_register_*` macros are the no-macros/explicit door into the graph. They
// emitted the `Registrable` impl and no submission until 2026-09-04.
;
hex_register_domain!;
/// Locate a registered node whose `type_name` ends with the given short name.
/// why: HexDomain must register its type in the graph at the Domain layer with role Entity;
/// guards the shared registration codegen for the domain derive.
/// why: the `#[hex(role = "ValueObject")]` override must be parsed and applied by HexDomain,
/// not ignored. The derive declared `attributes(hex)` and never called `role_override`, so a
/// value object marked ValueObject compiled clean and registered as an Entity — a graph that
/// lies is worse than no graph. Flip: drop `role_override` from `hex_domain.rs` and this
/// returns `Role::Entity`.
/// why: HexPort must register with the default role Repository when no #[hex(role)] is given.
/// why: the #[hex(role = "InputPort")] override must be parsed and applied instead of the
/// hardcoded Repository default (M16 — roles were previously not overridable).
/// why: HexAdapter must register at the Adapter layer with role Adapter.
/// why: HexDirective must register in the graph (role Directive). Previously its bare
/// `inventory::submit!` path meant the submission did not resolve for downstream crates;
/// this asserts the component actually lands in the graph.
/// why: HexQuery must register in the graph (role Query). Previously HexQuery emitted no
/// inventory submission, so query components were silently absent (M13).
/// why: HexQuery must honour `#[hex(role = "…")]` like HexPort/HexAdapter do, so an
/// Application-layer component that is neither a directive nor a query can be registered
/// with a derive instead of a hand-rolled macro. Flip: drop `role_override` from `query.rs`
/// and this returns `Role::Query`.
/// why: the generic derive's compile error names a marker struct as the remedy, so the marker
/// must actually land in the graph — an error pointing at a remedy that does not work is a
/// second defect, not a fix.
/// why: the other remedy the error names — hand-writing `Registrable` on the generic — must
/// still compile and answer node_info() for a concrete instantiation.
/// why: a hand-written `Registrable` submits nothing, so the generic must NOT be in the graph.
/// This is the fact the generic derive used to produce in silence, now asserted deliberately:
/// implementing `Registrable` and being in the graph are two different columns.
/// why: `hexser::hex_register_domain!` is the crate's advertised non-derive registration door.
/// It emitted the `Registrable` impl and NO `inventory::submit!`, for every type — so a
/// consumer following hexser's own template got a component that answered `node_info()` and
/// was in no graph. Flip: delete the `inventory::submit!` from `hex_register_component!` and
/// this returns `None` while `node_info()` keeps answering correctly.