@agentskit/doc-bridge 1.7.44 → 1.9.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 (223) hide show
  1. package/CHANGELOG.md +471 -0
  2. package/CONTRIBUTING.md +29 -4
  3. package/README.md +87 -40
  4. package/SECURITY.md +7 -0
  5. package/action.yml +1 -1
  6. package/bin/ak-docs.js +2 -2
  7. package/bin/ak-verify.js +13 -7
  8. package/dist/cli/program.d.ts +3 -1
  9. package/dist/cli/program.js +15888 -6061
  10. package/dist/cli/program.js.map +1 -1
  11. package/dist/config/index.d.ts +1 -1
  12. package/dist/config/index.js +91 -9
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/index-Beor6Yhi.d.ts +792 -0
  15. package/dist/index.d.ts +9979 -3257
  16. package/dist/index.js +15954 -5774
  17. package/dist/index.js.map +1 -1
  18. package/docs/MARKETPLACE.md +1 -1
  19. package/docs/PRD-documentation-efficiency-study.md +406 -0
  20. package/docs/PRD-knowledge-retrieval-and-enrichment.md +466 -0
  21. package/docs/RELEASE.md +22 -8
  22. package/docs/adr/0002-documentation-audit-boundary.md +22 -0
  23. package/docs/adr/0003-study-protocol-and-historical-evidence.md +40 -0
  24. package/docs/adr/0004-controlled-study-runner.md +25 -0
  25. package/docs/adr/0005-documentation-quality-and-criticality.md +20 -0
  26. package/docs/adr/0006-registry-semantic-grounding.md +20 -0
  27. package/docs/adr/0007-longitudinal-study-metrics.md +21 -0
  28. package/docs/adr/0008-study-verification-boundary.md +21 -0
  29. package/docs/adr/0009-study-provider-cli-adapter.md +25 -0
  30. package/docs/agent-corpus/INDEX.md +14 -3
  31. package/docs/agent-corpus/OVERVIEW.md +25 -0
  32. package/docs/agent-corpus/chat.md +7 -3
  33. package/docs/agent-corpus/cli.md +18 -2
  34. package/docs/agent-corpus/conformance.md +14 -2
  35. package/docs/agent-corpus/doc-bridge.md +48 -1
  36. package/docs/agent-corpus/doctor.md +10 -2
  37. package/docs/agent-corpus/gates.md +6 -2
  38. package/docs/agent-corpus/mcp.md +15 -2
  39. package/docs/agent-corpus/memory.md +6 -2
  40. package/docs/agent-corpus/query.md +35 -2
  41. package/docs/bench/README.md +122 -0
  42. package/docs/bench/retrieval-baseline-v1.json +28 -0
  43. package/docs/bench/retrieval-suite-v1.json +1033 -0
  44. package/docs/chat-and-rag.md +3 -2
  45. package/docs/for-agents.md +9 -1
  46. package/docs/getting-started.md +4 -11
  47. package/docs/guides/gate-ci.md +11 -1
  48. package/docs/guides/install-and-run.md +9 -65
  49. package/docs/index.md +22 -1
  50. package/docs/knowledge-engine-runbook.md +51 -4
  51. package/docs/landing/assets/context-payload-reduction.svg +21 -0
  52. package/docs/landing/assets/controlled-ab-comparison.svg +30 -0
  53. package/docs/landing/index.html +119 -5
  54. package/docs/loop-workflow.md +117 -0
  55. package/docs/mcp.md +6 -1
  56. package/docs/parity/public-claims-v1.json +145 -0
  57. package/docs/playbook/doc-bridge-pattern.md +1 -1
  58. package/docs/query.md +90 -2
  59. package/docs/recipes/index-pipeline.md +1 -1
  60. package/docs/schemas/agent-handoff-v1.md +15 -0
  61. package/docs/schemas/doc-bridge-index-v1.md +65 -0
  62. package/docs/spec/benchmark-v1.md +39 -1
  63. package/docs/spec/cli.md +30 -10
  64. package/docs/spec/config-v1.md +192 -8
  65. package/docs/spec/documentation-audit-v1.md +61 -0
  66. package/docs/spec/enrichment-overlay-v1.md +241 -0
  67. package/docs/spec/graph-signals-v1.md +92 -0
  68. package/docs/spec/incremental-scan-v1.md +102 -0
  69. package/docs/spec/markdown-analyzer-v1.md +73 -0
  70. package/docs/spec/mcp-knowledge-tools-v1.md +147 -0
  71. package/docs/spec/measured-enrichment-v1.md +229 -0
  72. package/docs/spec/public-parity-v1.md +119 -0
  73. package/docs/spec/registry-agents.md +6 -0
  74. package/docs/spec/render-v1.md +122 -0
  75. package/docs/spec/retrieval-index-v1.md +164 -0
  76. package/docs/spec/study-metrics-v1.md +58 -0
  77. package/docs/spec/study-protocol-v1.md +46 -0
  78. package/docs/spec/study-provider-cli-v1.md +116 -0
  79. package/docs/spec/study-runner-v1.md +35 -0
  80. package/docs/spec/study-task-suite-v1.md +41 -0
  81. package/docs/spec/study-verification-v1.md +40 -0
  82. package/docs/study/README.md +84 -0
  83. package/docs/study/ab-adjudicated-cost-analysis-v1.md +29 -0
  84. package/docs/study/ab-adjudicated-cost-plan-v1.json +33 -0
  85. package/docs/study/ab-adjudicated-cost-plan-v2-v1.json +33 -0
  86. package/docs/study/ab-adjudicated-cost-result-v1.json +80 -0
  87. package/docs/study/ab-baseline-analysis-v1.md +21 -0
  88. package/docs/study/ab-baseline-plan-v1.json +33 -0
  89. package/docs/study/ab-baseline-recovery-plan-v1.json +33 -0
  90. package/docs/study/ab-baseline-result-v1.json +79 -0
  91. package/docs/study/documentation-audit-round-2026-08-31.json +183 -0
  92. package/docs/study/historical-evidence-v1.json +252 -0
  93. package/docs/study/observation-ledger-v1.json +30632 -0
  94. package/docs/study/phase3-task-coverage-v1.json +34 -0
  95. package/docs/study/phase4-public-pilot-ledger-v1.json +1344 -0
  96. package/docs/study/phase4-public-pilot-result-v1.json +52 -0
  97. package/docs/study/phase4-public-pilot-run-plan-v1.json +26 -0
  98. package/docs/study/phase4-public-pilot-task-suite-v1.json +71 -0
  99. package/docs/study/pilot-round-2026-08-31.json +46 -0
  100. package/docs/study/protocol-v1.json +90 -0
  101. package/docs/study/publication-gate-v1.md +45 -0
  102. package/docs/study/quality-scorecard-cycle-plan.md +545 -0
  103. package/docs/study/quality-scorecard-v1.json +38 -0
  104. package/docs/study/round-1-adjudicated-smoke-v1.json +30642 -0
  105. package/docs/study/round-1-instrumentation-plan-v1.md +39 -0
  106. package/docs/study/round-2-expanded-adjudication-v1.json +91 -0
  107. package/docs/study/round-2-expanded-validation-v1.md +58 -0
  108. package/docs/study/round-3-evidence-contract-v1.json +75 -0
  109. package/docs/study/round-3-evidence-contract-v1.md +57 -0
  110. package/docs/study/round-4-confirmation-v1.json +75 -0
  111. package/docs/study/round-4-confirmation-v1.md +55 -0
  112. package/docs/study/run-plan-v1.json +33 -0
  113. package/docs/study/semantic-adjudication-cycle-8.md +20 -0
  114. package/docs/study/task-suite-v1.json +96 -0
  115. package/docs/study/token-efficiency-plan-v1.md +337 -0
  116. package/docs/study/token-efficiency-protocol-v2.json +62 -0
  117. package/docs/study/verification-binding-v1.json +27 -0
  118. package/docs/validation-cycle-plan.md +33 -0
  119. package/docs/verification-harness.md +15 -6
  120. package/ecosystem-claims.json +2 -2
  121. package/ecosystem-upstream.json +2 -2
  122. package/ecosystem.json +4 -4
  123. package/mcpb/manifest.json +9 -1
  124. package/package.json +89 -72
  125. package/scripts/check-ecosystem-upstream.mjs +36 -7
  126. package/scripts/report-visual-check.mjs +20 -3
  127. package/skills/doc-bridge-handoff/fixtures/synthetic-repo/docs/for-agents/packages/payments.md +7 -0
  128. package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
  129. package/src/agents/registry-adapter.ts +192 -24
  130. package/src/audit/documentation.ts +513 -0
  131. package/src/bench/baseline.ts +198 -0
  132. package/src/bench/overlay-delta.ts +139 -0
  133. package/src/bench/retrieval.ts +319 -0
  134. package/src/budget/compile.ts +91 -0
  135. package/src/budget/sections.ts +70 -0
  136. package/src/cli/demo.ts +2 -2
  137. package/src/cli/program.ts +699 -79
  138. package/src/cli/usage.ts +71 -0
  139. package/src/config/defaults.ts +1 -0
  140. package/src/config/index.ts +4 -0
  141. package/src/config/load-config.ts +7 -1
  142. package/src/config/schema.ts +121 -4
  143. package/src/conformance/documentation-standard-v1.ts +22 -14
  144. package/src/discovery/areas.ts +182 -0
  145. package/src/discovery/documentation.ts +255 -23
  146. package/src/discovery/identity.ts +24 -0
  147. package/src/discovery/incremental.ts +314 -0
  148. package/src/discovery/inputs.ts +110 -0
  149. package/src/discovery/markdown.ts +481 -0
  150. package/src/discovery/repository.ts +557 -125
  151. package/src/doctor/run-doctor.ts +246 -27
  152. package/src/enrich/approvals.ts +190 -0
  153. package/src/enrich/cache.ts +93 -0
  154. package/src/enrich/context-pack.ts +272 -0
  155. package/src/enrich/overlay.ts +255 -0
  156. package/src/enrich/review.ts +106 -0
  157. package/src/enrich/stage.ts +374 -0
  158. package/src/enrich/stats.ts +100 -0
  159. package/src/enrich/validate.ts +410 -0
  160. package/src/federation/llms.ts +74 -24
  161. package/src/findings/report.ts +103 -0
  162. package/src/fixes/proposals.ts +4 -3
  163. package/src/graph/build.ts +356 -0
  164. package/src/graph/memory.ts +208 -0
  165. package/src/index-builder/build-handoffs.ts +22 -11
  166. package/src/index-builder/build-index.ts +132 -3
  167. package/src/index-builder/human-adapters/fumadocs.ts +1 -1
  168. package/src/index-builder/llms-txt.ts +48 -8
  169. package/src/index-builder/project-corpus.ts +111 -0
  170. package/src/index-builder/watch-index.ts +1 -1
  171. package/src/index.ts +630 -2
  172. package/src/lib/bounded-text.ts +15 -10
  173. package/src/lib/fuzzy-match.ts +235 -0
  174. package/src/mcp/knowledge.ts +554 -0
  175. package/src/mcp/server.ts +113 -18
  176. package/src/metrics/benchmark.ts +21 -0
  177. package/src/parity/check.ts +309 -0
  178. package/src/parity/claims.ts +259 -0
  179. package/src/parity/resolve.ts +160 -0
  180. package/src/query/handoff.ts +326 -0
  181. package/src/query/load-index.ts +53 -1
  182. package/src/query/query.ts +92 -59
  183. package/src/query/search.ts +289 -92
  184. package/src/query/text.ts +155 -0
  185. package/src/reconciliation/reconcile.ts +148 -15
  186. package/src/render/data.ts +356 -0
  187. package/src/render/engine.ts +398 -0
  188. package/src/render/generated.ts +77 -0
  189. package/src/render/render.ts +209 -0
  190. package/src/render/template-source.ts +52 -0
  191. package/src/render/templates.ts +289 -0
  192. package/src/report/html.ts +23 -17
  193. package/src/retrieval/bm25.ts +161 -0
  194. package/src/retrieval/project.ts +495 -0
  195. package/src/retrieval/rank.ts +383 -0
  196. package/src/retrieval/weights.ts +39 -0
  197. package/src/retriever/doc-bridge-retriever.ts +100 -15
  198. package/src/rules/engine.ts +45 -12
  199. package/src/safety/repository.ts +1 -1
  200. package/src/schemas/agent-handoff.ts +56 -0
  201. package/src/schemas/budget.ts +37 -0
  202. package/src/schemas/doc-bridge-index.ts +53 -2
  203. package/src/schemas/enrichment.ts +369 -0
  204. package/src/schemas/json-schemas.ts +39 -2
  205. package/src/schemas/knowledge.ts +19 -3
  206. package/src/schemas/retrieval-index.ts +152 -0
  207. package/src/shims/graphology.d.ts +91 -0
  208. package/src/study/adjudication.ts +196 -0
  209. package/src/study/execution.ts +350 -0
  210. package/src/study/expectations.ts +219 -0
  211. package/src/study/metrics.ts +467 -0
  212. package/src/study/protocol.ts +271 -0
  213. package/src/study/provider-cli.ts +115 -0
  214. package/src/study/provider-telemetry.ts +47 -0
  215. package/src/study/quality-scorecard.ts +164 -0
  216. package/src/study/runner.ts +461 -0
  217. package/src/study/task-suite.ts +321 -0
  218. package/src/study/verification.ts +134 -0
  219. package/src/validate.ts +8 -5
  220. package/src/version.ts +1 -1
  221. package/src/workflow/engine.ts +36 -11
  222. package/dist/index-C2PCQSrB.d.ts +0 -2251
  223. package/scripts/verification-harness.mjs +0 -483
@@ -0,0 +1,229 @@
1
+ ---
2
+ title: Measured enrichment v1
3
+ description: What an enrichment run cost, what it invented, whether it improved retrieval at all, and the study measurements — mechanical task expectations, the assisted arm, tokens to first evidence — that decide the same questions for a controlled round.
4
+ ---
5
+
6
+ # Measured enrichment v1
7
+
8
+ An enrichment stage nobody measures is a stage nobody can defend.
9
+
10
+ [Enrichment overlay v1](./enrichment-overlay-v1.md) made agent proposals typed, validated and
11
+ bounded. It did not say whether any of it helped. The overlay reported what it accepted, which is
12
+ the one number an agent cannot fail: a curator that proposes a hundred things and has ninety
13
+ rejected looks exactly like one that proposes ten good ones. And the controlled study could not
14
+ answer the question either — its last round recorded zero semantic successes in both arms, because
15
+ a task whose only success criterion is a model's opinion produces no signal, and its third arm has
16
+ been reserved since the first suite without ever running.
17
+
18
+ This document is the measurement layer: the whole shape of an enrichment run, the retrieval delta
19
+ that can block it, and the study machinery that asks the same questions of a controlled round.
20
+
21
+ ## What the run reports
22
+
23
+ `EnrichmentStats` in `src/schemas/enrichment.ts` is written by `runEnrichment` and stored on the
24
+ overlay. It is deliberately outside the overlay's content hash — two runs over one unchanged
25
+ repository must agree on their decisions while disagreeing about how long they took.
26
+
27
+ | Field | Meaning |
28
+ | --- | --- |
29
+ | `byKind` | Per proposal kind: `proposed`, `accepted`, `pending`, `rejected`. Sorted by kind, so two runs produce the same bytes. |
30
+ | `rejectionReasons` | A histogram over the closed list of rejection reasons, sorted by reason. A run's rejections are comparable across runs rather than a log to read. |
31
+ | `inventedReferences` | Rejections that named something the repository does not contain. |
32
+ | `agentRuns`, `inputBytes`, `outputBytes` | What was actually sent and received. Batched packs, so a run is not a call count. |
33
+ | `cacheHits`, `cacheHitRate` | Hits, and hits over hits plus runs, rounded to six places. |
34
+ | `wallTimeMs` | Measured from a monotonic clock, not derived. `runEnrichment` takes a `clock` option so a test can pin it. |
35
+ | `expired` | Entries whose target content hash moved since they were accepted. |
36
+
37
+ `enrichmentCost(stats)` in `src/enrich/stats.ts` is the same numbers as an object for a reader who
38
+ only wants the bill; `ak-docs enrich --json` reports it under `cost`.
39
+
40
+ ### Invented references
41
+
42
+ `INVENTED_RELATION_REASONS` is `unknown-endpoint`, `unknown-entity`, `unknown-scope`,
43
+ `unknown-directory`, `unknown-diagnostic`. `inventedReferences` counts exactly those, and it is
44
+ reported next to the rejection total rather than folded into it.
45
+
46
+ The distinction is not cosmetic. A curator that classifies a document badly is wrong about a
47
+ judgement, and a validator rejecting it is the system working. A curator that proposes a relation
48
+ to a module the repository does not contain is making things up, and that number must trend to
49
+ zero or the agent is unusable. Folding the two together hides the second inside the first.
50
+
51
+ A schema failure or an unknown kind is a rejection and not an invention: `invalid-kind` is not in
52
+ the list, and a test asserts the counts stay apart.
53
+
54
+ ### Stability
55
+
56
+ `enrichmentStability(current, previous)` compares a run with the one before it and reports both
57
+ halves of the question at once:
58
+
59
+ - `overlayHashIdentical` — a deterministic agent over an unchanged repository must reach this. The
60
+ overlay hash covers decisions, not `acceptedAt` and not `stats`, so a second run that is answered
61
+ entirely from the cache produces the same hash as the first.
62
+ - `proposalIdShare` — for a live model, which will not reach an identical hash, the share of
63
+ proposal identifiers present in both runs. The share is over the **union** of the two runs'
64
+ identifiers, so a run that merely proposes fewer things does not score as more stable.
65
+
66
+ `sharedProposalIds`, `proposalIds` and `previousProposalIds` are reported next to the share, because
67
+ a share of 0.5 over two identifiers and over two hundred are different facts.
68
+
69
+ ## The retrieval delta
70
+
71
+ `measureOverlayRetrievalDelta` in `src/bench/overlay-delta.ts` answers the question the rest of the
72
+ statistics cannot: did the overlay make retrieval better?
73
+
74
+ It runs the golden suite twice over **one snapshot** — once with the accepted overlay projected,
75
+ once with `overlay: 'ignore'` — so the only difference between the two runs is the overlay. Not a
76
+ re-scan, not a different revision, not a different configuration. Neither half needs an index on
77
+ disk: both are projected from the snapshot the caller passes.
78
+
79
+ The rule is asymmetric on purpose. An overlay may leave retrieval unchanged and it may improve it,
80
+ but it must not lower `hitAt3` (`OVERLAY_BLOCKING_METRIC`): aliases, summaries and rank hints an
81
+ agent proposed exist to help an agent find things. A drop is reported as `regression: true` with
82
+ status `regressed`, and the caller exits non-zero. It is a finding about the agent, never a new
83
+ baseline.
84
+
85
+ The result carries both metric sets, a per-metric delta with `improved` and `worsened` (lower is
86
+ better for `meanContextBytes`, `meanApproxTokens` and `zeroResultRate`), and — the part that makes a
87
+ regression actionable — `lostCases` and `gainedCases`, the case ids that changed at hit@3.
88
+
89
+ ```bash
90
+ # The overlay on disk, against the configured golden suite.
91
+ ak-docs bench retrieval docs/bench/retrieval-suite-v1.json --overlay --text
92
+
93
+ # Or as part of the run that produced it.
94
+ ak-docs enrich --retrieval-delta --json
95
+ ```
96
+
97
+ Both exit 1 on a regression. Both report the delta in text and in JSON: `formatOverlayRetrievalDeltaText`
98
+ for a person, `retrievalDelta` (or `overlayDelta`) for a machine. The delta is opt-in because it runs
99
+ the suite twice, which is the right cost for an answer about whether the overlay helped and the wrong
100
+ cost for every routine run.
101
+
102
+ ## Study task expectations
103
+
104
+ The study's correction is the same idea one level up: state what retrieval is expected to return,
105
+ and check it deterministically.
106
+
107
+ A task in `docs/study/task-suite-v1.json` may now declare `expectedEntities`, `expectedDocuments`
108
+ and `retrievalQueries`. The references are **opaque** — `primary-entrypoint`, not `docs/alpha.md` —
109
+ because the task suite is publication-bound and a repository path in it is a privacy failure, not a
110
+ convenience. A query with nothing expected is refused by the schema: it would check nothing.
111
+
112
+ The resolution lives in a second artifact, `StudyExpectationsV1` in `src/study/expectations.ts`,
113
+ which declares `scope: 'local'` and binds to the suite by `taskSuiteHash`. It maps each reference to
114
+ the concrete entity ids or document paths it stands for in one repository on the operator's disk.
115
+ It is never published; putting it under `docs/study/` would fail the privacy gate, which is the
116
+ intended outcome rather than a bug. References resolved against a suite that has moved on are
117
+ refused outright.
118
+
119
+ `studyRetrievalSuite` turns the pair into an ordinary Open Eval Format suite — one case per task
120
+ query, with the resolved targets as `expectedTargets` and the task, repository and category carried
121
+ in metadata — so the same command, the same ranking and the same metrics that gate this
122
+ repository's retrieval answer the study's mechanical questions. `checkStudyExpectations` runs it:
123
+
124
+ ```bash
125
+ ak-docs study expectations docs/study/task-suite-v1.json \
126
+ --expectations ./local-study-expectations.json --index .doc-bridge/index.json --text
127
+ ```
128
+
129
+ `ok` requires that every reference resolved **and** every case hit. Two failures are reported rather
130
+ than smoothed over: a task that declares no expectations is listed under `withoutExpectations` as
131
+ unchecked, never counted as a pass, and an unresolved reference fails the check, because an
132
+ expectation nobody resolved is an expectation nobody tested — which is exactly the failure this
133
+ replaces. The command exits 1 when the check does not pass.
134
+
135
+ `validateStudyTaskSuite(suite, { requireExpectations: true })` names the tasks that cannot be checked
136
+ mechanically. It is off by default: a suite written before expectations existed is still a valid
137
+ suite.
138
+
139
+ ### Where the model adjudicator stops
140
+
141
+ A rubric item may now be prose, or prose with the mechanical check that decides it
142
+ (`acceptance-checks`, `evidence-coverage`, `retrieval-expectations`). `mechanicalRubricItems` and
143
+ `modelRubricItems` split the rubric on that field, and `adjudicatorRubric(task)` is what the
144
+ adjudicator input carries: `rubric` holds only the items no checker can settle, and the settled ones
145
+ travel next to them as `mechanical` — verdicts, for context, not for review. The instruction says so
146
+ in the same words.
147
+
148
+ Handing a model an item the runner already measured invites it to disagree with a measurement, which
149
+ is how a study ends up with an opinion where it had a number.
150
+
151
+ ## The assisted arm
152
+
153
+ `registry-assisted` has been reserved since the first suite and has never executed. It now reports
154
+ its own readiness, and `assistedArmReadiness(plan, providers, suite)` is the only thing that decides
155
+ whether it runs:
156
+
157
+ | Status | When | Effect |
158
+ | --- | --- | --- |
159
+ | `unavailable` | No assisted scenario in the plan; the scenario names no agent identity and version; no provider CLI for the scenario and a model the suite uses | Its executions are recorded as unavailable observations |
160
+ | `ready` with `undeclared` | The scenario declares no `promptVersion`, or no `agentBudget` | The arm runs; the run says what it could not name |
161
+ | `ready` | Everything declared | The arm runs |
162
+
163
+ An unavailable arm is **recorded, not skipped**: a scenario absent from a ledger is
164
+ indistinguishable from one that was never planned, and comparing the arms it planned is the study's
165
+ whole purpose. Each such execution lands in the ledger with `execution.status: 'unavailable'`,
166
+ `errorCode: 'registry-unavailable'`, and an automated adjudication of `blocked` carrying the reason.
167
+ A missing Registry is a fact about the environment, not a reason to lose the other two arms —
168
+ `assertRunInputs` skips provider validation for those executions so the rest of the run proceeds.
169
+
170
+ A missing declaration is the other way round: losing the third arm over an undeclared prompt version
171
+ would be worse than running it without one, so the arm runs and `undeclared` says what a reader
172
+ cannot reconstruct from the ledger — the prompt it used, or the agent's cost apart from the model's.
173
+ The run summary prints the status, the reason and the undeclared fields.
174
+
175
+ A run plan may declare `promptVersion` and `agentBudget` only on `registry-assisted`: the other two
176
+ arms have no agent to budget.
177
+
178
+ ## Tokens to first evidence
179
+
180
+ The parent PRD's second claim is that an agent reaches correct grounded evidence for fewer tokens
181
+ than by reading the repository. Total tokens at the end of a task does not test that claim: a run
182
+ that wandered for ten thousand tokens and then found the answer looks the same as one that landed on
183
+ it immediately.
184
+
185
+ `tokensToFirstEvidence` is a canonical provider measurement — tokens consumed before correct
186
+ grounded evidence was in hand — and the metrics report its p95 per scenario as
187
+ `tokensToFirstEvidenceP95`, rounded up to whole tokens. It is in the improvement list, so a round
188
+ that reaches evidence sooner reads as an improvement, and absent or partial coverage is reported as
189
+ `tokensToFirstEvidence` or `tokensToFirstEvidence-partial` under `missingMetrics` rather than as a
190
+ zero.
191
+
192
+ The assisted arm's enrichment agent is costed apart from the model: `registryAgentInputTokens`,
193
+ `registryAgentOutputTokens`, `registryAgentCostUsd` and `registryAgentRuns`. `registryAgentCostUsd`
194
+ is added into `totalCostUsd`, so the arm cannot look cheap by charging its work to a line nobody
195
+ adds up, and `missingMetrics` names it when an assisted observation reports no agent cost at all.
196
+ The scenario line in `--text` prints the p95 next to the agent cost and its run count.
197
+
198
+ ## Invariants
199
+
200
+ - `stats` is outside the overlay's content hash. Cost and timing may differ between two runs that
201
+ decided identically.
202
+ - `inventedReferences` counts only `INVENTED_RELATION_REASONS`, and is reported separately from the
203
+ rejection total.
204
+ - `proposalIdShare` is over the union of both runs' identifiers.
205
+ - The retrieval delta builds both indexes from one snapshot. An overlay on disk cannot leak into the
206
+ baseline half.
207
+ - An overlay that lowers hit@3 is a regression with a non-zero exit, in every mode.
208
+ - A study task's expectations are opaque references. A path or a URL in a publication-bound artifact
209
+ is a privacy failure, and the privacy gate is what says so.
210
+ - An unresolved reference fails the check; a task without expectations is reported unchecked and is
211
+ never a pass.
212
+ - A rubric item with a mechanical check never reaches the model adjudicator.
213
+ - An unavailable assisted arm is recorded as unavailable observations, and never fails the study.
214
+
215
+ ## Deviation: the committed study suite
216
+
217
+ The mechanism above is delivered and tested; the committed 24-task suite, run plan and ledgers are
218
+ **not** rewritten to use it.
219
+
220
+ Two reasons, both about not making the artifacts worse. The suite's content hash is bound to
221
+ published artifacts — the run plan's `taskSuiteHash`, the observation ledgers, the verification
222
+ binding — and changing it invalidates every one of them. And the concrete targets an expectations
223
+ file would resolve belong to the six study repositories, which are not present in this repository,
224
+ so the references could only be guessed.
225
+
226
+ An operator adding expectations to a round therefore does three things: add `expectedEntities`,
227
+ `expectedDocuments` and `retrievalQueries` to the tasks, re-seal the suite, and write the local
228
+ expectations file that resolves the references against their checkouts. `ak-docs study expectations`
229
+ then reports which tasks can be answered mechanically and which cannot.
@@ -0,0 +1,119 @@
1
+ ---
2
+ title: Public parity v1
3
+ description: A registry of the claims this repository makes in public, the repository facts they stand for, and the gate that fails when a public surface states something the repository has moved past.
4
+ ---
5
+
6
+ # Public parity v1
7
+
8
+ Documentation drifts silently, and the drift reaches readers before it reaches anyone who could fix it.
9
+
10
+ Four figures from the published A/B round appear in `README.md` and again in `docs/study/README.md`.
11
+ They agree today. Nothing made them agree: an edit to one of them, or a new round replacing the
12
+ artifact both quote, would leave two public surfaces stating a number the repository no longer
13
+ measures — and the only mechanism for noticing was a person happening to read both pages on the same
14
+ day.
15
+
16
+ `ak-docs parity` is that mechanism. A registry names each public claim and the repository fact it
17
+ stands for; the checker resolves the fact, finds the claim's occurrences, and reports the difference
18
+ with a file, a line, an owner and a remediation.
19
+
20
+ ## The registry
21
+
22
+ `docs/parity/public-claims-v1.json` is a sealed artifact — `createPublicClaims` computes its content
23
+ hash, `parsePublicClaims` refuses a tampered one — holding claims and exceptions.
24
+
25
+ | Field | Meaning |
26
+ | --- | --- |
27
+ | `claimId` | A slug. Stable across rounds, because findings are keyed on it. |
28
+ | `statement` | What the claim asserts, for a reader of the report. Never a path or a URL. |
29
+ | `owner` | Who answers for it: an ownership id, a team, a handle. Every finding carries it. |
30
+ | `valueType` | `number`, `percent`, `semver` or `text`. Decides the capture. |
31
+ | `template` | How the value appears in prose, with exactly one `{value}`. Omitted for a presence claim. |
32
+ | `templates` | Per-surface wording, when one fact is stated differently in different places. |
33
+ | `evidence` | Where the canonical value comes from. Below. |
34
+ | `required` | Surfaces that must state the claim. One that omits it is a `missing` finding. |
35
+ | `optional` | Surfaces that may. Checked when present, never required. |
36
+ | `severity` | `error` blocks the gate; `warn` is reported and does not. |
37
+ | `remediation` | What to do about a finding. Written once, in the registry, not per finding. |
38
+
39
+ ### Evidence
40
+
41
+ Every resolver is deterministic and local. Nothing reaches the network, and nothing asks a model.
42
+
43
+ | Kind | Resolves to |
44
+ | --- | --- |
45
+ | `package-field` | A scalar field in `package.json` — `version`, `description`. |
46
+ | `artifact-field` | A dotted path into a committed JSON artifact: `arms.1.completedRate`. |
47
+ | `artifact-sum` | The sum of one numeric field across an array in an artifact: prose states a total where the artifact stores the parts. |
48
+ | `snapshot-count` | Entities of one kind in the snapshot this run produced. |
49
+ | `doctor-metric` | A figure the doctor measured: the grade, the score, reachability, connectivity, hit@3, agent-doc coverage. |
50
+ | `cli-command` | Whether the CLI's own usage offers a command. A presence claim: no template, no value to read. |
51
+
52
+ A numeric value can be rendered the way prose states it: `round`, `percent-1dp`, `percent-0dp`, and
53
+ two signed transforms. `negative-percent-2dp` and `negative-seconds-2dp` render the magnitude of a
54
+ negative measurement — "18.46% fewer", "39.75 seconds lower" — and **refuse to render a positive
55
+ one**. The direction of those claims lives in a word the checker cannot read, so a measurement that
56
+ turns stops resolving instead of matching the same digits for the opposite result.
57
+
58
+ ## The four outcomes
59
+
60
+ They are kept apart because they need different actions.
61
+
62
+ | Code | Meaning | Blocks |
63
+ | --- | --- | --- |
64
+ | `PARITY_STALE` | A surface states a value the repository has moved past. | At `error` |
65
+ | `PARITY_MISSING` | A required surface does not state the claim at all. | At `error` |
66
+ | `PARITY_CONTRADICTION` | Two public surfaces state different values for one claim. | Always |
67
+ | `PARITY_NOT_ANALYZED` | The canonical value could not be resolved. | Never |
68
+
69
+ A contradiction is decided over the whole claim rather than per surface: two pages disagreeing is a
70
+ finding even when neither matches the repository, and it is the one an agent reading the
71
+ documentation cannot resolve on its own.
72
+
73
+ `PARITY_NOT_ANALYZED` is reported and never counted as a pass. A claim nobody could check is not a
74
+ claim anybody verified, and passing it quietly is how a parity report becomes decoration.
75
+
76
+ ## Exceptions
77
+
78
+ An exception accepts a finding for one claim on one surface, and it must carry a `reason` of at
79
+ least eight characters and an `acceptedBy`. The finding still appears in the report, marked
80
+ `accepted` with its reason, and stops blocking. There is no way to silence a claim without saying
81
+ why in the artifact — silence is what drift needs.
82
+
83
+ ## The gate
84
+
85
+ ```bash
86
+ ak-docs parity # JSON: { ok, parity }
87
+ ak-docs parity --text # one line per finding, with its remediation
88
+ ak-docs parity --claims <file> # a registry somewhere else
89
+ ```
90
+
91
+ Exit 1 when any finding blocks, 0 otherwise, 2 on a broken registry or an unreadable surface. CI runs
92
+ it in the dogfood step, next to the index, the gate, the doctor and the retrieval benchmark, so a
93
+ pull request that edits a public number and not its siblings fails before review.
94
+
95
+ The doctor is measured only when a claim asks for one of its figures: that costs an index and a
96
+ benchmark run, and most registries never need it.
97
+
98
+ ## Publication safety
99
+
100
+ The report is publication-safe by construction. It carries claim ids, repository-relative surface
101
+ paths, line numbers, the stated and canonical values, and a bounded excerpt of the matching line —
102
+ at most 160 characters, with secrets redacted through the same scanner the enrichment validators
103
+ use. It never carries a document's contents and never an absolute path: the registry's surface
104
+ paths are validated as repository-relative, so an operator's home directory cannot reach an
105
+ artifact that is meant to be shareable. A test asserts all of it against a fixture whose README
106
+ contains a secret-shaped string and a four-hundred-character line.
107
+
108
+ ## Invariants
109
+
110
+ - The registry is sealed; a tampered hash is refused before anything is checked.
111
+ - A template's literal halves are escaped, so a registry cannot smuggle a pattern into the checker,
112
+ and `{value}` becomes one bounded capture: no nesting, no ambiguity, nothing to backtrack over.
113
+ - A claim that states a value has a template; a presence claim has none. The schema enforces both.
114
+ - An exception needs a reason and an approver, and applies to one claim on one surface.
115
+ - A signed transform refuses a value whose sign no longer matches the prose.
116
+ - Two runs over one unchanged repository produce the same report content hash: findings are sorted,
117
+ and nothing in the report is a timestamp.
118
+ - `missingSurfaces` names a surface the registry addresses that the checkout does not contain, so a
119
+ claim cannot pass by pointing at nothing.
@@ -68,3 +68,9 @@ mergePolicy:
68
68
  - `knowledge-promoter` may draft PR content, but must never merge.
69
69
  - `code-review` runs after gates so reviewers see deterministic failures first.
70
70
  - Future RAG mode should inject `createDocBridgeRetriever(index)` and keep exact handoff resolution ahead of semantic results.
71
+
72
+ ## Grounding and approval boundary
73
+
74
+ The adapter accepts only a typed `AgentProposalV1` whose base snapshot and report hashes match the supplied artifacts. A proposal must identify the configured Registry agent and exact installed version, reference known diagnostics, and include evidence present in the supplied snapshot or reconciliation report. When `ak-docs suggest --documentation` is used, the bounded documentation-audit context is also supplied; the proposal must bind `baseDocumentationAuditHash` and may reference audit finding IDs and evidence. Unknown diagnostics, out-of-scope evidence, malformed output, timeout, response limits, and token limits fail closed.
75
+
76
+ The adapter returns advisory evidence only. It does not apply documentation changes, mark findings resolved, or approve its own output. Convert an accepted suggestion into the existing human-gated fix-proposal flow, run post-apply verification, and treat the new source revision as a new evidence run. An alternate Registry agent is selected by changing `intelligence.registry.agentId` and installing matching metadata under `agentRoot`; the common adapter and evidence contract remain unchanged.
@@ -0,0 +1,122 @@
1
+ ---
2
+ title: Render v1
3
+ description: The canonical artifacts rendered as Markdown people can read, from templates a project can replace.
4
+ ---
5
+
6
+ # Render v1
7
+
8
+ Doc Bridge writes JSON for agents and HTML for browsing. `ak-docs render` writes the Markdown for
9
+ people: the same artifacts, through templates rather than string concatenation, so a project can
10
+ change the house style without a code change and every rendering is a function of its inputs.
11
+
12
+ ```bash
13
+ ak-docs render llms.txt
14
+ ak-docs render area --output docs/areas
15
+ ak-docs render ownership --output docs/agent-corpus/generated
16
+ ak-docs render change-digest --data .doc-bridge/previous-snapshot.json
17
+ ak-docs render overlay-review --output review.md
18
+ ak-docs render area --print-template > templates/area.md
19
+ ```
20
+
21
+ Rendering never calls an agent and never reads the Registry. Its inputs are the index, built in
22
+ memory from the working tree or read from the artifact `--data` names, the workflow's last
23
+ reconciliation report and snapshot, and an overlay file. Lists are sorted, nothing carries a
24
+ timestamp, and equal inputs render to equal bytes — every bundled template has a golden file, and
25
+ a test renders the same data twice.
26
+
27
+ ## Templates
28
+
29
+ | Name | Renders | `--data` |
30
+ | --- | --- | --- |
31
+ | `llms.txt` | the curated reading order for agents, exactly as `ak-docs index` writes it | a `DocBridgeIndex` |
32
+ | `area` | one page per code area: purpose, modules, documents, related areas, checks, open findings | a `DocBridgeIndex` |
33
+ | `ownership` | one sidecar per ownership record: start page, what to read, edit roots, checks, related areas | a `DocBridgeIndex` |
34
+ | `change-digest` | entities and documents whose content hash moved since the last scan, and the documents that should have moved with them | the previous snapshot |
35
+ | `overlay-review` | pending agent proposals with their evidence links, for a human to judge | an enrichment overlay |
36
+
37
+ Without `--output`, the pages go to standard output; `--json` wraps them as `{ template, source,
38
+ pages: [{ path, content }] }`. With `--output`, a single page is written to that path and a
39
+ multi-page template under it as a directory; the command prints what it wrote.
40
+
41
+ Templates use [knap](https://github.com/obsidianmd/knap) syntax: `{{ variable }}`, `{% if %}`,
42
+ `{% for item in list %}`, and knap's standard filters. They parse to an abstract syntax tree and
43
+ are interpreted without `eval`; the application computes every variable before rendering, and a
44
+ template cannot call anything. knap renders asynchronously and the index pipeline is synchronous,
45
+ so Doc Bridge walks knap's AST with a synchronous evaluator of its own; a test renders every
46
+ bundled template through knap's engine as well and holds the two to byte-identical output.
47
+
48
+ ## Overriding a template
49
+
50
+ ```json
51
+ {
52
+ "render": {
53
+ "templates": {
54
+ "area": "templates/area.md"
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ The path is relative to the project root. `ak-docs render <name> --print-template` prints the
61
+ bundled template to start from, and the variables each template sees are the exported view types
62
+ (`AreaPageView`, `OwnershipPageView`, `ChangeDigestView`, `OverlayReviewView`, `LlmsTxtVariables`).
63
+ An override replaces the bundled template entirely and is compiled by the same engine, so one that
64
+ does not parse fails before anything is written. The `llms.txt` override is also what
65
+ `ak-docs index` writes and what the documentation-standard profile re-renders to check freshness:
66
+ the two always agree.
67
+
68
+ ## Generated regions
69
+
70
+ Every Markdown page carries a marker around what the generator owns:
71
+
72
+ ```markdown
73
+ <!-- doc-bridge:generated hash=2dc9b98ec7b986b0 -->
74
+ …
75
+ <!-- /doc-bridge:generated -->
76
+ ```
77
+
78
+ A template places the markers with `{{ region.open }}` and `{{ region.close }}`; a template that
79
+ prints neither is wrapped whole. The hash is the first sixteen hex characters of the SHA-256 of
80
+ the lines between the markers, with line endings normalised, so an editor converting a file to
81
+ CRLF has not changed what the generator wrote.
82
+
83
+ The marker is what closes the loop with the rest of the pipeline. The Markdown analyzer skips
84
+ mentions and links inside a generated region (`docs/spec/markdown-analyzer-v1.md`), so Doc Bridge
85
+ never reads its own output back in as evidence about the repository. The documentation audit
86
+ recomputes the hash of every region and reports one that no longer matches its marker as
87
+ `GENERATED_REGION_EDITED` under `generated-freshness`, with the region's lines as evidence: a
88
+ manual edit inside a region is a finding, never something a regeneration silently discards. Text
89
+ outside the markers is a person's and is left alone.
90
+
91
+ `llms.txt` carries no marker. It is a whole-file artifact with consumers of its own — the
92
+ federation retriever and the freshness gate — and its bytes are unchanged from before.
93
+
94
+ ## The change digest
95
+
96
+ The digest compares two discovery snapshots by the content hash each file-backed entity carries
97
+ (`docs/spec/incremental-scan-v1.md`): an entity in both with a different hash is *changed*, one
98
+ only in the current snapshot is *added*, one only in the previous is *removed*. An entity without
99
+ a hash — an external package, an area — has nothing to move and is not listed.
100
+
101
+ "Documentation to review" answers which documents this change should have touched: every document
102
+ that covers, mentions, links to or references a symbol of something that moved, and did not move
103
+ itself. A document that changed alongside its subject is in *changed*, not there.
104
+
105
+ The previous snapshot is the one `--data` names, otherwise the last `ak-docs scan` (the
106
+ workflow's `normalize` output under `.doc-bridge/workflow`). The current one is a cold scan of the
107
+ working tree. Rendering does not move the baseline — only a scan does — so the digest can be
108
+ rendered as many times as a review needs. With no previous snapshot the command says so and
109
+ exits 2.
110
+
111
+ ## The overlay review page
112
+
113
+ The page reads an enrichment overlay's `pending` proposals — id, kind, entity, reason, confidence
114
+ and evidence locations — and renders each with links of the form `path#L10-L12`. It is
115
+ deliberately loose about the overlay's shape, which the enrichment workstream owns. With no
116
+ overlay it renders an explicit empty state rather than failing; `.doc-bridge/enrich/overlay.json`
117
+ is read when it exists, and `--data` names any other file.
118
+
119
+ ## Boundaries
120
+
121
+ Nothing under `src/render` imports anything under `src/agents`; a test walks the imports.
122
+ Rendering works with the Registry disabled, because it never consults it.
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: Retrieval index v1
3
+ description: The retrieval index as a projection of the snapshot, the ranking that reads it, and the handoffs derived from its graph.
4
+ ---
5
+
6
+ # Retrieval index v1
7
+
8
+ The retrieval index is what search ranks. It is a projection of the discovery snapshot — a pure
9
+ function of the snapshot, the accepted enrichment overlay and the effective configuration — and it
10
+ has no scanner of its own.
11
+
12
+ That sentence closes a gap that had been in the design since the first index. `buildDocBridgeIndex`
13
+ walked the repository a second time, parsed every module and document again, and produced records
14
+ that shared nothing with the snapshot but a file path. Two views of one repository, built by two
15
+ pipelines, could disagree; the index held eleven sidecars while the snapshot held hundreds of
16
+ entities. Now an entity retrieval can find is an entity discovery observed — same id, same content
17
+ hash, same evidence — and the routes the configuration declares (intents, changes, ownership) are
18
+ projected next to them.
19
+
20
+ ## The artifact
21
+
22
+ `RetrievalIndexV1` lives inside `DocBridgeIndexV1` as `projection`, so every reader of the index
23
+ receives it with the same freshness check. `knowledge[]` is still written for every reader that
24
+ predates it and now carries every projected document and module — without body text, which lives
25
+ once in the projection.
26
+
27
+ ```json
28
+ {
29
+ "type": "retrieval-index",
30
+ "schemaVersion": 1,
31
+ "contentHash": "…",
32
+ "snapshotHash": "…",
33
+ "overlayHash": "…",
34
+ "configurationHash": "…",
35
+ "lexiconVersion": 1,
36
+ "graphMetricsVersion": "1.0.0",
37
+ "weights": { "title": 4, "headings": 3, "symbols": 3, "path": 2, "aliases": 2, "summary": 2, "body": 1 },
38
+ "params": { "k1": 1.2, "b": 0.75 },
39
+ "lexical": { "version": 1, "documentCount": 364, "fieldNames": ["…"], "averageFieldLength": { "…": 0 } },
40
+ "entries": [
41
+ {
42
+ "id": "module:src/query/search.ts",
43
+ "kind": "module",
44
+ "path": "src/query/search.ts",
45
+ "title": "search.ts",
46
+ "aliases": [],
47
+ "symbols": ["searchIndex"],
48
+ "tags": ["module", "ts", "query"],
49
+ "fields": { "title": "search.ts", "headings": "", "path": "src/query/search.ts", "symbols": "searchIndex", "summary": "", "body": "", "aliases": "module ts query" },
50
+ "graph": { "pagerank": 0, "inboundLinks": 3, "coveredBy": ["document:docs/agent-corpus/query.md"], "mentionedBy": ["…"], "areaId": "area:src/query", "packageId": "package:@agentskit/doc-bridge", "inbound": [], "outbound": [] },
51
+ "contentHash": "…",
52
+ "provenance": "observed",
53
+ "confidence": "observed",
54
+ "ownershipId": "doc-bridge-query"
55
+ }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ `contentHash` is over the inputs — snapshot hash, overlay hash, configuration hash, lexicon and
61
+ graph-metrics versions, weights and parameters — because the projection is a function: equal
62
+ inputs, equal artifact. `IndexStaleError` keeps working from the same three hashes.
63
+
64
+ Every entry carries the entity's own `contentHash`, its `provenance`, and a `confidence`. An entry
65
+ kind is one of `document`, `module`, `area`, `package`, `intent`, `change`. An ownership record
66
+ attaches to the entity at its path and lends it its id as an alias; the unit then inherits its
67
+ agent document's title, headings, summary and body, because that document is the documentation of
68
+ that unit. A record whose path matches no entity is projected as a declared `package` entry, so a
69
+ query for it still has an answer.
70
+
71
+ The postings are not stored. `fields` already is the serialised index: tokenisation is versioned
72
+ (`lexiconVersion`) and deterministic, so the postings a reader rebuilds are the postings the writer
73
+ would have stored, and `lexical` records the shape of the collection so a reader can check it
74
+ rebuilt the same one.
75
+
76
+ The one thing read from disk is the body of a document the snapshot already names — bounded by the
77
+ same text budget as before — and the read is verified against the entity's content hash. A file
78
+ that changed since the scan is projected from what the snapshot recorded about it, not from what is
79
+ on disk now.
80
+
81
+ ## Ranking
82
+
83
+ `src/retrieval/rank.ts` replaces the internals of `searchIndex` and keeps its signature. The score
84
+ has named parts:
85
+
86
+ ```
87
+ score = lexical × prior
88
+ + exactId + exactPath + exactSymbol
89
+ + graphProximity + canonicality + audienceFit + acceptedAgentSignals
90
+ ```
91
+
92
+ - **lexical** — BM25 over the projected fields, with the weights above. Configurable under
93
+ `retrieval.weights`; recorded in the artifact.
94
+ - **prior** — the query-shape heuristics, as a multiplier: a curated (agent-audience) document, an
95
+ ownership record for a routing question, an intent or change route whose title covers the query,
96
+ a module for a symbol- or path-shaped query. A prior can only amplify evidence that exists.
97
+ - **exactId / exactPath / exactSymbol** — the query names the thing: an alias or id, a path or
98
+ filename, an exported symbol. Per-token identity applies to queries of at most two tokens; a
99
+ sentence does not name a thing by containing one of its tokens.
100
+ - **graphProximity** — within two hops of one of the ten strongest lexical hits, over covers,
101
+ mentions, links and imports. Scaled by the anchor's share of the best score, so a hub page that
102
+ barely matched cannot lift everything it links to.
103
+ - **canonicality** — log-scaled PageRank over `links-to` and `covers`: the page other pages point
104
+ at outranks the leaf that mentions the same thing. A tie-breaker among answers, never an answer:
105
+ it applies only to lexical hits.
106
+ - **audienceFit** — the `--agent` prior for documentation written for an agent.
107
+ - **acceptedAgentSignals** — the overlay hook, carried at zero weight until the overlay workstream
108
+ lands, so the code path and the explain view already exist.
109
+
110
+ A result that only a relation surfaced earns proximity and nothing else. Results below a third of
111
+ the best score are dropped; a caller assembling a neighbourhood rather than an answer passes
112
+ `floor: 0`.
113
+
114
+ **Confidence** on a result is the entry's own when the query matched it directly, and the weaker
115
+ of the entry and the surfacing relation when a relation alone surfaced it — a `fuzzy` mention makes
116
+ a `fuzzy` result. Every result carries evidence (the entity's path and content hash), provenance and
117
+ confidence.
118
+
119
+ `ak-docs search <term> --explain`, or `explain: true` over the API, attaches the matched terms and
120
+ fields and every component's contribution to each result. Explaining never changes the ranking.
121
+
122
+ ```
123
+ $ ak-docs search "workflow transitions persisted" --explain --text
124
+ [module] module:src/workflow/engine.ts score=115.27 confidence=observed
125
+ src/workflow/engine.ts
126
+ why: lexical=115.27
127
+ matched: aliases: workflow | path: workflow | symbols: workflow
128
+ ```
129
+
130
+ ## Handoffs for any entity
131
+
132
+ `handoffForEntity(index, id, config, { root })` in `src/query/handoff.ts` answers for a package, an
133
+ area, a module or a document — by entity id, ownership id, alias or path — and replaces
134
+ `handoffForPackage`. `runQuery` and MCP `handoff.resolve` go through it.
135
+
136
+ | Field | Derived from |
137
+ | --- | --- |
138
+ | `editRoots` | the area or package itself; a module's area; a document's own path |
139
+ | `startHere` | the ownership record's agent document, then documents that `cover` the target, then those that `mention` it, then one `links-to` hop from those, then the corpus index; within each tier the more canonical page first |
140
+ | `readBeforeEditing` | the next two, plus `AGENTS.md` |
141
+ | `checks` | an ownership override, then what the index recorded when it merged frontmatter, package scripts and defaults, then the package-manager default for the unit's package |
142
+ | `related` | the strongest importing and imported areas, with the import that proves each |
143
+ | `explain` | which relation produced each field |
144
+ | `evidence` | the target's path and content hash, and the documents behind `startHere` |
145
+ | `metadata` | `entityId`, `kind`, `checksSource`, `confidence`, `areaId`, `packageId` |
146
+
147
+ `AgentHandoffV1` stays byte-compatible: `related`, `explain`, `evidence` and `metadata` are optional
148
+ additions, and `target.type` gains `area` and `document`. A handoff written before they existed is
149
+ still a valid handoff.
150
+
151
+ ## The retriever
152
+
153
+ `createDocBridgeRetriever(index)` returns `RetrievedDocument[]` from `@agentskit/core` — content is
154
+ the projected title, summary and body, `metadata` carries `kind`, `path`, `evidence`, `explain` and
155
+ `confidence` — so `createHybridRetriever`, `createRerankedRetriever` and `formatRetrievedDocuments`
156
+ consume Doc Bridge with no adapter. The contract is mirrored in-repo (the core package is an optional
157
+ peer) and a test asserts assignability against the real package and runs the real hybrid retriever
158
+ over it. `retrieve('query', { limit })` still works.
159
+
160
+ ## Boundaries
161
+
162
+ Nothing reachable from `search`, `query` or the projection imports anything under `src/agents`; a
163
+ test walks the imports. The deterministic layer is complete on its own, and the enrichment stage
164
+ never sits on its path.