yarramate 1.14.0 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ # Architecture states
2
+
3
+ Architecture states are optional planning contexts for comparing a baseline,
4
+ one or more transitions, and a target without copying the native model or
5
+ overloading operational lifecycle.
6
+
7
+ ## Authoring
8
+
9
+ A native document may declare states:
10
+
11
+ ```yaml
12
+ states:
13
+ - id: baseline
14
+ kind: baseline
15
+ name: Current architecture
16
+
17
+ - id: migration
18
+ kind: transition
19
+ name: Migration plateau
20
+ after: baseline
21
+
22
+ - id: target
23
+ kind: target
24
+ name: Target architecture
25
+ after: migration
26
+ ```
27
+
28
+ `kind` is one of `baseline`, `transition`, or `target`. `after` is an optional
29
+ local or globally qualified architecture-state reference. Ordering must be
30
+ acyclic. Core does not require one state of each kind, require a linear plan,
31
+ or interpret ordering as approval or execution progress.
32
+
33
+ Concepts and relationships may declare where they are present:
34
+
35
+ ```yaml
36
+ concepts:
37
+ - id: shared-service
38
+ kind: applicationComponent
39
+ name: Shared service
40
+
41
+ - id: legacy-service
42
+ kind: applicationComponent
43
+ name: Legacy service
44
+ presentIn: [baseline, migration]
45
+
46
+ - id: modern-service
47
+ kind: applicationComponent
48
+ name: Modern service
49
+ presentIn: [migration, target]
50
+ ```
51
+
52
+ An unscoped concept is present in every declared architecture state. An
53
+ unscoped relationship is present wherever both endpoint concepts are present.
54
+ An explicit relationship scope may narrow that inferred presence, but cannot
55
+ place the relationship in a state where either endpoint is absent. Authors
56
+ therefore annotate only subjects that vary.
57
+
58
+ Architecture states and lifecycle status are orthogonal. `planned`, `current`,
59
+ and `retired` remain operational lifecycle claims; baseline, transition, and
60
+ target are planning contexts.
61
+
62
+ ## Graph v2 claims
63
+
64
+ Each state compiles as a globally qualified concept with Core `plateau` kind,
65
+ its authored name, `yarramate/state/type`, and optional
66
+ `yarramate/state/after` claim. `presentIn` entries compile to
67
+ `yarramate/state/present-in` reference claims.
68
+
69
+ Presence claim IDs encode the globally qualified state identity rather than a
70
+ file path or list position, so reordering authoring does not change identity.
71
+ The feature uses the existing graph-v2 subject and claim shape; it does not
72
+ introduce graph v3.
73
+
74
+ ## Projection
75
+
76
+ The `states` projection selector accepts globally qualified state identities:
77
+
78
+ ```yaml
79
+ query:
80
+ states:
81
+ - roadmap#target
82
+ relationships: between
83
+ ```
84
+
85
+ State selectors combine with other projection filters using logical AND.
86
+ Several state values combine with logical OR. State concepts themselves are
87
+ not included in a state-filtered result. Like other projection selectors, an
88
+ unavailable state is portable and produces no matches.
89
+
90
+ ## Comparison
91
+
92
+ The typed `compareArchitectureStates(graph, from, to)` API and CLI classify
93
+ subjects as added, removed, or retained:
94
+
95
+ ```sh
96
+ yarramate ask .yarramate/workspace.yaml \
97
+ --compare roadmap#baseline roadmap#target
98
+ ```
99
+
100
+ ## Optional LikeC4 views
101
+
102
+ A normal state projection can be exported without any new native syntax:
103
+
104
+ ```sh
105
+ yarramate-likec4 export-project \
106
+ .yarramate/projections/state-engine-target.yaml \
107
+ .yarramate/integrations/likec4/subject-mapping.yaml \
108
+ .yarramate-out/state-target \
109
+ --kinds .yarramate/integrations/likec4/kind-mapping.yaml \
110
+ .yarramate/workspace.yaml
111
+ ```
112
+
113
+ For a comparison view, the projection selects the union of both states and the
114
+ adapter receives their ordered comparison:
115
+
116
+ ```sh
117
+ yarramate-likec4 export-project \
118
+ .yarramate/projections/state-engine-change.yaml \
119
+ .yarramate/integrations/likec4/subject-mapping.yaml \
120
+ .yarramate-out/state-change \
121
+ --compare yarramate-evolution#adapter-foundation \
122
+ yarramate-evolution#state-foundation \
123
+ --kinds .yarramate/integrations/likec4/kind-mapping.yaml \
124
+ .yarramate/workspace.yaml
125
+ ```
126
+
127
+ The adapter derives `yarramateChange` metadata and local concept styles from
128
+ Core's comparison result. Both states must appear in `query.states`; otherwise
129
+ the adapter reports `YMLC106`. An unknown compared state reports `YMLC105` at
130
+ its portable state selector. These are adapter correctness checks, not
131
+ architecture completeness or approval rules.
132
+
133
+ Colors and borders remain disposable LikeC4 presentation. Native documents,
134
+ graph v2, and the state-comparison result contain no renderer styling.
135
+
136
+ The comparison result conforms to
137
+ `schema/yarramate-state-comparison.schema.json` and uses format
138
+ `yarramate/state-comparison/v1`; `yarramate ask --compare --json` nests it
139
+ under a `comparison` key inside the `yarramate/ask-result/v1` envelope.
140
+ The result is a deterministic structural comparison, not an assessment,
141
+ migration plan, approval, or completeness claim.
142
+
143
+ ## Deliberate boundary
144
+
145
+ This foundation does not allow names, kinds, owners, lifecycle values, or
146
+ other claim values to vary by state. Supporting state-scoped claims would
147
+ change claim interpretation and requires a separate graph-version decision.
148
+ It also does not implement a planning workflow, dates, work packages,
149
+ automatic transition derivation, or an external framework metamodel.
@@ -0,0 +1,429 @@
1
+ # Evidence overlays
2
+
3
+ Evidence overlays evaluate existing native architecture subjects and claims
4
+ without changing canonical documents or graph v2. Their normative structure is
5
+ `schema/yarramate-evidence.schema.json`.
6
+
7
+ ```yaml
8
+ format: yarramate/evidence/v1
9
+ id: repository-observation
10
+ version: "1.0"
11
+ provider: repository-audit
12
+ observations:
13
+ - subject: yarramate-engine#compiler
14
+ result: confirmed
15
+ evidence:
16
+ uri: repo:src/compiler.ts
17
+ - claim: yarramate-repository#compiler-source-realizes-compiler
18
+ result: contradicted
19
+ evidence:
20
+ uri: repo:src/compiler.ts
21
+ message: Expected implementation marker was not found
22
+ ```
23
+
24
+ Each observation targets exactly one globally qualified graph subject or
25
+ stable claim ID. It has one controlled result:
26
+
27
+ - `confirmed` — the provider observed evidence supporting the target;
28
+ - `contradicted` — the provider observed evidence conflicting with the target;
29
+ - `unknown` — the provider cannot determine a result;
30
+ - `not-observed` — the provider looked but found no relevant observation.
31
+
32
+ These results are facts reported by the named provider. A contradiction does
33
+ not by itself fail Core validation, and confirmation is not approval.
34
+
35
+ The `evidence.uri` value is opaque to YarraMate Core. Its provider owns URI
36
+ resolution and external validity. An optional non-empty message may explain
37
+ the observation; arbitrary provider metadata is not accepted. One syntactic
38
+ reading is the exception (ADR 0130): artifact coverage, below, compares
39
+ `repo:<path>` locators against a declared scope as strings — still without
40
+ resolving, opening, or validating anything a locator points at.
41
+
42
+ ## Value observations
43
+
44
+ An observation may additionally report the value it read, by carrying an
45
+ observed `key` and `value` together (ADR 0075):
46
+
47
+ ```yaml
48
+ observations:
49
+ - subject: shop#customer-data
50
+ result: confirmed
51
+ key: region
52
+ value: us-east-1
53
+ evidence:
54
+ uri: repo:infra/main.tf#L12
55
+ message: aws_s3_bucket.customer_data region
56
+ ```
57
+
58
+ The two fields are required together: a key without a value, or a value
59
+ without a key, is rejected. The key is provider-owned naming with no
60
+ whitespace; the value is any non-empty string, compared verbatim.
61
+
62
+ `key` and `value` do not replace or reinterpret `result`. The result still
63
+ answers whether the target holds up at all, which is a presence or absence
64
+ judgment; the keyed value reports a fact that a declared expectation can be
65
+ compared against. A provider that reads several facts in one place may report
66
+ several keys at one target, and each key at a target at most once. Its
67
+ presence result is still stated once per target.
68
+
69
+ Providers own their key vocabulary. Core neither defines key names nor
70
+ resolves them: it only compares the reported value with the value a
71
+ constraint declared it expects.
72
+
73
+ ## Evaluation
74
+
75
+ Generic evaluation checks:
76
+
77
+ - every subject target exists in graph v2;
78
+ - every claim target exists in graph v2;
79
+ - one evidence document evaluates a target at most once;
80
+ - versioned evidence document identities are unique in one workspace.
81
+
82
+ Successful evaluation produces deterministic
83
+ `yarramate/evidence-report/v1` JSON with counts and sorted observations. Its
84
+ normative structure is `schema/yarramate-evidence-report.schema.json`. The
85
+ input semantic graph is not modified.
86
+
87
+ No command emits the per-document report directly: it is a library-level
88
+ format. Workspace evidence declared in the manifest is evaluated by
89
+ `yarramate reconcile` — the aggregated reconciliation report below — and
90
+ gated by `yarramate check --strict`. Structural or reference correctness
91
+ failures emit `yarramate/diagnostic-result/v1` with exit status `1`; status
92
+ `2` means invocation or file access failed.
93
+
94
+ The typed API exposes `loadEvidence`, `evaluateEvidence`, and
95
+ `evaluateEvidenceWorkspace`. Evidence documents may be declared in the
96
+ optional `evidence` category of a workspace manifest, in which case
97
+ `yarramate check` validates them against the compiled graph.
98
+
99
+ `yarramate check --strict` additionally fails the check (exit `1`) when any
100
+ evidence observation is `contradicted`, rendering each contradiction as a
101
+ source-located `YM901` diagnostic anchored at the declared claim. A
102
+ contradicted expectation surfaces the same way, anchored at the authored
103
+ expected value, with no gate semantics of its own. `unknown`
104
+ and `not-observed` results stay advisory. A strict pass reports how many
105
+ observations it evaluated, so a gate over zero evidence is visible rather
106
+ than silently vacuous (ADR 0047).
107
+
108
+ ## Reconciliation
109
+
110
+ ```sh
111
+ yarramate reconcile .yarramate/workspace.yaml
112
+ ```
113
+
114
+ This command evaluates every evidence overlay declared by the manifest and
115
+ emits deterministic `yarramate/reconciliation-report/v1` JSON. Its normative
116
+ schema is `schema/yarramate-reconciliation-report.schema.json`.
117
+
118
+ The summary counts all observations. The `findings` array contains only
119
+ `contradicted`, `unknown`, and `not-observed` results, ordered by target and
120
+ provider, plus the `stale-attestation` and `unconfirmed-attestation`
121
+ findings described below. Confirmed observations remain summarized rather
122
+ than repeated so a reviewer or agent can focus on unresolved evidence.
123
+
124
+ When a finding targets the primary claim of a declared relationship, it also
125
+ carries an optional `asserted` object with the declared `from`, `to`, and
126
+ `kind` (and `name` when present), so the disagreement between the model and
127
+ the evidence is visible in the finding itself:
128
+
129
+ ```json
130
+ {
131
+ "target": { "type": "claim", "id": "payments#payment-api-writes-ledger" },
132
+ "asserted": {
133
+ "from": "payments#payment-api",
134
+ "to": "payments#ledger",
135
+ "kind": "yarramate/core@0.1#access",
136
+ "name": "Records payments"
137
+ },
138
+ "result": "contradicted",
139
+ "provider": "repository-inspection",
140
+ "evidenceDocument": "payments-repository@1.0",
141
+ "evidence": {
142
+ "uri": "repo:src/payments.ts",
143
+ "message": "Payment API writes to the billing store, not the ledger"
144
+ }
145
+ }
146
+ ```
147
+
148
+ Subject-targeted findings and findings on relationship sub-claims (such as
149
+ `…~name`) do not carry `asserted`.
150
+
151
+ ### Declared expectations
152
+
153
+ When a constraint declares an expected observation
154
+ (`expects` in `docs/NATIVE-DOCUMENT.md`), reconciliation compares the declared
155
+ value with what the named provider observed. A declared expectation is matched
156
+ to observations by provider and key. The observation's own target anchors its
157
+ provenance but does not narrow the match, because a keyed value is a fact
158
+ about the project rather than about one subject, and several constraints may
159
+ legitimately expect the same fact.
160
+
161
+ A disagreement is an ordinary `contradicted` finding, rendered with both
162
+ sides: the declared expectation with the source location where it was
163
+ authored, and the observed value with its provider, evidence document, and
164
+ locator.
165
+
166
+ ```json
167
+ {
168
+ "target": {
169
+ "type": "claim",
170
+ "id": "shop#customer-data~expects-residency"
171
+ },
172
+ "expectation": {
173
+ "provider": "terraform-scan",
174
+ "key": "region",
175
+ "expected": "ap-southeast-2",
176
+ "observed": "us-east-1",
177
+ "declared": {
178
+ "document": "shop",
179
+ "path": "architecture/shop.yaml",
180
+ "pointer": "/concepts/1/constraints/0/expects/value",
181
+ "line": 18,
182
+ "column": 18
183
+ }
184
+ },
185
+ "result": "contradicted",
186
+ "provider": "terraform-scan",
187
+ "evidenceDocument": "shop-terraform@1.0",
188
+ "evidence": {
189
+ "uri": "repo:infra/main.tf#L12",
190
+ "message": "aws_s3_bucket.customer_data region"
191
+ }
192
+ }
193
+ ```
194
+
195
+ The summary counts `expectationsCompared`, the declared expectations a
196
+ matching observation reached, and `expectationsWithoutObservation`, those no
197
+ provider reported on. An expectation nobody observed is listed in a top-level
198
+ `unobservedExpectations` array, sorted by claim then key, and never converted
199
+ into a finding: no provider disagreed, so there is nothing to accuse, and the
200
+ same discipline applies as for unobserved subjects (ADR 0049). An unobserved
201
+ expectation is therefore reported honestly rather than passing as satisfied,
202
+ and it does not fail `check --strict`.
203
+
204
+ The summary also counts `current` concepts that appear in no observation at
205
+ all — neither targeted directly, nor through a claim they own, nor as an
206
+ endpoint of an observed relationship claim — as `subjectsWithoutEvidence`.
207
+ When the count is positive the report lists them in a top-level
208
+ `unobservedSubjects` array, sorted lexicographically. This is not a finding:
209
+ no provider looked and disagreed; reconciliation simply has no opinion, and
210
+ the report says so instead of letting the gap pass as verified.
211
+
212
+ A finding is advisory evidence, not a proposed replacement claim, validation
213
+ error, CI verdict, or authorization to modify the native model.
214
+
215
+ ## Artifact coverage
216
+
217
+ Unobserved subjects answer only half of the coverage question: they report
218
+ declared intent no observation supports, and nothing reported code the model
219
+ never mentions at all (#175, ADR 0130). A workspace manifest may therefore
220
+ declare a `coverage` list of glob patterns naming the artifacts the model
221
+ intends to cover:
222
+
223
+ ```yaml
224
+ coverage:
225
+ - src/**/*.ts
226
+ - schema/*.json
227
+ ```
228
+
229
+ `reconcile` — only `reconcile` — resolves the patterns against the root of
230
+ the git repository the manifest lives in. An artifact is any selected file
231
+ git can see there: tracked, or untracked and not ignored. An observation
232
+ claims an artifact when its locator is `repo:<path>`, with any `#fragment`
233
+ stripped; a locator naming a directory claims everything beneath it; a
234
+ locator in any other scheme claims nothing. The summary counts
235
+ `artifactsInScope` and `unclaimedArtifacts` exactly when coverage was
236
+ assessed, and a positive count lists the paths in a top-level
237
+ `unclaimedArtifacts` array, sorted, beside a `coverageScope` echo of the
238
+ declared patterns.
239
+
240
+ When coverage was not assessed — no scope declared, or no git repository —
241
+ the report says why in `notes` rather than staying silent, and a declared
242
+ pattern that selects no artifact gets a note naming it: a dead glob is
243
+ indistinguishable from a typo. An unclaimed artifact is absence, never
244
+ accusation: no finding is fabricated, and `check --strict` does not read
245
+ the list — the same line unobserved subjects and unobserved expectations
246
+ hold.
247
+
248
+ ## Stale attestations
249
+
250
+ An attestation records that an accountable human accepted a subject *as
251
+ stated on a date*. `reconcile` reports when that sign-off no longer covers
252
+ the current wording (ADR 0074). Using git, it compares the attestation's
253
+ `on` date against the commits that touched the attested subject's `name`
254
+ and `description` spans; if the wording changed later, it emits a
255
+ `stale-attestation` finding:
256
+
257
+ ```json
258
+ {
259
+ "target": { "type": "subject", "id": "policy#refund-rule" },
260
+ "result": "stale-attestation",
261
+ "attestation": {
262
+ "topic": "signed-off",
263
+ "by": "policy#compliance-lead",
264
+ "on": "2026-01-15"
265
+ },
266
+ "provider": "git",
267
+ "changedAt": "2026-06-01T12:00:00+00:00",
268
+ "evidence": {
269
+ "uri": "git:9f2c1ab…",
270
+ "message": "Attestation \"signed-off\" by policy#compliance-lead on 2026-01-15 predates the current wording of policy#refund-rule: the description changed in commit 9f2c1ab on 2026-06-01T12:00:00+00:00."
271
+ }
272
+ }
273
+ ```
274
+
275
+ This finding kind has no `evidenceDocument`: no provider authored it, so
276
+ its provider is `git`. When the change is only in the working tree the
277
+ locator is `git:worktree` and `changedAt` is absent.
278
+
279
+ The comparison rule is exact. A sign-off dated `on` covers every commit up
280
+ to and including the end of that calendar day in UTC; a change counts as
281
+ later only when its committer timestamp is at or past midnight UTC of the
282
+ following day. Only the `name` and `description` spans are compared in v1,
283
+ so marking a subject `current` or adding a reference does not reopen a
284
+ sign-off.
285
+
286
+ When git cannot answer honestly the report says so instead of guessing.
287
+ Outside a git repository, on a shallow clone, against an untracked
288
+ document, or when the sign-off predates the file's earliest commit, no
289
+ staleness finding is emitted and a note is added to a top-level `notes`
290
+ array:
291
+
292
+ ```json
293
+ "notes": [
294
+ "Attestation staleness was not assessed: the workspace is not inside a git repository."
295
+ ]
296
+ ```
297
+
298
+ The `summary.staleAttestations` counter is present exactly when staleness
299
+ was assessed, so a report that never looked is distinguishable from one
300
+ that looked and found nothing. `check --strict` is unaffected: staleness
301
+ is a freshness signal about a human process, not a contradiction between
302
+ the model and observed reality, so it reports rather than gates.
303
+
304
+ ## Unconfirmed attestations
305
+
306
+ `by` names the authority a judgment belongs to, resolved against the
307
+ model as a subject reference; `recordedBy` names whoever actually
308
+ wrote the record, when that is not the authority's own hand (ADR
309
+ 0082). `reconcile` reports when the two disagree — a recorder is
310
+ present and names someone other than the authority itself — as an
311
+ `unconfirmed-attestation` finding:
312
+
313
+ ```json
314
+ {
315
+ "target": { "type": "subject", "id": "policy#refund-rule" },
316
+ "result": "unconfirmed-attestation",
317
+ "attestation": {
318
+ "topic": "signed-off",
319
+ "by": "policy#compliance-lead",
320
+ "recordedBy": "yarramate-apply-agent",
321
+ "on": "2026-01-15"
322
+ },
323
+ "provider": "model",
324
+ "declared": {
325
+ "document": "policy",
326
+ "path": "architecture/policy.yaml",
327
+ "pointer": "/concepts/1/attestations/0/topic",
328
+ "line": 24,
329
+ "column": 16
330
+ }
331
+ }
332
+ ```
333
+
334
+ This finding kind has no `evidenceDocument` and involves no git
335
+ lookup: its provider is `model`, because the disagreement is legible
336
+ from the attestation claim alone. `declared` locates the authored
337
+ attestation entry — the same source location the claim carries, its
338
+ `topic` line — rather than an external observation. A recorder that names
339
+ the authority itself — by its qualified id or its document-local
340
+ form — is a self-recorded sign-off and produces no finding.
341
+
342
+ The `summary.unconfirmedAttestations` counter is present whenever a
343
+ graph was compiled, so a report that found nothing unconfirmed is
344
+ distinguishable from one that never had a graph to check against.
345
+ `check --strict` is unaffected: an unconfirmed recorder is a fact
346
+ about who wrote a record, not a contradiction between the model and
347
+ observed reality, so it reports rather than gates.
348
+
349
+ ## Writing overlays
350
+
351
+ An overlay is a workspace document, so `yarramate apply` writes it the way
352
+ it writes a model document: `add-observation`, `update-observation`, and
353
+ `delete-observation` address an evidence document declared by the manifest's
354
+ `evidence` list (ADR 0089). Before this the only way to record what a
355
+ provider read was to open the file, which is exactly the hand-editing the
356
+ apply loop exists to prevent — a reviewer who adds a concept through the
357
+ visual canvas or a batch has no path to the evidence for it.
358
+
359
+ ```yaml
360
+ format: yarramate/operations/v1
361
+ operations:
362
+ - op: add-observation
363
+ document: .yarramate/evidence/repository.yaml
364
+ observation:
365
+ subject: yarramate-engine#compiler
366
+ result: confirmed
367
+ evidence:
368
+ uri: repo:src/compiler.ts
369
+ ```
370
+
371
+ An observation is addressed by the pair (target, key) rather than by an
372
+ `id`, because an overlay entry has none: `reconcile` already treats that
373
+ pair as unique per document (ADR 0075, `YM803`). The target is the
374
+ observation's `subject` or `claim`; a keyless observation is the presence
375
+ claim for its target, so an absent `key` is itself an address rather than a
376
+ wildcard. `update-observation` names the entry the same way and changes
377
+ whatever else it carries — `result`, `value`, `evidence.uri`,
378
+ `evidence.message` — scalars replacing in place. Retraction is explicit and
379
+ narrow: `remove: [message]` is the only retraction an observation admits,
380
+ because `message` is the only optional field it holds, and setting and
381
+ removing it in one operation is rejected rather than ordered.
382
+ `delete-observation` removes the whole entry.
383
+
384
+ The gate is the one every batch passes. The candidate workspace compiles,
385
+ then every touched overlay is loaded and evaluated against the compiled
386
+ graph, before a single byte is written: an observation whose subject the
387
+ graph does not carry rejects the batch with `YM801` and leaves every source
388
+ unchanged. `yarramate/apply-result/v1` counts the work as
389
+ `addedObservations`, `updatedObservations`, and `deletedObservations`
390
+ alongside the concept and relationship counts.
391
+
392
+ ## Boundary
393
+
394
+ Evidence overlays do not:
395
+
396
+ - introduce independent observed claims;
397
+ - mutate graph v2 or native documents;
398
+ - define completeness or CI failure policy;
399
+ - grant approval or governance status;
400
+ - require Graphify or another provider;
401
+ - interpret provider-specific URIs.
402
+
403
+ A later opt-in policy layer may decide how a report affects CI. Independent
404
+ observed claims would require a separate semantic contract rather than an
405
+ unstructured extension of this format.
406
+
407
+ Constraint satisfaction uses this same boundary. A provider may target a
408
+ stable claim such as
409
+ `yarramate-engine#compiler~constraint-tool-neutral`; its result is a
410
+ constraint assessment, not Core conformance. Core does not execute policy
411
+ rules, interpret missing observations, manage exceptions, or convert a result
412
+ into CI failure.
413
+
414
+ A declared expectation does not change that boundary. Comparing two strings is
415
+ not a rule engine: Core still holds no policy language, no waivers, no
416
+ exceptions, and no opinion about which value is correct. It reports that the
417
+ model and a provider disagree, and leaves the judgment to a reviewer.
418
+
419
+ Diagnostics use `YM801` for an unknown subject, `YM802` for an unknown claim,
420
+ `YM803` for a duplicate target, and `YM804` for a duplicate evidence document.
421
+ The schemas are exported as `yarramate/schema/evidence` and
422
+ `yarramate/schema/evidence-report`. The reconciliation schema is exported as
423
+ `yarramate/schema/reconciliation-report`.
424
+
425
+ Evidence locators also answer the inverse question. `ask --where`
426
+ (ADR 0068) reads the same observations as verified code locations for
427
+ matched subjects — so every observation authored for reconciliation
428
+ doubles as a pointer, and every subject a `--where` answer lists as
429
+ unobserved is a nudge toward the missing observation.