@gmickel/gno 1.18.0 → 1.20.0

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.
Files changed (153) hide show
  1. package/README.md +14 -7
  2. package/assets/skill/SKILL.md +54 -12
  3. package/assets/skill/mcp-reference.md +7 -2
  4. package/assets/skill/recipes/citation-and-provenance.md +32 -9
  5. package/package.json +2 -1
  6. package/spec/AGENTS.md +83 -0
  7. package/spec/CLAUDE.md +83 -0
  8. package/spec/bench-fixture.schema.json +137 -0
  9. package/spec/cli.md +2919 -0
  10. package/spec/db/schema.sql +442 -0
  11. package/spec/evals-agentic.md +592 -0
  12. package/spec/evals.md +1106 -0
  13. package/spec/mcp.md +2279 -0
  14. package/spec/output-schemas/activation-verification.schema.json +515 -0
  15. package/spec/output-schemas/ask.schema.json +564 -0
  16. package/spec/output-schemas/backlinks.schema.json +131 -0
  17. package/spec/output-schemas/bench-result.schema.json +120 -0
  18. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  19. package/spec/output-schemas/claim-verification.schema.json +291 -0
  20. package/spec/output-schemas/collection-list.schema.json +45 -0
  21. package/spec/output-schemas/context-capsule-v1.schema.json +726 -0
  22. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  23. package/spec/output-schemas/context-list.schema.json +21 -0
  24. package/spec/output-schemas/doctor.schema.json +313 -0
  25. package/spec/output-schemas/error.schema.json +30 -0
  26. package/spec/output-schemas/expansion.schema.json +37 -0
  27. package/spec/output-schemas/get.schema.json +140 -0
  28. package/spec/output-schemas/graph-query.schema.json +99 -0
  29. package/spec/output-schemas/graph.schema.json +371 -0
  30. package/spec/output-schemas/links-list.schema.json +186 -0
  31. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  32. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  33. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  34. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  35. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  36. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  37. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  38. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  39. package/spec/output-schemas/models-list.schema.json +93 -0
  40. package/spec/output-schemas/multi-get.schema.json +103 -0
  41. package/spec/output-schemas/process-status.schema.json +119 -0
  42. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  43. package/spec/output-schemas/resident-status.schema.json +154 -0
  44. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  45. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  46. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  47. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  48. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  49. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  50. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  51. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  52. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  53. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  54. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  55. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  56. package/spec/output-schemas/search-result.schema.json +154 -0
  57. package/spec/output-schemas/search-results.schema.json +338 -0
  58. package/spec/output-schemas/similar.schema.json +84 -0
  59. package/spec/output-schemas/status.schema.json +676 -0
  60. package/spec/output-schemas/tags-list.schema.json +48 -0
  61. package/src/app/context-runtime-contract.ts +10 -5
  62. package/src/app/context-runtime-input.ts +29 -1
  63. package/src/app/context-runtime-types.ts +7 -0
  64. package/src/app/context-runtime.ts +20 -2
  65. package/src/app/context-surface.ts +4 -0
  66. package/src/app/verified-ask.ts +291 -0
  67. package/src/cli/commands/ask-format.ts +255 -0
  68. package/src/cli/commands/ask.ts +144 -183
  69. package/src/cli/commands/context-build.ts +56 -9
  70. package/src/cli/commands/get.ts +64 -3
  71. package/src/cli/commands/query.ts +62 -23
  72. package/src/cli/commands/replay.ts +140 -0
  73. package/src/cli/commands/search.ts +48 -3
  74. package/src/cli/commands/shared.ts +3 -1
  75. package/src/cli/commands/trace.ts +200 -0
  76. package/src/cli/commands/vsearch.ts +75 -53
  77. package/src/cli/program.ts +287 -1
  78. package/src/config/index.ts +9 -0
  79. package/src/config/retrieval-traces.ts +56 -0
  80. package/src/config/types.ts +4 -0
  81. package/src/core/context-budget.ts +6 -0
  82. package/src/core/context-capsule-retrieval-schema.ts +4 -0
  83. package/src/core/context-capsule-schema.ts +17 -0
  84. package/src/core/context-capsule-validation.ts +3 -2
  85. package/src/core/context-capsule.ts +18 -0
  86. package/src/core/context-compiler.ts +44 -25
  87. package/src/core/context-evidence.ts +6 -0
  88. package/src/core/retrieval-qrels.ts +405 -0
  89. package/src/core/retrieval-replay-candidate.ts +368 -0
  90. package/src/core/retrieval-replay-types.ts +109 -0
  91. package/src/core/retrieval-replay-validation.ts +89 -0
  92. package/src/core/retrieval-replay.ts +441 -0
  93. package/src/core/retrieval-trace-evidence-origin.ts +178 -0
  94. package/src/core/retrieval-trace-export.ts +113 -0
  95. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  96. package/src/core/retrieval-trace-filters.ts +19 -0
  97. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  98. package/src/core/retrieval-trace-management-types.ts +132 -0
  99. package/src/core/retrieval-trace-management.ts +422 -0
  100. package/src/core/retrieval-trace-request.ts +141 -0
  101. package/src/core/retrieval-trace-session.ts +507 -0
  102. package/src/core/retrieval-trace.ts +472 -0
  103. package/src/llm/errors.ts +10 -1
  104. package/src/llm/httpGeneration.ts +11 -1
  105. package/src/llm/nodeLlamaCpp/generation.ts +54 -10
  106. package/src/llm/types.ts +6 -0
  107. package/src/mcp/tools/ask.ts +228 -0
  108. package/src/mcp/tools/context.ts +87 -15
  109. package/src/mcp/tools/get.ts +35 -1
  110. package/src/mcp/tools/index.ts +83 -0
  111. package/src/mcp/tools/query.ts +95 -64
  112. package/src/mcp/tools/search.ts +36 -13
  113. package/src/mcp/tools/trace.ts +143 -0
  114. package/src/mcp/tools/vsearch.ts +71 -38
  115. package/src/pipeline/answer.ts +167 -26
  116. package/src/pipeline/claim-verification-schema.ts +235 -0
  117. package/src/pipeline/claim-verification.ts +487 -0
  118. package/src/pipeline/claim-verifier.ts +474 -0
  119. package/src/pipeline/graph-retrieval.ts +15 -1
  120. package/src/pipeline/hybrid.ts +151 -43
  121. package/src/pipeline/search.ts +36 -3
  122. package/src/pipeline/trace-metadata.ts +47 -0
  123. package/src/pipeline/types.ts +68 -0
  124. package/src/pipeline/vsearch.ts +101 -38
  125. package/src/sdk/client.ts +415 -73
  126. package/src/sdk/documents.ts +48 -1
  127. package/src/sdk/index.ts +17 -0
  128. package/src/sdk/types.ts +28 -0
  129. package/src/serve/context-capsule.ts +67 -8
  130. package/src/serve/public/app.tsx +12 -1
  131. package/src/serve/public/components/AskVerificationPanel.tsx +189 -0
  132. package/src/serve/public/globals.built.css +1 -1
  133. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  134. package/src/serve/public/pages/Ask.tsx +42 -4
  135. package/src/serve/public/pages/Dashboard.tsx +10 -0
  136. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  137. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  138. package/src/serve/retrieval-trace.ts +28 -0
  139. package/src/serve/routes/api.ts +508 -68
  140. package/src/serve/routes/traces.ts +156 -0
  141. package/src/serve/server.ts +87 -2
  142. package/src/store/index.ts +31 -0
  143. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  144. package/src/store/migrations/index.ts +2 -0
  145. package/src/store/retrieval-trace-codec.ts +384 -0
  146. package/src/store/sqlite/adapter.ts +153 -1
  147. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  148. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  149. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  150. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  151. package/src/store/types.ts +297 -0
  152. package/src/store/vector/sqlite-vec.ts +76 -1
  153. package/src/store/vector/types.ts +1 -1
@@ -0,0 +1,592 @@
1
+ # Agentic Retrieval Evaluation Contract
2
+
3
+ This specification defines GNO's deterministic end-to-end retrieval outcome
4
+ benchmark. It measures whether an agent finds enough evidence, cites it exactly,
5
+ and stops efficiently. It complements ranking evals; it does not replace them.
6
+
7
+ The standard test suite validates fixtures, schemas, hashing, scoring, and
8
+ production ingestion without a model, network access, API key, or global GNO
9
+ configuration. Generation-backed benchmark runs remain local and opt-in.
10
+
11
+ ## Versions and layout
12
+
13
+ All contracts currently use `schemaVersion: "1.0"` and closed JSON Schema
14
+ draft-07 objects. Unknown properties fail validation.
15
+
16
+ ```text
17
+ evals/agentic/
18
+ types.ts
19
+ canonical.ts
20
+ strict-json.ts
21
+ validation.ts
22
+ fixture-db.ts
23
+ scoring.ts
24
+ promotion.ts
25
+ verified-ask-outcome.ts
26
+ verified-ask-promotion.ts
27
+ registry.ts
28
+ report.ts
29
+ report-artifacts.ts
30
+ cli-options.ts
31
+ cli.ts
32
+ adapter.ts
33
+ agent.ts
34
+ fixture-agent.ts
35
+ local-model-agent.ts
36
+ runner.ts
37
+ runner-contract.ts
38
+ runner-receipt.ts
39
+ runner-trial.ts
40
+ runner-validation.ts
41
+ schemas/
42
+ agent-task.schema.json
43
+ hidden-oracle.schema.json
44
+ final-envelope.schema.json
45
+ trajectory-receipt.schema.json
46
+ benchmark-report.schema.json
47
+
48
+ evals/fixtures/agentic-retrieval/
49
+ manifest.json
50
+ tasks/<opaque-task-id>.json
51
+ oracles/<opaque-task-id>.json
52
+ corpus/<opaque-task-id>/<opaque-collection>/<opaque-file>.md
53
+ agent-model.lock.json
54
+ baseline/
55
+ README.md
56
+ fixture-agent/
57
+ report.json
58
+ canonical.json
59
+ observations.json
60
+ report.md
61
+ verified-ask-promotion.json
62
+ verified-ask-promotion.md
63
+ optional/{qmd,local-model}/ # local opt-in evidence; not authoritative
64
+ ```
65
+
66
+ The first fixture version contains 24 original synthetic tasks and 34 Markdown
67
+ documents under the MIT license. It covers exact identifiers, ambiguity,
68
+ multi-document comparisons, meeting decisions, temporal questions, typed
69
+ relationships, code/documentation, multilingual prose, and missing-evidence
70
+ abstention. The manifest hashes the exact bytes of every task, oracle, and
71
+ corpus file. Its separate corpus fingerprint hashes the sorted logical inventory
72
+ of task ID, collection, relative path, and source hash.
73
+
74
+ ## Public task and hidden oracle boundary
75
+
76
+ An `AgentTask` contains only:
77
+
78
+ - an opaque ID and category
79
+ - the agent-visible goal and instructions
80
+ - public `claimKey`, tagged `valueType`, required, and substantive flags
81
+ - allowed tool names and call/context budgets
82
+ - opaque collection names available to the task
83
+
84
+ The outer agent receives the result of `projectAgentVisibleTask()` and normalized
85
+ tool results. It never receives the oracle, fixture manifest, setup paths,
86
+ adapter labels, or evaluation metadata.
87
+
88
+ The normalized tool contract is one deeply frozen `search`, `get`, and
89
+ `multi_get` schema shared by every adapter. An adapter declares unsupported or
90
+ unavailable capabilities without changing that schema. Result
91
+ `resultRole` is agent-visible and closed: `candidates` requires a subsequent
92
+ source read, `source` is an exact read, and `evidence_bundle` is complete enough
93
+ to support a one-call final envelope. The fixture agent responds to this role,
94
+ never to an adapter ID. A one-call-budget task may finalize exact candidate
95
+ evidence rather than exceed its budget.
96
+
97
+ A separate `HiddenOracle` contains normalized expected typed values,
98
+ normalizer ID/version, required/optional/forbidden evidence, expected missing
99
+ claims, collection/filter expectations, completion predicates, and hidden leak
100
+ canaries. Every task and oracle filename is an opaque ID. Validation scans every
101
+ agent-visible task, path, and corpus file for the oracle-only canaries.
102
+
103
+ Corpus text necessarily contains the evidence an agent is meant to retrieve;
104
+ the isolation guarantee concerns evaluator answers, normalizers, qrels,
105
+ completion predicates, and other oracle metadata.
106
+
107
+ ## Structured final envelope
108
+
109
+ `FinalEnvelope` deliberately has no answer or prose field:
110
+
111
+ ```json
112
+ {
113
+ "schemaVersion": "1.0",
114
+ "claims": [
115
+ {
116
+ "claimKey": "launchDate",
117
+ "value": { "type": "date", "value": "2026-09-14" },
118
+ "citations": []
119
+ }
120
+ ],
121
+ "gaps": [],
122
+ "abstained": false,
123
+ "stopReason": "complete"
124
+ }
125
+ ```
126
+
127
+ Claim values are tagged unions: `string`, `number`, `boolean`, `string[]`,
128
+ `date`, or `identifier`. Dates are ISO calendar dates. Gaps use one of
129
+ `missing_evidence`, `conflicting_evidence`, `budget_exhausted`, or
130
+ `tool_unavailable`. Stop reasons are `complete`, `abstained`,
131
+ `budget_exhausted`, `tool_unavailable`, or `error`.
132
+
133
+ Strict parsing rejects comments, trailing commas, duplicate properties at any
134
+ depth, prose, and malformed tagged values. Semantic validation
135
+ then rejects unknown or duplicate claim/gap keys, value types that disagree with
136
+ the public claim definition, missing required claims, and uncited required
137
+ claims. Invalid output is scored as unsupported; it is never ignored.
138
+
139
+ ## Exact evidence semantics
140
+
141
+ Evidence coordinates contain:
142
+
143
+ - `gno://<collection>/<relative-path>`
144
+ - lowercase SHA-256 of the source's exact UTF-8 bytes
145
+ - 1-based inclusive `startLine` and `endLine`
146
+ - lowercase SHA-256 of the exact selected span bytes
147
+ - separate source- and span-hash provenance
148
+
149
+ For line selection only, the fixture loader converts CRLF and lone CR to LF.
150
+ It then selects the inclusive lines and joins them with LF. It does not append a
151
+ synthetic final newline, trim whitespace, or normalize Unicode before hashing
152
+ the span. A source hash never performs newline or Unicode normalization.
153
+
154
+ Fixture corpus files are additionally required to already be stable under GNO's
155
+ production Markdown canonicalizer. That keeps production-ingested mirror line
156
+ coordinates aligned without changing the exact source-byte contract.
157
+
158
+ `harness_observed` means the harness derived the hash from exact observed bytes.
159
+ `backend_provided` means the adapter returned the hash. Normalized qmd results
160
+ will preserve these separately rather than synthesizing backend hashes from the
161
+ hidden oracle.
162
+
163
+ ## Immutable corpus and native indexes
164
+
165
+ `loadAgenticFixture()` verifies every manifest hash, schema, task/oracle pairing,
166
+ evidence coordinate, span hash, leak canary, and corpus fingerprint. It exposes
167
+ one frozen `CorpusSnapshot` for all adapters.
168
+
169
+ An adapter builds its native immutable index during unmeasured preparation from
170
+ that snapshot. `recordAdapterNativeIndex()` binds each adapter-specific index
171
+ fingerprint and volatile build observations to the same corpus fingerprint.
172
+ Cross-adapter index bytes need not match.
173
+
174
+ `prepareGnoNativeIndex()` is the reference production-ingestion helper. It:
175
+
176
+ 1. materializes the exact manifest-pinned bytes into a temporary root once;
177
+ 2. opens an explicit temporary SQLite path with the production tokenizer;
178
+ 3. registers the complete collection set;
179
+ 4. runs production `SyncService` conversion, canonicalization, chunking, FTS,
180
+ tag, link, and relationship projection with deterministic concurrency one;
181
+ 5. verifies processed/error counts and active document source hashes;
182
+ 6. fingerprints stable URI/source/mirror/index inputs while excluding document
183
+ IDs, timestamps, temp paths, and timings.
184
+
185
+ It never loads or writes global GNO configuration or a production database.
186
+ Cold and warm cohorts for an adapter must reuse the same prepared native index.
187
+
188
+ ## Trajectory receipts
189
+
190
+ A `TrajectoryReceipt` has two top-level partitions.
191
+
192
+ ### Canonical
193
+
194
+ The canonical partition contains every decision-affecting input and output:
195
+
196
+ - task, adapter, trial, seed, lifecycle, and agent IDs
197
+ - normalized calls, arguments, results, evidence, and stable error codes
198
+ - distinct outer `agentCalls` and internal `backendInvocations`
199
+ - exact model-visible UTF-8 bytes for every tool result, including repeated
200
+ reads and model-visible errors
201
+ - measured token counts and tokenizer fingerprint, or `null` when unavailable
202
+ - structured final envelope, stop reason, and failure class
203
+ - explicit tool/span/token/hash/lifecycle capability states, including
204
+ `unsupported` and `unavailable`
205
+ - corpus, prompt, tools, model, runtime, config, and index fingerprints
206
+
207
+ The exact agent-visible result projection includes status, `resultRole`, content,
208
+ citeable observed coordinates/hashes/provenance, evidence text, and error code.
209
+ Adapter backend hashes, backend-hash diagnostics, call accounting, tokenizer
210
+ accounting, temp paths, and oracle data are excluded from both the agent history
211
+ and its UTF-8 byte count.
212
+
213
+ For Capsule evidence bundles, `content` is byte-for-byte the production MCP
214
+ `gno-context-agent-v1` text projection. Exact evidence is not duplicated in the
215
+ normalized evidence field. The benchmark charges the complete normalized
216
+ agent-visible envelope containing that text once; the full canonical Capsule
217
+ in MCP `structuredContent` is application-only and excluded. This target models
218
+ hosts that keep structured data outside model context. Hosts that expose both
219
+ text and `structuredContent` must charge both and cannot cite this promotion
220
+ result without a separate run.
221
+
222
+ Canonical JSON recursively sorts object keys by code-unit order, preserves array
223
+ order, and rejects `undefined`, non-finite numbers, and non-JSON values. It
224
+ excludes all observations. Unchanged deterministic inputs therefore produce
225
+ byte-identical canonical JSON and SHA-256.
226
+
227
+ `agentCalls` counts every valid outer-agent tool choice, including a choice
228
+ whose adapter call times out or throws, whose returned envelope is malformed,
229
+ or whose result is rejected before delivery by context/token accounting.
230
+ Each canonical call records `deliveredToAgent` and a nullable `failureCode`.
231
+ Undelivered calls retain a valid returned result and known backend invocation
232
+ count when available; otherwise they use a closed synthetic error result. They
233
+ always contribute zero model-visible bytes and have no per-call token
234
+ measurement. Aggregate measured tokens sum delivered calls only, preserving
235
+ any earlier measured context; with no delivered measured call, tokenizer
236
+ comparability remains `null`. The undelivered call is unique, terminal, bound
237
+ to the receipt failure code, and excluded from the outer-agent history.
238
+ `backendInvocations` counts adapter-internal
239
+ retrieval, fetch, rerank, or synthesis operations. Promotion efficiency uses
240
+ `agentCalls`; backend invocations are reported separately and cannot substitute
241
+ for them.
242
+
243
+ ### Observations
244
+
245
+ The observations partition contains volatile data:
246
+
247
+ - recording timestamp
248
+ - preparation, startup, model-load, tool, driver, and end-to-end timings
249
+ - process/resource measurements
250
+ - temporary paths and redacted diagnostics, including volatile failure messages
251
+
252
+ Each timing is either `{ valueMs: <non-negative>, unavailableReason: null }` or
253
+ `{ valueMs: null, unavailableReason: <non-empty reason> }`. Changing an
254
+ observation cannot change the canonical receipt fingerprint.
255
+
256
+ Preparation/index build is outside both lifecycle cohorts. Cold end-to-end time
257
+ starts before fresh process startup against the prepared index and includes the
258
+ first scored call. Warm time starts at the first scored agent step after exactly
259
+ one discarded readiness probe on a preserved process/model/index. Reports never
260
+ compare unlike lifecycle states.
261
+
262
+ ## Failure and cohort accounting
263
+
264
+ Failures are `harness_error`, `agent_error`, or `product_error`; successful
265
+ receipts use `none`. Harness failures are attempted pairs but are excluded from
266
+ product scoring with an explicit reason. They cannot silently reduce a cohort.
267
+ Malformed tool/final envelopes, duplicate JSON keys, timeouts, and unavailable
268
+ requested adapters fail closed. Adapter calls receive an `AbortSignal`; a timed
269
+ out or state-unknown warm call excludes the remaining cohort explicitly rather
270
+ than running alongside a leaked request. Deterministic agent errors do not
271
+ invalidate later warm pairs.
272
+
273
+ The runner rejects empty, duplicate, or malformed task, adapter, lifecycle, and
274
+ trial schedules before preparation. Every attached adapter must preserve its
275
+ prepared owner identity, config fingerprint, capability contract, and index
276
+ fingerprint. Preparation, reset, tool outcomes, runtime/session identities,
277
+ token measurements, agent steps, and tool arguments are closed runtime-validated
278
+ before they enter a receipt or reach a product adapter. Tool listing, session
279
+ construction, inference, calls, and best-effort disposal are bounded.
280
+
281
+ ## Outer-agent lanes and lifecycle
282
+
283
+ The standard fixture agent is a pinned answer-free state machine. It selects one
284
+ preferred lexical cue per public claim, performs `search`, reads returned URIs
285
+ for candidate results, reduces exact observed lines into declared typed claims,
286
+ and stops or abstains within the public budgets. Ordinary candidates require a
287
+ `get` or `multi_get`; a complete evidence bundle can stop after one call.
288
+
289
+ The optional cached-local-model lane is fail-closed. `agent-model.lock.json`
290
+ pins one exact Hugging Face URI, whole-GGUF SHA-256 (also binding its embedded
291
+ tokenizer), tokenizer identifier, step/output budgets, and exactly three unique
292
+ paired trial IDs/seeds. `GNO_AGENTIC_MODEL_PATH` must point at that already
293
+ cached exact file. Preflight performs strict duplicate-safe JSON parsing and a
294
+ streaming SHA-256 check before model initialization. It never resolves a remote
295
+ endpoint, downloads a model, reads an API key, or falls back to the network.
296
+ Model output is one strict JSON tool action or `FinalEnvelope`; prose and
297
+ duplicate keys are agent errors.
298
+
299
+ ## Product-faithful GNO MCP comparator
300
+
301
+ The `gno-mcp` adapter measures the shipped stdio MCP process rather than
302
+ importing a retrieval pipeline. Its normalized surface maps `search` to
303
+ `gno_query`, `get` to `gno_get`, and `multi_get` to `gno_multi_get`. The adapter
304
+ lists the real product tools during unmeasured preparation and fails closed if
305
+ any mapped field is missing from the shipped input schemas. It sets
306
+ `lineNumbers: false` on reads so exact returned bytes can be bound to fixture
307
+ line coordinates; this is a public MCP option, not an evaluator shortcut.
308
+
309
+ Preparation materializes the immutable corpus into an isolated config/data/
310
+ cache root, runs production ingestion, and embeds every active chunk before
311
+ scoring. `gno-models.lock.json` pins the exact embed, rerank, expansion, and
312
+ generation GGUF URI, byte size, and SHA-256. `GNO_AGENTIC_GNO_MODEL_DIR` may
313
+ point at a cache containing those exact files. The harness streams and verifies
314
+ all four files, rewrites the isolated config to `file://` URIs, passes
315
+ `--offline`, sets `HF_HUB_OFFLINE=1`, and rejects missing or mismatched vectors.
316
+ It never downloads a model or mutates the user's config/database/model cache.
317
+
318
+ Cold trials create a fresh stdio MCP process against the already prepared
319
+ index. Warm trials preserve one process and first issue exactly one discarded
320
+ `gno_query` readiness probe with `fast: true`; the probe must report
321
+ `vectorsUsed: true`. Process startup and tool latency are measured separately.
322
+ GNO does not expose model-load timing independently from its first query, so
323
+ that observation is explicitly unavailable rather than inferred.
324
+
325
+ Normalized candidate and source payloads strip absolute paths, mtimes, and
326
+ volatile error messages. Non-default-index URI decoration is removed because
327
+ the isolated adapter already owns one explicit index. Citeable evidence is
328
+ emitted only when returned text exactly matches the snapshot's inclusive line
329
+ span. Evidence is line-atomic even when GNO returns a multi-line chunk. Source
330
+ and span hashes are recomputed from observed fixture bytes. Because GNO does
331
+ not return a backend span hash, the closed backend hash pair is explicitly
332
+ unavailable; the product source hash remains in normalized candidate metadata.
333
+ Repeated reads remain repeated calls and bytes. Query backend invocation
334
+ accounting includes lexical/vector retrieval, expansion, reranking, and graph
335
+ stages declared by structured MCP metadata.
336
+
337
+ The native index may contain the whole fixture, but every reset establishes one
338
+ task visibility boundary. Single-collection searches are automatically scoped;
339
+ multi-collection tasks must name one of their declared collections. Foreign
340
+ collections and foreign `get`/`multi_get` URIs are rejected before MCP traffic,
341
+ and any foreign result returned by the product fails the trial without exposing
342
+ its content to the outer agent.
343
+
344
+ Fake-process tests are part of the normal offline suite. The isolated real
345
+ stdio smoke is opt-in with `GNO_AGENTIC_RUN_REAL_MCP=1`; it uses the exact model
346
+ lock and performs no network access. Successful MCP envelopes are validated
347
+ before normalization; malformed or source-hash-mismatched output fails closed.
348
+ Preparation cancellation is threaded through model verification, embedding,
349
+ and MCP preflight, with child termination and isolated-root cleanup.
350
+
351
+ ## Optional pinned qmd comparator
352
+
353
+ The `qmd` lane is explicit opt-in and fail-closed. `QMD_REPO` must be an
354
+ absolute, clean checkout at commit
355
+ `e428df76bc0274d9e93eb7ca3e95673315c42e90`. Preflight verifies the exact
356
+ origin, commit, clean tree, package manifest, lockfile, executable entrypoint,
357
+ and the dynamically listed MCP tool name, description, and input-schema
358
+ fingerprints. It also verifies three pinned model identities by URI, filename,
359
+ native cache filename, byte size, and streamed SHA-256:
360
+
361
+ - `hf_ggml-org_embeddinggemma-300M-Q8_0.gguf`
362
+ - `hf_ggml-org_qwen3-reranker-0.6b-q8_0.gguf`
363
+ - `hf_tobil_qmd-query-expansion-1.7B-q4_k_m.gguf`
364
+
365
+ The lock deliberately does not claim a separately verifiable model-repository
366
+ revision: the already-cached GGUF identity is pinned by exact URI, native
367
+ filename, byte size, and whole-file SHA-256. The committed lock's raw bytes are
368
+ also SHA-256 pinned before any checkout or model validation, and that raw lock
369
+ identity is included in the adapter configuration fingerprint.
370
+
371
+ `QMD_MODEL_CACHE` may name an absolute read-only cache containing those exact
372
+ files. The adapter never resolves qmd from `PATH`, uses a global install,
373
+ downloads a model, pulls or checks out the repository, or mutates the checkout
374
+ or supplied cache. Missing, stale, dirty, mismatched, or schema-drifted inputs
375
+ are harness errors, never skips or degraded comparisons. The intentionally
376
+ strict preflight therefore fails until the exact checkout and model artifacts
377
+ have been prepared.
378
+
379
+ Preparation runs qmd's update, embed, and status lifecycle outside measured
380
+ trials, then verifies the native index. `QMD_CONFIG_DIR`, `XDG_CONFIG_HOME`,
381
+ `XDG_CACHE_HOME`, `INDEX_PATH`, and data paths are isolated under one temporary
382
+ root; locked models are copied and reverified there. Cold trials start a fresh
383
+ stdio process on a byte-identical clone of the pristine prepared database.
384
+ Warm trials retain one process after exactly one discarded full query using the
385
+ task goal, declared collections, `rerank: true`, and readiness-only intent so
386
+ models load without colliding with a scored query cache key.
387
+
388
+ qmd result ranges are parsed from the inner `@@` coordinates and accepted only
389
+ when returned bytes exactly match the fixture snapshot. Evidence is emitted as
390
+ atomic lines with harness-observed source and span hashes; backend hashes remain
391
+ the complete null pair with an explicit unavailable reason. The same pre-call
392
+ task scope and post-result isolation rules apply as for GNO. qmd does not expose
393
+ reliable internal backend invocation counts, model-load timing, or token
394
+ measurements, so invocation-accounting capability is `false`, its count is
395
+ zero with a diagnostic, and those observations remain explicitly unavailable.
396
+
397
+ Committed reports record attempted pairs, scored pairs, every exclusion,
398
+ receipts, identity-bearing task scores, Capsule replay proofs, exact
399
+ environment/methodology, and known limitations. The environment includes the
400
+ package and Bun versions, platform/architecture, Git commit/dirty state,
401
+ fixture version/fingerprint, selected agent, and trial schedule.
402
+
403
+ The report `canonicalFingerprint` hashes a non-self-referential projection:
404
+ the fingerprint field itself and volatile receipt observations are excluded;
405
+ environment provenance, methodologies, limitations, native index identities,
406
+ canonical receipts, identity-bearing scores, exclusions, Capsule payload bytes
407
+ and hashes, and promotion results remain included. `report.json` is schema
408
+ valid. `canonical.json` contains that exact projection. `observations.json`
409
+ holds environment, build observations, and full-identity receipt observations;
410
+ committed temporary paths are projected to `<temp>`. `report.md` is the readable
411
+ summary. The six files are staged and directory-renamed as one baseline set.
412
+ The verified Ask files are a separate attributable outcome lane; they do not
413
+ rename the Capsule retrieval promotion in `report.json`.
414
+
415
+ ## Deterministic scoring
416
+
417
+ The scorer compares typed claims and exact citations with the hidden oracle. It
418
+ reports:
419
+
420
+ - completed and supported claims
421
+ - unsupported claims and invalid outputs
422
+ - missing required claims/evidence
423
+ - forbidden evidence
424
+ - correct abstention
425
+ - premature `complete` stops
426
+ - unnecessary reads from call/context budgets and, for designated tasks,
427
+ unexpected evidence
428
+ - collection and filter correctness
429
+ - substantive claims linked to complete supporting evidence
430
+
431
+ No LLM judge participates in these gates. Optional model judging may assess
432
+ prose in separate experiments, but cannot override deterministic promotion.
433
+
434
+ ## Capsule promotion formulas
435
+
436
+ Promotion compares Capsule and current GNO only over the identical non-harness-
437
+ failed task/trial/seed/lifecycle/agent pair set `P`. Baseline adapter identity
438
+ must be exactly `gno-mcp`; candidate identity must be exactly `capsule`. Corpus,
439
+ prompt, tool, model, and runtime fingerprints must match. Adapter config and
440
+ native index fingerprints may differ by design. Every score record must match
441
+ its receipt identity.
442
+
443
+ For every pair:
444
+
445
+ ```text
446
+ success_capsule(p) >= success_gno(p)
447
+ ```
448
+
449
+ Aggregate accuracy must also have no loss:
450
+
451
+ ```text
452
+ sum(success_capsule) / |P| >= sum(success_gno) / |P|
453
+ ```
454
+
455
+ Efficiency gates are:
456
+
457
+ ```text
458
+ 1 - sum(agentCalls_capsule) / sum(agentCalls_gno) >= 0.25
459
+ 1 - sum(modelVisibleUtf8Bytes_capsule) / sum(modelVisibleUtf8Bytes_gno) >= 0.35
460
+ ```
461
+
462
+ Claim linkage is:
463
+
464
+ ```text
465
+ linkedSupportedClaims_capsule / substantiveClaims_capsule >= 0.95
466
+ ```
467
+
468
+ Unsupported substantive claims must strictly decrease on a comparable paired
469
+ cohort:
470
+
471
+ ```text
472
+ unsupportedClaims_capsule < unsupportedClaims_gno
473
+ 1 - unsupportedClaims_capsule / unsupportedClaims_gno
474
+ ```
475
+
476
+ The report records both counts and the reduction. A missing paired baseline,
477
+ an identity mismatch, or a zero unsupported-claim baseline makes this reduction
478
+ unavailable/non-comparable; GNO reports that state rather than fabricating an
479
+ improvement.
480
+
481
+ All denominators must be non-zero. Abstention-only tasks use their completion
482
+ predicate and do not fabricate substantive claims. Every fixture-agent Capsule
483
+ task must also emit byte-identical canonical Capsule payload JSON and matching
484
+ SHA-256 across the scored run and one unchanged-input replay. Empty,
485
+ non-canonical, wrong-task, or synthetic sentinel payloads fail. Missing pairs,
486
+ duplicates, identity mismatches,
487
+ pairwise or aggregate accuracy loss, denominator failure, threshold miss, or
488
+ nondeterminism fails promotion.
489
+
490
+ ## Verified Ask promotion formulas
491
+
492
+ The authoritative fixture-agent write additionally runs a separate 22-task
493
+ outcome lane. It excludes only the two declared expected-missing/abstention
494
+ tasks. Every included task must contain exactly one required substantive claim;
495
+ missing, duplicate, extra, or mismatched pairs fail closed.
496
+
497
+ The compatible cohort is an independent frozen contract, not inferred from the
498
+ artifact under validation:
499
+
500
+ ```text
501
+ t012ab3c t0a1b2c3 t123bc4d t1b2c3d4 t2c3d4e5 t3d4e5f6
502
+ t456ef70 t4e5f607 t567f081 t5f60718 t6071829 t6780192
503
+ t718293a t7891a03 t8293a4b t93a4b5c ta4b5c6d tb5c6d7e
504
+ tc6d7e8f td7e8f90 te8f901a tf901a2b
505
+ ```
506
+
507
+ The exact exclusions are `t234cd5e` and `t345de6f`, both with reason
508
+ `expected_missing_evidence`. Removing or replacing a complete receipt/score
509
+ pair and resealing every derived fingerprint still fails validation.
510
+
511
+ The baseline executes the production raw Ask path:
512
+ `searchHybrid` → `generateGroundedAnswer` → `processAnswerResult`. The candidate
513
+ executes production `buildVerifiedAsk`. Each pair shares the immutable native
514
+ index, task goal, collection, structured search modes, deterministic answer
515
+ agent/model fingerprint, and initial answer draft. Receipt and score identities
516
+ bind task, lane, trial, seed, and agent. Pairing also requires identical fixture,
517
+ index, request, and model fingerprints.
518
+
519
+ Four fixed, diverse tasks receive an unsupported deterministic draft in both
520
+ lanes. The other 18 receive the oracle-supported draft. This controlled
521
+ adversarial subset tests enforcement at the product boundary; it does not claim
522
+ general model quality.
523
+
524
+ For every pair and in aggregate:
525
+
526
+ ```text
527
+ answerAccuracy_verified(p) >= answerAccuracy_raw(p)
528
+ mean(answerAccuracy_verified) >= mean(answerAccuracy_raw)
529
+ unsupportedSubstantiveClaims_verified < unsupportedSubstantiveClaims_raw
530
+ ```
531
+
532
+ `verified-ask-promotion.json` contains canonical receipts, identity-bearing
533
+ scores, exact cohort/exclusions, metrics, and the gate result.
534
+ `verified-ask-promotion.md` is its readable projection. Temporary collection
535
+ paths and timings are excluded from the canonical contract; fixture, index,
536
+ request, model, exact answer, citation hashes, verification status, and scored
537
+ outcome remain bound. The evaluator parses the typed claim from the exact final
538
+ product answer and scores it against the independent fn-97 oracle; it
539
+ recomputes receipt, answer, score, and artifact fingerprints rather than
540
+ trusting harness-assigned claim or score fields. Raw and verified lane semantics
541
+ are validated independently. A supported final answer is exactly one encoded
542
+ typed claim followed by its lane citation (`[1].` or one
543
+ `[evidence:<sha256>].`); prefixes, extra claims, and trailing prose are invalid.
544
+ An abstention must equal the production abstention text and contain no
545
+ citations. Authoritative generation refuses a dirty Git checkout and records
546
+ the exact clean source commit.
547
+
548
+ ## Commands
549
+
550
+ Contract tests are ordinary offline tests:
551
+
552
+ ```bash
553
+ bun test test/eval/agentic
554
+ ```
555
+
556
+ The runner is local and opt-in:
557
+
558
+ ```bash
559
+ bun run eval:agentic
560
+ bun run eval:agentic -- --adapter gno-mcp,lexical,capsule --task t0a1b2c3 --lifecycle cold --agent fixture --timeout-ms 30000
561
+ QMD_REPO=/path/to/pinned/qmd QMD_MODEL_CACHE=/path/to/cache bun run eval:agentic -- --adapter qmd
562
+ ```
563
+
564
+ Filters are CSV lists and reject empty, duplicate, or unknown values before
565
+ adapter preparation. Defaults are all tasks, `gno-mcp,lexical,capsule`, both
566
+ lifecycles, and the fixture agent. qmd is lazily registered and never runs by
567
+ default. A requested unavailable qmd lane produces the complete requested
568
+ harness-error matrix/report and exits `2`; it never disappears or downgrades.
569
+
570
+ Exit `0` means a complete run and, when applicable, both promotions pass. Exit
571
+ `1` means the complete Capsule or verified Ask promotion gate failed. Exit `2`
572
+ means invalid CLI, preflight, harness, or requested-adapter failure. `--write`
573
+ accepts only a full
574
+ 24-task/two-lifecycle lane: the fixture-agent three-adapter lane writes the
575
+ authoritative baseline, while qmd and the three-trial cached-local-model lane
576
+ write only under `baseline/optional/`. Filtered or mixed writes are refused and
577
+ can never overwrite the authoritative baseline.
578
+
579
+ ## Limitations
580
+
581
+ - The corpus is controlled regression evidence, not a representative claim
582
+ about every agent, domain, or language.
583
+ - The deterministic fixture agent will be narrower than a general model.
584
+ - Model-visible UTF-8 bytes are the primary cross-adapter context measure;
585
+ tokens compare only under one pinned tokenizer.
586
+ - Latency remains environment-specific and only compares matching lifecycle
587
+ cohorts.
588
+ - qmd is an optional exact-revision comparator and is never required by the
589
+ standard test suite.
590
+ - Capsule retrieval/planning remains a deterministic fixture prototype; its
591
+ model-visible serializer and omission accounting are the production MCP
592
+ contract.