@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.
- package/CHANGELOG.md +471 -0
- package/CONTRIBUTING.md +29 -4
- package/README.md +87 -40
- package/SECURITY.md +7 -0
- package/action.yml +1 -1
- package/bin/ak-docs.js +2 -2
- package/bin/ak-verify.js +13 -7
- package/dist/cli/program.d.ts +3 -1
- package/dist/cli/program.js +15888 -6061
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +91 -9
- package/dist/config/index.js.map +1 -1
- package/dist/index-Beor6Yhi.d.ts +792 -0
- package/dist/index.d.ts +9979 -3257
- package/dist/index.js +15954 -5774
- package/dist/index.js.map +1 -1
- package/docs/MARKETPLACE.md +1 -1
- package/docs/PRD-documentation-efficiency-study.md +406 -0
- package/docs/PRD-knowledge-retrieval-and-enrichment.md +466 -0
- package/docs/RELEASE.md +22 -8
- package/docs/adr/0002-documentation-audit-boundary.md +22 -0
- package/docs/adr/0003-study-protocol-and-historical-evidence.md +40 -0
- package/docs/adr/0004-controlled-study-runner.md +25 -0
- package/docs/adr/0005-documentation-quality-and-criticality.md +20 -0
- package/docs/adr/0006-registry-semantic-grounding.md +20 -0
- package/docs/adr/0007-longitudinal-study-metrics.md +21 -0
- package/docs/adr/0008-study-verification-boundary.md +21 -0
- package/docs/adr/0009-study-provider-cli-adapter.md +25 -0
- package/docs/agent-corpus/INDEX.md +14 -3
- package/docs/agent-corpus/OVERVIEW.md +25 -0
- package/docs/agent-corpus/chat.md +7 -3
- package/docs/agent-corpus/cli.md +18 -2
- package/docs/agent-corpus/conformance.md +14 -2
- package/docs/agent-corpus/doc-bridge.md +48 -1
- package/docs/agent-corpus/doctor.md +10 -2
- package/docs/agent-corpus/gates.md +6 -2
- package/docs/agent-corpus/mcp.md +15 -2
- package/docs/agent-corpus/memory.md +6 -2
- package/docs/agent-corpus/query.md +35 -2
- package/docs/bench/README.md +122 -0
- package/docs/bench/retrieval-baseline-v1.json +28 -0
- package/docs/bench/retrieval-suite-v1.json +1033 -0
- package/docs/chat-and-rag.md +3 -2
- package/docs/for-agents.md +9 -1
- package/docs/getting-started.md +4 -11
- package/docs/guides/gate-ci.md +11 -1
- package/docs/guides/install-and-run.md +9 -65
- package/docs/index.md +22 -1
- package/docs/knowledge-engine-runbook.md +51 -4
- package/docs/landing/assets/context-payload-reduction.svg +21 -0
- package/docs/landing/assets/controlled-ab-comparison.svg +30 -0
- package/docs/landing/index.html +119 -5
- package/docs/loop-workflow.md +117 -0
- package/docs/mcp.md +6 -1
- package/docs/parity/public-claims-v1.json +145 -0
- package/docs/playbook/doc-bridge-pattern.md +1 -1
- package/docs/query.md +90 -2
- package/docs/recipes/index-pipeline.md +1 -1
- package/docs/schemas/agent-handoff-v1.md +15 -0
- package/docs/schemas/doc-bridge-index-v1.md +65 -0
- package/docs/spec/benchmark-v1.md +39 -1
- package/docs/spec/cli.md +30 -10
- package/docs/spec/config-v1.md +192 -8
- package/docs/spec/documentation-audit-v1.md +61 -0
- package/docs/spec/enrichment-overlay-v1.md +241 -0
- package/docs/spec/graph-signals-v1.md +92 -0
- package/docs/spec/incremental-scan-v1.md +102 -0
- package/docs/spec/markdown-analyzer-v1.md +73 -0
- package/docs/spec/mcp-knowledge-tools-v1.md +147 -0
- package/docs/spec/measured-enrichment-v1.md +229 -0
- package/docs/spec/public-parity-v1.md +119 -0
- package/docs/spec/registry-agents.md +6 -0
- package/docs/spec/render-v1.md +122 -0
- package/docs/spec/retrieval-index-v1.md +164 -0
- package/docs/spec/study-metrics-v1.md +58 -0
- package/docs/spec/study-protocol-v1.md +46 -0
- package/docs/spec/study-provider-cli-v1.md +116 -0
- package/docs/spec/study-runner-v1.md +35 -0
- package/docs/spec/study-task-suite-v1.md +41 -0
- package/docs/spec/study-verification-v1.md +40 -0
- package/docs/study/README.md +84 -0
- package/docs/study/ab-adjudicated-cost-analysis-v1.md +29 -0
- package/docs/study/ab-adjudicated-cost-plan-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-plan-v2-v1.json +33 -0
- package/docs/study/ab-adjudicated-cost-result-v1.json +80 -0
- package/docs/study/ab-baseline-analysis-v1.md +21 -0
- package/docs/study/ab-baseline-plan-v1.json +33 -0
- package/docs/study/ab-baseline-recovery-plan-v1.json +33 -0
- package/docs/study/ab-baseline-result-v1.json +79 -0
- package/docs/study/documentation-audit-round-2026-08-31.json +183 -0
- package/docs/study/historical-evidence-v1.json +252 -0
- package/docs/study/observation-ledger-v1.json +30632 -0
- package/docs/study/phase3-task-coverage-v1.json +34 -0
- package/docs/study/phase4-public-pilot-ledger-v1.json +1344 -0
- package/docs/study/phase4-public-pilot-result-v1.json +52 -0
- package/docs/study/phase4-public-pilot-run-plan-v1.json +26 -0
- package/docs/study/phase4-public-pilot-task-suite-v1.json +71 -0
- package/docs/study/pilot-round-2026-08-31.json +46 -0
- package/docs/study/protocol-v1.json +90 -0
- package/docs/study/publication-gate-v1.md +45 -0
- package/docs/study/quality-scorecard-cycle-plan.md +545 -0
- package/docs/study/quality-scorecard-v1.json +38 -0
- package/docs/study/round-1-adjudicated-smoke-v1.json +30642 -0
- package/docs/study/round-1-instrumentation-plan-v1.md +39 -0
- package/docs/study/round-2-expanded-adjudication-v1.json +91 -0
- package/docs/study/round-2-expanded-validation-v1.md +58 -0
- package/docs/study/round-3-evidence-contract-v1.json +75 -0
- package/docs/study/round-3-evidence-contract-v1.md +57 -0
- package/docs/study/round-4-confirmation-v1.json +75 -0
- package/docs/study/round-4-confirmation-v1.md +55 -0
- package/docs/study/run-plan-v1.json +33 -0
- package/docs/study/semantic-adjudication-cycle-8.md +20 -0
- package/docs/study/task-suite-v1.json +96 -0
- package/docs/study/token-efficiency-plan-v1.md +337 -0
- package/docs/study/token-efficiency-protocol-v2.json +62 -0
- package/docs/study/verification-binding-v1.json +27 -0
- package/docs/validation-cycle-plan.md +33 -0
- package/docs/verification-harness.md +15 -6
- package/ecosystem-claims.json +2 -2
- package/ecosystem-upstream.json +2 -2
- package/ecosystem.json +4 -4
- package/mcpb/manifest.json +9 -1
- package/package.json +89 -72
- package/scripts/check-ecosystem-upstream.mjs +36 -7
- package/scripts/report-visual-check.mjs +20 -3
- package/skills/doc-bridge-handoff/fixtures/synthetic-repo/docs/for-agents/packages/payments.md +7 -0
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +192 -24
- package/src/audit/documentation.ts +513 -0
- package/src/bench/baseline.ts +198 -0
- package/src/bench/overlay-delta.ts +139 -0
- package/src/bench/retrieval.ts +319 -0
- package/src/budget/compile.ts +91 -0
- package/src/budget/sections.ts +70 -0
- package/src/cli/demo.ts +2 -2
- package/src/cli/program.ts +699 -79
- package/src/cli/usage.ts +71 -0
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +4 -0
- package/src/config/load-config.ts +7 -1
- package/src/config/schema.ts +121 -4
- package/src/conformance/documentation-standard-v1.ts +22 -14
- package/src/discovery/areas.ts +182 -0
- package/src/discovery/documentation.ts +255 -23
- package/src/discovery/identity.ts +24 -0
- package/src/discovery/incremental.ts +314 -0
- package/src/discovery/inputs.ts +110 -0
- package/src/discovery/markdown.ts +481 -0
- package/src/discovery/repository.ts +557 -125
- package/src/doctor/run-doctor.ts +246 -27
- package/src/enrich/approvals.ts +190 -0
- package/src/enrich/cache.ts +93 -0
- package/src/enrich/context-pack.ts +272 -0
- package/src/enrich/overlay.ts +255 -0
- package/src/enrich/review.ts +106 -0
- package/src/enrich/stage.ts +374 -0
- package/src/enrich/stats.ts +100 -0
- package/src/enrich/validate.ts +410 -0
- package/src/federation/llms.ts +74 -24
- package/src/findings/report.ts +103 -0
- package/src/fixes/proposals.ts +4 -3
- package/src/graph/build.ts +356 -0
- package/src/graph/memory.ts +208 -0
- package/src/index-builder/build-handoffs.ts +22 -11
- package/src/index-builder/build-index.ts +132 -3
- package/src/index-builder/human-adapters/fumadocs.ts +1 -1
- package/src/index-builder/llms-txt.ts +48 -8
- package/src/index-builder/project-corpus.ts +111 -0
- package/src/index-builder/watch-index.ts +1 -1
- package/src/index.ts +630 -2
- package/src/lib/bounded-text.ts +15 -10
- package/src/lib/fuzzy-match.ts +235 -0
- package/src/mcp/knowledge.ts +554 -0
- package/src/mcp/server.ts +113 -18
- package/src/metrics/benchmark.ts +21 -0
- package/src/parity/check.ts +309 -0
- package/src/parity/claims.ts +259 -0
- package/src/parity/resolve.ts +160 -0
- package/src/query/handoff.ts +326 -0
- package/src/query/load-index.ts +53 -1
- package/src/query/query.ts +92 -59
- package/src/query/search.ts +289 -92
- package/src/query/text.ts +155 -0
- package/src/reconciliation/reconcile.ts +148 -15
- package/src/render/data.ts +356 -0
- package/src/render/engine.ts +398 -0
- package/src/render/generated.ts +77 -0
- package/src/render/render.ts +209 -0
- package/src/render/template-source.ts +52 -0
- package/src/render/templates.ts +289 -0
- package/src/report/html.ts +23 -17
- package/src/retrieval/bm25.ts +161 -0
- package/src/retrieval/project.ts +495 -0
- package/src/retrieval/rank.ts +383 -0
- package/src/retrieval/weights.ts +39 -0
- package/src/retriever/doc-bridge-retriever.ts +100 -15
- package/src/rules/engine.ts +45 -12
- package/src/safety/repository.ts +1 -1
- package/src/schemas/agent-handoff.ts +56 -0
- package/src/schemas/budget.ts +37 -0
- package/src/schemas/doc-bridge-index.ts +53 -2
- package/src/schemas/enrichment.ts +369 -0
- package/src/schemas/json-schemas.ts +39 -2
- package/src/schemas/knowledge.ts +19 -3
- package/src/schemas/retrieval-index.ts +152 -0
- package/src/shims/graphology.d.ts +91 -0
- package/src/study/adjudication.ts +196 -0
- package/src/study/execution.ts +350 -0
- package/src/study/expectations.ts +219 -0
- package/src/study/metrics.ts +467 -0
- package/src/study/protocol.ts +271 -0
- package/src/study/provider-cli.ts +115 -0
- package/src/study/provider-telemetry.ts +47 -0
- package/src/study/quality-scorecard.ts +164 -0
- package/src/study/runner.ts +461 -0
- package/src/study/task-suite.ts +321 -0
- package/src/study/verification.ts +134 -0
- package/src/validate.ts +8 -5
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +36 -11
- package/dist/index-C2PCQSrB.d.ts +0 -2251
- 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.
|