@gmickel/gno 2.7.1 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +3 -2
  2. package/assets/skill/SKILL.md +11 -1
  3. package/assets/skill/cli-reference.md +8 -1
  4. package/assets/skill/examples.md +2 -1
  5. package/assets/skill/mcp-reference.md +3 -1
  6. package/assets/skill/recipes/memory-scoped-recall.md +10 -5
  7. package/assets/spa-production.json.gz +0 -0
  8. package/browser-extension/artifacts/{gno-browser-clipper-v2.7.1.zip → gno-browser-clipper-v2.8.1.zip} +0 -0
  9. package/browser-extension/artifacts/gno-browser-clipper-v2.8.1.zip.sha256 +1 -0
  10. package/browser-extension/dist/manifest.json +1 -1
  11. package/package.json +1 -1
  12. package/spec/cli.md +86 -9
  13. package/spec/db/schema.sql +0 -1
  14. package/spec/mcp.md +26 -7
  15. package/spec/output-schemas/audit-report.schema.json +18 -4
  16. package/spec/output-schemas/backlinks.schema.json +4 -0
  17. package/spec/output-schemas/collection-list.schema.json +13 -0
  18. package/spec/output-schemas/graph.schema.json +2 -0
  19. package/spec/output-schemas/links-list.schema.json +4 -0
  20. package/spec/output-schemas/memory-recall.schema.json +1 -1
  21. package/spec/output-schemas/status.schema.json +18 -3
  22. package/src/cli/commands/audit.ts +23 -4
  23. package/src/cli/commands/collection/list.ts +39 -5
  24. package/src/cli/commands/embed.ts +3 -3
  25. package/src/cli/commands/graph.ts +3 -1
  26. package/src/cli/commands/links.ts +61 -180
  27. package/src/cli/commands/shared.ts +7 -0
  28. package/src/cli/commands/status.ts +6 -0
  29. package/src/cli/program.ts +12 -2
  30. package/src/config/loader.ts +43 -0
  31. package/src/config/types.ts +8 -0
  32. package/src/core/audit-contract.ts +16 -4
  33. package/src/core/audit-freshness.ts +11 -1
  34. package/src/core/audit-links.ts +197 -25
  35. package/src/core/audit-outside-index.ts +215 -0
  36. package/src/core/audit-provenance.ts +11 -4
  37. package/src/core/audit-workspace.ts +30 -9
  38. package/src/core/audit.ts +76 -16
  39. package/src/core/context-compiler.ts +3 -0
  40. package/src/core/context-evidence.ts +11 -0
  41. package/src/core/graph-edge-confidence.ts +23 -1
  42. package/src/core/host-paths.ts +1 -0
  43. package/src/core/knowledge-impact.ts +28 -0
  44. package/src/core/link-inventory-markdown.ts +2 -3
  45. package/src/core/link-workspace.ts +324 -0
  46. package/src/core/links.ts +40 -17
  47. package/src/core/memory-recall.ts +254 -15
  48. package/src/core/memory-types.ts +12 -0
  49. package/src/core/memory.ts +2 -0
  50. package/src/core/retrieval-replay-candidate.ts +6 -0
  51. package/src/core/retrieval-trace-request.ts +3 -0
  52. package/src/index.ts +14 -1
  53. package/src/ingestion/graph-reconciliation.ts +77 -15
  54. package/src/ingestion/source-availability/darwin-path.ts +9 -3
  55. package/src/ingestion/sync.ts +27 -4
  56. package/src/ingestion/types.ts +14 -0
  57. package/src/llm/inference-scope.ts +4 -3
  58. package/src/mcp/http-egress.ts +42 -3
  59. package/src/mcp/tools/audit.ts +11 -2
  60. package/src/mcp/tools/changes.ts +1 -0
  61. package/src/mcp/tools/links.ts +74 -93
  62. package/src/mcp/tools/sessions.ts +33 -4
  63. package/src/mcp/tools/status.ts +4 -0
  64. package/src/pipeline/expansion.ts +19 -31
  65. package/src/pipeline/graph-retrieval.ts +22 -2
  66. package/src/pipeline/hybrid.ts +1 -1
  67. package/src/pipeline/search.ts +2 -0
  68. package/src/pipeline/types.ts +10 -3
  69. package/src/sdk/client.ts +1 -0
  70. package/src/serve/findings-pass.ts +1 -1
  71. package/src/serve/public/components/editor/MarkdownPreview.tsx +5 -3
  72. package/src/serve/public/pages/GraphView.tsx +2 -0
  73. package/src/serve/routes/changes.ts +6 -1
  74. package/src/serve/routes/graph.ts +3 -1
  75. package/src/serve/routes/links.ts +45 -50
  76. package/src/serve/routes/sessions.ts +41 -53
  77. package/src/serve/server.ts +2 -1
  78. package/src/serve/status.ts +1 -0
  79. package/src/sessions/config-refresh.ts +111 -0
  80. package/src/store/migrations/033-drop-documents-active-index.ts +30 -0
  81. package/src/store/migrations/034-collection-link-workspace.ts +47 -0
  82. package/src/store/migrations/index.ts +4 -0
  83. package/src/store/sqlite/adapter.ts +477 -337
  84. package/src/store/sqlite/eligibility.ts +8 -2
  85. package/src/store/sqlite/graph-link-resolver.ts +259 -5
  86. package/src/store/sqlite/graph-neighbors.ts +147 -40
  87. package/src/store/sqlite/graph-reference-state.ts +13 -2
  88. package/src/store/sqlite/graph-similarity.ts +96 -0
  89. package/src/store/sqlite/workspace-link-resolver.ts +742 -0
  90. package/src/store/types.ts +64 -5
  91. package/src/store/vector/stats.ts +1 -1
  92. package/src/store/vector/status.ts +27 -0
  93. package/src/store/vector/stored-vectors.ts +158 -0
  94. package/src/store/vector/types.ts +6 -0
  95. package/src/store/vector/variant-search.ts +30 -14
  96. package/browser-extension/artifacts/gno-browser-clipper-v2.7.1.zip.sha256 +0 -1
package/README.md CHANGED
@@ -139,7 +139,7 @@ See the [guide](docs/COMPILED-CONTEXT.md).
139
139
 
140
140
  <!-- public-truth:current-version -->
141
141
 
142
- > Current source version: **v2.7.1**. See [CHANGELOG.md](./CHANGELOG.md).
142
+ > Current source version: **v2.8.1**. See [CHANGELOG.md](./CHANGELOG.md).
143
143
 
144
144
  <!-- /public-truth -->
145
145
 
@@ -216,7 +216,8 @@ See the [guide](docs/COMPILED-CONTEXT.md).
216
216
  and traces. Authentication never overrides policy.
217
217
  - **Source availability (`any` | `local`)**: opt-in `local` indexes only files
218
218
  already on disk and never makes a cloud provider download one. Supported on
219
- macOS with Google Drive, iCloud Drive, and OneDrive SharePoint library roots.
219
+ macOS with Google Drive (My Drive and Shared drives), iCloud Drive, and
220
+ OneDrive SharePoint library roots.
220
221
  Cloud-only files are skipped and reported, not treated as conversion errors,
221
222
  and documents under a cloud-only folder stay indexed. Other platforms fail
222
223
  with an error. On a 5,000-file all-local collection, `local` scans about 1%
@@ -352,6 +352,15 @@ offline audits inspect parsed local links, explicitly declared capture/logical-
352
352
  record provenance, and observable source/index freshness. They never repair,
353
353
  rewrite, persist findings, judge factual truth, or replace retrieval.
354
354
 
355
+ `--max-findings` accepts `all` (MCP `maxFindings: "all"`) to export every
356
+ finding. Link findings carry `referenceKind`, `resolutionStatus`, and
357
+ `resolvedScope` in their evidence detail; ambiguous vault links list the tied
358
+ `candidates`. `links.outside-index` `info` findings are link targets that
359
+ exist in the vault but are not indexed (attachments, excluded or unindexed
360
+ folders); Obsidian resolves them, so do not report them as broken links.
361
+ Report `truncation.snapshotTruncated` as "totals cover the bounded snapshot,
362
+ not the whole index".
363
+
355
364
  Treat exit `4` as a complete report with findings. Exit `5` or report status
356
365
  `partial`/`changed_during_audit` means evidence is unavailable, inconclusive,
357
366
  cancelled, truncated, or repeatedly changed—never healthy. Preserve stable
@@ -389,7 +398,7 @@ remaining steps apply under the default `full` profile.
389
398
  - MCP text is the compact `gno-context-agent-v1` evidence projection. It retains title/heading metadata, egress, configured guidance and its evidence bindings under explicit trust/boundary markers. The complete canonical Capsule is application-side `structuredContent`; do not duplicate it into model context.
390
399
  3. Use `gno_ask` only for explicit local verified synthesis. Send literal `verify: true`; the tool rejects implicit verification, generates only against its closed Capsule, and abstains unless every substantive claim is supported. Preserve exact spans, gaps, semantic capability state, and abstention. This does not guarantee corpus completeness or source truth.
391
400
  4. Use `gno_query` for interactive lookup or manual retrieval control. It returns snippets plus `uri`, `docid`, often `line`, and sometimes `context`. Treat `context` as user-configured guidance for interpreting that exact result; cite source content at the returned URI/lines, not the guidance itself. Bounded graph expansion is on by default; set `graph: false` or `noGraph: true` only for an explicit BM25/vector-only path.
392
- 5. Use graph/link expansion for relationship context: `gno_graph_query` for typed relationship traversal, `gno_graph_neighbors` for nearby documents, `gno_graph_path` for "how are X and Y connected?", `gno_links`/`gno_backlinks` for one-document link expansion, and `gno_similar` for semantic neighbors. Prefer explicit or typed edges over inferred, ambiguous, or similarity edges when confidence matters.
401
+ 5. Use graph/link expansion for relationship context: `gno_graph_query` for typed relationship traversal, `gno_graph_neighbors` for nearby documents, `gno_graph_path` for "how are X and Y connected?", `gno_links`/`gno_backlinks` for one-document link expansion, and `gno_similar` for semantic neighbors. Prefer explicit or typed edges over inferred, ambiguous, or similarity edges when confidence matters. Plain `[[Note]]` links resolve across collections of the same vault (link workspace), so backlinks and impact can name other collections; pass `collection`/`collections` to keep results inside the user's scope. Never tell users to rewrite links into `[[collection:Note]]` to make them resolve.
393
402
  6. Use `gno_query_diagnose` when a known target document should have appeared but did not; it reports BM25/vector/fusion/graph/rerank stage presence and filter state.
394
403
  7. Use `gno_get` with `fromLine`/`lineCount` for targeted reads, or `gno_multi_get` to batch top refs.
395
404
  8. Use `gno_section` only when you need a durable section locator or must re-resolve one after edits. Prefer search → `gno_get` for ordinary retrieval. `action=create` needs `ref` plus exactly one of `anchor`|`line`; `action=resolve` needs `ref` plus `target`. Cite or open content only for `exact`/`recovered` results, then follow the tool's ready-to-use `gno_get` guidance (`fromLine = lineStart`; `lineCount = lineEnd - lineStart + 1`). Never navigate or cite `ambiguous`/`stale`/`missing`.
@@ -418,6 +427,7 @@ changed source:
418
427
  gno changes --since 2026-07-20T00:00:00Z --json
419
428
  gno diff gno://notes/plan.md --json
420
429
  gno impact gno://notes/plan.md --max-depth 3 --json
430
+ gno impact gno://notes/plan.md --collection notes --json # stay in scope
421
431
  ```
422
432
 
423
433
  Treat cursors and change IDs as opaque. Journal results are bounded,
@@ -594,7 +594,8 @@ gno impact gno://notes/plan.md --max-depth 3 --json
594
594
  `changes` accepts an ISO time or opaque cursor and optional collection/limit.
595
595
  `diff` reports structural headings, links, and typed-relationship changes for
596
596
  one retained change. `impact` follows inbound evidence edges with explicit
597
- depth/node/edge/frontier/visited bounds. Expired journal history is reported,
597
+ depth/node/edge/frontier/visited bounds; repeat `-c, --collection <name>` to
598
+ keep the traversal (and every path step) inside those collections. Expired journal history is reported,
598
599
  not reconstructed.
599
600
 
600
601
  ## Private Retrieval Traces
@@ -679,6 +680,12 @@ gno backlinks <ref> [options]
679
680
  `--relation <type>` to query semantic typed edges instead of positional
680
681
  wiki/markdown links. Do not combine `--type` with `--edge-type`.
681
682
 
683
+ Plain `[[Note]]` and `[[Folder/Note]]` links resolve across all collections
684
+ that share one vault (the nearest `.obsidian` folder, or `workspaceRoot` in
685
+ the config): exact path, then same folder, then shallowest file; ties stay
686
+ unresolved and are listed by `gno audit links`. JSON names the other
687
+ collection (`resolvedCollection` for links, `sourceCollection` for backlinks).
688
+
682
689
  ### gno graph query
683
690
 
684
691
  Bounded typed-edge traversal from a document.
@@ -314,7 +314,8 @@ gno similar gno://notes/auth.md --cross-collection
314
314
  # In your documents:
315
315
 
316
316
  See [[API Design]] for details.
317
- Check [[work:Project Plan]] for cross-collection link.
317
+ Check [[Projects/Plan]] for a path link (resolves across collections of one vault).
318
+ Check [[work:Project Plan]] to name the target collection explicitly.
318
319
  Read [[Security#OAuth]] for specific section.
319
320
  ```
320
321
 
@@ -144,7 +144,9 @@ lives in `structuredContent`; its text projection is deliberately compact and
144
144
  should not be expanded back into duplicate model context.
145
145
 
146
146
  Use `gno_changes`, `gno_diff`, and `gno_impact` for retained metadata history
147
- and bounded dependency questions. Use `gno_trace_list` and `gno_trace_show` for
147
+ and bounded dependency questions. `gno_impact` takes optional `collections`
148
+ to keep the traversal in scope; links in a vault can resolve across
149
+ collections, so unscoped graph tools cover every collection. Use `gno_trace_list` and `gno_trace_show` for
148
150
  private local diagnostics. Invoke `gno_trace_label` only when the user
149
151
  explicitly provides a relevant, irrelevant, or missing-expected judgment.
150
152
  Trace export/replay/delete/purge and saved-Capsule watch lifecycle remain
@@ -32,11 +32,16 @@ the flag.
32
32
  Superseded records never appear.
33
33
  - `budget.omitted` > 0: facts matched but did not fit. Narrow the query
34
34
  or raise the budget.
35
- - Empty `facts` plus a `hint`: nothing is stored in scope yet. Say so;
36
- the hint names `gno remember` for when the user wants to store one.
37
- - `retrieval.mode: "lexical"`: the collection has no cached embeddings, so
38
- the query matched every term. Rephrase to the fact's own words, or embed
39
- the collection (`gno embed <collection>`) for question-shaped queries.
35
+ - Empty `facts` plus a `hint`: the hint says why. "No memories in scope
36
+ yet" means nothing is stored there; "No memories in scope matched this
37
+ query" means facts exist but none shares a word with the question, so
38
+ retry with other words before concluding memory is silent. The hints
39
+ name `gno remember` for when the user wants to store one.
40
+ - `retrieval.mode: "lexical"`: the collection has no cached embeddings.
41
+ Questions still work (question words are dropped and facts sharing a
42
+ content word are returned), but a paraphrase with no word in common
43
+ misses; embed the collection (`gno embed <collection>`) to match by
44
+ meaning.
40
45
 
41
46
  3. Answer from the facts and cite each by its `gno://` URI. When memory is
42
47
  silent or stale, fall through to `gno search` / `gno query` on the
Binary file
@@ -0,0 +1 @@
1
+ 3f23832d1ed8b19e24eef29d6ea888a8c84009e39c61981d0b665c95cd7d792e gno-browser-clipper-v2.8.1.zip
@@ -21,5 +21,5 @@
21
21
  "content_security_policy": {
22
22
  "extension_pages": "script-src 'self'; object-src 'none'; connect-src http://127.0.0.1:*"
23
23
  },
24
- "version": "2.7.1"
24
+ "version": "2.8.1"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "2.7.1",
3
+ "version": "2.8.1",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
package/spec/cli.md CHANGED
@@ -240,7 +240,12 @@ the model with `id`, `model`, `dimensions`, `state` (`active`|`shadow`),
240
240
  `provenance` (building runtime, e.g. `CUDA, Bun 1.4.2`),
241
241
  `compatibleRuntimes` (runtimes that read it) and `incompatibleRuntimes`.
242
242
  Terminal output prints a `Vector partitions:` block
243
- unless there is exactly one healthy partition. Per-collection chunk totals remain deduplicated by canonical chunk;
243
+ unless there is exactly one healthy partition. `collections`, `totalDocuments`
244
+ and `totalChunks` cover configured collections only: a collection removed from
245
+ config (for example by `gno collection remove`) is not reported even while its
246
+ rows remain in the index until the next `gno update`. `totalChunks` counts the
247
+ distinct chunks of active documents; chunks left by deleted documents do not
248
+ count. Per-collection chunk totals remain deduplicated by canonical chunk;
244
249
  embedded counts require matching current inputs for every active owner within
245
250
  that collection. Status reads persisted identity and coverage without loading
246
251
  models. Legacy storage remains the fallback before variant authority; ambiguous
@@ -263,6 +268,8 @@ gno status [--json|--md]
263
268
  {
264
269
  "name": "work",
265
270
  "path": "/path",
271
+ "workspaceRoot": "/vault",
272
+ "workspaceSource": "detected",
266
273
  "documentCount": 100,
267
274
  "chunkCount": 500,
268
275
  "embeddedCount": 500
@@ -624,9 +631,11 @@ refuses descent into dataless or availability-unknown directories
624
631
  (`DATALESS_DIRECTORY` skip, or the fail-closed codes above) and preserves
625
632
  previously indexed descendants under those unproven prefixes rather than
626
633
  marking them inactive. Eligible files have no availability `errorCode`.
627
- Evidence-qualified scope: Google Drive, iCloud Drive, and OneDrive only for the
628
- tested OS/provider configuration; OneDrive only for both validated immediate
629
- SharePoint library roots. No Windows/Linux guarantee; metadata/provider
634
+ Evidence-qualified scope: Google Drive (`My Drive` and immediate Shared drives
635
+ under `Shared drives/<drive>`; the `Shared drives` folder itself is
636
+ unsupported), iCloud Drive, and OneDrive only for the tested OS/provider
637
+ configuration; OneDrive only for the two validated immediate SharePoint library
638
+ roots. No Windows/Linux guarantee; metadata/provider
630
639
  bookkeeping may occur; GNO does not pin/evict/download as product behavior.
631
640
  Source availability is distinct from `egressPolicy` (where derived content may
632
641
  travel).
@@ -886,6 +895,15 @@ projection.
886
895
  content-free decision, lineage, partial disclosure, audit metadata, and
887
896
  remediation contract.
888
897
 
898
+ `workspaceSource` is `detected` (nearest `.obsidian/` ancestor-or-self of the
899
+ collection root), `configured` (`workspaceRoot` setting), `disabled`
900
+ (`workspaceRoot: false`), `unavailable` (the root or an ancestor could not be
901
+ inspected; links stay collection-scoped) or `none`. `workspaceRoot` is the
902
+ effective workspace root, present only for `detected`/`configured`, and is a
903
+ same-host-only field like `path`. Terminal output adds a `Link workspace:` line
904
+ per workspace collection. `gno collection list` reports the same per
905
+ collection as `effectiveWorkspaceRoot` and `workspaceSource`.
906
+
889
907
  ### gno audit
890
908
 
891
909
  Read-only, offline knowledge-integrity audits. This command is distinct from
@@ -901,8 +919,17 @@ gno audit [links|provenance|freshness|all] [--collection <name>...] \
901
919
  [--json] [--output <path>]
902
920
  ```
903
921
 
904
- The default category is `all`; `--max-findings` defaults to 100 and is bounded
905
- to 1–1000. Truncation limits returned findings but preserves exact totals.
922
+ The default category is `all`; `--max-findings` defaults to 100 and accepts an
923
+ integer from 1 to 100000 or `all`. `all` returns every finding of the bounded
924
+ audit snapshot, and per-rule caps follow the same value. Any other value
925
+ (zero, negative, non-integer, above 100000) is a validation error (exit 1)
926
+ that states the accepted range. Truncation limits returned findings but
927
+ preserves exact totals. The report's `truncation` block reports three separate
928
+ conditions: `findingsTruncated` (the finding cap cut the list),
929
+ `snapshotTruncated` (the bounded snapshot of 50,000 documents/links was cut, so
930
+ totals cover the snapshot only and are not complete-index totals), and
931
+ `evidenceTruncated` (evidence items, tied-candidate lists, or evidence detail
932
+ text were shortened).
906
933
  `--output` writes only the requested report artifact with local file
907
934
  permissions. Human output renders the same report represented by
908
935
  `audit-report.schema.json`.
@@ -924,6 +951,41 @@ healthy.
924
951
  - `4` — complete report with findings
925
952
  - `5` — partial, inconclusive, unavailable, or changed-during-audit evidence
926
953
 
954
+ Link findings resolve with the workspace-aware resolver (see
955
+ docs/ARCHITECTURE.md "Resolution"). Their evidence `detail` JSON carries
956
+ separate `referenceKind` (`wiki-name`, `wiki-path`, `explicit-collection`,
957
+ `markdown`), `resolutionStatus` (`unresolved`, `ambiguous`, `outside-index`)
958
+ and `resolvedScope` (`same-collection`, `cross-collection`,
959
+ `explicit-collection`, or null when unresolved). Ambiguous workspace links add `candidateCount` and
960
+ `candidates` (tied candidate URIs in canonical path order); in a
961
+ collection-scoped audit, candidates outside the requested collections are
962
+ counted in `candidatesWithheld` and never named, and a list longer than the
963
+ detail bound sets `candidatesTruncated` (and `truncation.evidenceTruncated`).
964
+ Orphans stay "no incoming or outgoing resolved links", with connectivity drawn
965
+ from the whole index even when the audited documents are scoped; a tied link
966
+ connects nothing.
967
+
968
+ A plain wiki link or embed from a document in a link workspace whose target
969
+ is not an indexed document but exists as a file inside the workspace root (a
970
+ non-Markdown attachment, a note in an unindexed folder, a note excluded by a
971
+ collection pattern) is not reported by `links.local-targets`. It is an `info`
972
+ finding of `links.outside-index` (evidence kind `outside-index-target`,
973
+ `resolutionStatus: "outside-index"`). That rule stays `pass`, so these
974
+ findings never change the exit code. The check is existence-only: each
975
+ involved workspace root is listed once per run (hidden files and folders
976
+ skipped, symlinked folders not descended, a symlink counted only when it
977
+ resolves to a regular file inside the workspace, at most 200,000 files), no
978
+ file is opened, and no graph edge is created. Targets match files by the workspace
979
+ link rules; a non-Markdown target needs its extension, `.md` is optional. A
980
+ target missing from the listing stays unresolved; an unreadable folder or the
981
+ file bound makes the listing incomplete, which the `links.outside-index` rule
982
+ message states once.
983
+
984
+ The wiki link parser reads Obsidian's table-escaped alias `[[Note\|Alias]]`
985
+ as target `Note` with alias `Alias`. Markdown link text may contain balanced
986
+ square brackets (`[see [1]](note.md)`); a destination containing a square
987
+ bracket is not a link.
988
+
927
989
  The JSON contract is versioned as `gno://schemas/audit-report@1.0`. Finding IDs
928
990
  are stable SHA-256 identities derived from rule, normalized subject/location,
929
991
  and load-bearing evidence. Wall-clock timing and traversal order do not affect
@@ -1978,6 +2040,12 @@ gno recall <query> --scope <scope> [--scope <scope>...] [--collection <name>] [-
1978
2040
  `retrieval.semanticUnavailable` explaining why). Query expansion, graph
1979
2041
  expansion, and reranking are disabled. Scope and supersession filtering run
1980
2042
  inside the retrieval query; superseded facts are never returned.
2043
+ - The lexical leg searches the query's content words (question and function
2044
+ words dropped; quoted phrases and `-term` exclusions kept). Facts containing
2045
+ every content word are returned first; when none does, facts sharing any
2046
+ content word are returned in BM25 order, dropping those scoring below 0.1
2047
+ of the best match.
2048
+ - The budget is filled in retrieval-rank order.
1981
2049
  - Budget: at most `--max-facts` facts (default 8) under `--max-tokens`
1982
2050
  (default 512). Both must be positive integers. Recall never downloads a
1983
2051
  model.
@@ -1987,8 +2055,11 @@ gno recall <query> --scope <scope> [--scope <scope>...] [--collection <name>] [-
1987
2055
  (`caller`, `session`, `issuedAt`, `memoryIds`, `spanHashes`, `digest`) plus
1988
2056
  `budget` and `retrieval`. Derived output inherits the strictest source
1989
2057
  egress policy (`egressLineage`).
1990
- - Empty recall prints the self-teaching line naming `gno remember`
1991
- (`hint` in JSON) and exits 0.
2058
+ - Empty recall prints a hint (`hint` in JSON) and exits 0. The hint
2059
+ distinguishes an empty scope (`No memories in scope yet. Store one with:
2060
+ gno remember ...`), a populated scope with no match (`No memories in scope
2061
+ matched this query. ...`, also naming `gno remember`), and matches that did
2062
+ not fit the token budget.
1992
2063
 
1993
2064
  **Output:** `--json` prints the shared `RecallResult`. Terminal output lists
1994
2065
  numbered facts with URI, text, scopes, hash, and identity, then `Budget:`,
@@ -4179,13 +4250,19 @@ Find active documents that depend on one document through inbound typed,
4179
4250
  wiki-link, or Markdown-link edges.
4180
4251
 
4181
4252
  ```bash
4182
- gno impact <doc> [--max-depth <n>] [--max-nodes <n>] [--max-edges <n>] [--frontier-limit <n>] [--visited-limit <n>] [--json]
4253
+ gno impact <doc> [-c, --collection <name>...] [--max-depth <n>] [--max-nodes <n>] [--max-edges <n>] [--frontier-limit <n>] [--visited-limit <n>] [--json]
4183
4254
  ```
4184
4255
 
4185
4256
  The traversal is cycle-safe and enforces depth, node, edge, frontier, and
4186
4257
  visited-row caps. Every impacted document includes one deterministic
4187
4258
  dependency-to-root evidence path. JSON output uses `impact.schema.json`.
4188
4259
 
4260
+ `--collection` (repeatable) limits the traversal to those collections: only
4261
+ their documents are visited, returned, or used as a path step, so a document
4262
+ outside the scope never bridges two in-scope documents. Omitted means every
4263
+ indexed collection. An unknown collection, or a `<doc>` outside the requested
4264
+ collections, is a validation error (exit 1).
4265
+
4189
4266
  **Exit Codes:**
4190
4267
 
4191
4268
  - 0: Success
@@ -172,7 +172,6 @@ CREATE TABLE IF NOT EXISTS documents (
172
172
  );
173
173
 
174
174
  CREATE INDEX IF NOT EXISTS idx_documents_collection ON documents(collection);
175
- CREATE INDEX IF NOT EXISTS idx_documents_active ON documents(active);
176
175
  CREATE INDEX IF NOT EXISTS idx_documents_mirror_hash ON documents(mirror_hash);
177
176
  CREATE INDEX IF NOT EXISTS idx_documents_docid ON documents(docid);
178
177
  CREATE INDEX IF NOT EXISTS idx_documents_uri ON documents(uri);
package/spec/mcp.md CHANGED
@@ -351,7 +351,8 @@ Collection names are case-insensitive on input and normalized to lowercase in re
351
351
  `gno_audit` is a read-only, offline tool that returns the same
352
352
  `gno://schemas/audit-report@1.0` report as `gno audit`. Its closed input accepts
353
353
  `category` (`links`, `provenance`, `freshness`, or `all`), collection/path/tag
354
- filters, and `maxFindings` (1–1000). `maxAgeDays`, `orphanRoots`, and
354
+ filters, and `maxFindings` (an integer from 1 to 100000, or `"all"` for every
355
+ finding of the bounded snapshot; default 100). `maxAgeDays`, `orphanRoots`, and
355
356
  `orphanIgnorePrefixes` are optional explicit policy inputs. Request
356
357
  cancellation returns a partial report rather than a false clean result. The
357
358
  tool is annotated with
@@ -360,7 +361,10 @@ modifies source files, config, index rows, graph edges, daemon state, or a
360
361
  persisted audit baseline.
361
362
 
362
363
  Rules report `pass`, `fail`, `skip`, `unavailable`, or `inconclusive`; reports
363
- are `complete`, `partial`, `changed_during_audit`, or `failed`. Unavailable or
364
+ are `complete`, `partial`, `changed_during_audit`, or `failed`. A passing rule
365
+ may carry `info` findings (`links.outside-index`: link targets that exist as
366
+ files in the link workspace but are not indexed); only `warning` and `error`
367
+ findings fail a rule. Link semantics match `gno audit` (spec/cli.md). Unavailable or
364
368
  changing evidence never appears healthy. Stable finding IDs are derived from
365
369
  rule, normalized subject/location, and evidence fingerprint. Responses are
366
370
  canonically ordered and bounded while retaining exact pre-truncation totals.
@@ -1458,8 +1462,9 @@ Budgeted, cited, current-state recall from a memory-managed collection
1458
1462
  the vector leg did not run
1459
1463
  - `egressLineage` — strictest source policy across returned facts (absent when
1460
1464
  empty)
1461
- - `hint` — self-teaching line naming `gno remember`, present only when no fact
1462
- was returned
1465
+ - `hint` — present only when no fact was returned; says why: the scope holds
1466
+ no current fact (names `gno remember`), nothing in scope matched the query,
1467
+ or the matches did not fit `maxTokens`
1463
1468
 
1464
1469
  **Identity:** `caller` is the MCP client implementation name from the
1465
1470
  `initialize` handshake (`mcp` when absent); `session` is the Streamable HTTP
@@ -1841,8 +1846,15 @@ pending), `sourceUnavailable`, `staleParser` and last import time, and the
1841
1846
  switches, pending and running work, last run, last success, next due time,
1842
1847
  recovery action). The result contains no host paths.
1843
1848
 
1849
+ Each call (and each `gno_sessions_import` call) first re-reads the server's
1850
+ config file and adopts it when it changed, so source changes made by another
1851
+ process show without a restart. An unchanged file is a no-op; a source-only
1852
+ change keeps open HTTP sessions.
1853
+
1844
1854
  **Errors:** `SESSIONS_NOT_CONFIGURED` when the server's config has no
1845
- `sessions` block.
1855
+ `sessions` block; `SESSIONS_RUNTIME_FAILURE` when the config file cannot be
1856
+ read (never answered from the stale config); `SESSIONS_BINDING_MISMATCH` when
1857
+ the file is now bound to a different index (not adopted).
1846
1858
 
1847
1859
  ### gno_sessions_import
1848
1860
 
@@ -2177,7 +2189,7 @@ Find semantically similar documents using vector embeddings.
2177
2189
  **Algorithm:**
2178
2190
 
2179
2191
  1. Get all chunks for the source document
2180
- 2. Retrieve embeddings for each chunk from content_vectors
2192
+ 2. Retrieve each chunk's stored embedding from the active vector partition (legacy `content_vectors` only before any partition activates); no model is loaded
2181
2193
  3. Compute average embedding across all chunks
2182
2194
  4. Search for nearest neighbors using sqlite-vec
2183
2195
  5. Exclude self and filter by collection if not crossCollection
@@ -2371,7 +2383,14 @@ structure is derived from `structureDelta.truncated`.
2371
2383
 
2372
2384
  ### gno_impact
2373
2385
 
2374
- Read-only inbound dependency traversal for `ref`. Inputs `maxDepth`,
2386
+ Read-only inbound dependency traversal for `ref`. Optional `collections`
2387
+ (array of collection names) limits the traversal as `gno impact --collection`
2388
+ does; omitted means every collection. Over Streamable HTTP, egress policy is
2389
+ checked on `collections` (every collection when omitted, because the result
2390
+ can reach any collection a link resolves into) and on the collection of `ref`;
2391
+ a docid `ref` is checked against every collection. The same rule applies to
2392
+ `gno_backlinks` and `gno_graph*` refs, and `gno_similar` with
2393
+ `crossCollection: true` is checked against every collection. Inputs `maxDepth`,
2375
2394
  `maxNodes`, `maxEdges`, `frontierLimit`, and `visitedLimit` use the same bounds
2376
2395
  as CLI/REST/SDK. Structured content is `impact.schema.json`; each impacted
2377
2396
  document includes a deterministic evidence path over typed or backlink
@@ -260,13 +260,27 @@
260
260
  "truncation": {
261
261
  "type": "object",
262
262
  "additionalProperties": false,
263
- "required": ["findingsTruncated", "maxFindings"],
263
+ "required": [
264
+ "findingsTruncated",
265
+ "maxFindings",
266
+ "snapshotTruncated",
267
+ "evidenceTruncated"
268
+ ],
264
269
  "properties": {
265
270
  "findingsTruncated": { "type": "boolean" },
266
271
  "maxFindings": {
267
- "type": "integer",
268
- "minimum": 1,
269
- "maximum": 1000
272
+ "oneOf": [
273
+ { "type": "integer", "minimum": 1, "maximum": 100000 },
274
+ { "const": "all" }
275
+ ]
276
+ },
277
+ "snapshotTruncated": {
278
+ "type": "boolean",
279
+ "description": "The bounded audit snapshot was cut; finding totals cover the snapshot only, not the whole index"
280
+ },
281
+ "evidenceTruncated": {
282
+ "type": "boolean",
283
+ "description": "Evidence items, candidate lists, or evidence detail text were shortened"
270
284
  }
271
285
  }
272
286
  },
@@ -28,6 +28,10 @@
28
28
  "type": "string",
29
29
  "description": "Source document title"
30
30
  },
31
+ "sourceCollection": {
32
+ "type": "string",
33
+ "description": "Collection of the linking document (may differ from the target's in a link workspace)"
34
+ },
31
35
  "linkText": {
32
36
  "type": "string",
33
37
  "description": "Link display text"
@@ -39,6 +39,19 @@
39
39
  "languageHint": {
40
40
  "type": "string",
41
41
  "description": "BCP-47 language hint for the collection"
42
+ },
43
+ "workspaceRoot": {
44
+ "description": "Configured link workspace: an absolute path joins the collection to that workspace root; false keeps links inside the collection. Absent means auto-detect. Same-host callers only: omitted for a remote REST caller.",
45
+ "oneOf": [{ "type": "string" }, { "const": false }]
46
+ },
47
+ "effectiveWorkspaceRoot": {
48
+ "type": "string",
49
+ "description": "CLI list only: effective link workspace root (nearest .obsidian folder, or the configured root). Absent when links stay inside the collection."
50
+ },
51
+ "workspaceSource": {
52
+ "type": "string",
53
+ "enum": ["none", "detected", "configured", "disabled", "unavailable"],
54
+ "description": "CLI list only: how the link workspace was set"
42
55
  }
43
56
  }
44
57
  }
@@ -92,6 +92,8 @@
92
92
  "enum": [
93
93
  "exact-title",
94
94
  "exact-path",
95
+ "exact-name",
96
+ "tie-break",
95
97
  "path-fallback",
96
98
  "ambiguous-fallback",
97
99
  "similarity"
@@ -78,6 +78,10 @@
78
78
  "type": "string",
79
79
  "description": "URI of resolved target document"
80
80
  },
81
+ "resolvedCollection": {
82
+ "type": "string",
83
+ "description": "Collection of the resolved target (may differ from the source's in a link workspace)"
84
+ },
81
85
  "resolvedTitle": {
82
86
  "type": "string",
83
87
  "description": "Title of resolved target document"
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "hint": {
40
40
  "type": "string",
41
- "description": "Self-teaching line naming `gno remember`; present only when no fact was returned."
41
+ "description": "Why nothing was returned (empty scope, no match in scope, or matches over the token budget); present only when no fact was returned."
42
42
  }
43
43
  },
44
44
  "$defs": {
@@ -98,7 +98,7 @@
98
98
  },
99
99
  "collections": {
100
100
  "type": "array",
101
- "description": "Collection statistics",
101
+ "description": "Statistics for configured collections; a collection removed from config is omitted even before its index rows are pruned",
102
102
  "items": {
103
103
  "type": "object",
104
104
  "required": ["name", "documentCount", "chunkCount", "embeddedCount"],
@@ -111,6 +111,21 @@
111
111
  "type": "string",
112
112
  "description": "Collection root path. Same-host callers only: omitted for a remote REST or HTTP MCP caller."
113
113
  },
114
+ "workspaceRoot": {
115
+ "type": "string",
116
+ "description": "Effective link workspace root: plain wiki links resolve across every collection under it. Present only when the collection is in a workspace. Same-host callers only: omitted for a remote REST or HTTP MCP caller."
117
+ },
118
+ "workspaceSource": {
119
+ "type": "string",
120
+ "enum": [
121
+ "none",
122
+ "detected",
123
+ "configured",
124
+ "disabled",
125
+ "unavailable"
126
+ ],
127
+ "description": "How the link workspace was set: detected (nearest .obsidian folder), configured (workspaceRoot setting), disabled (workspaceRoot: false), unavailable (root could not be inspected; links stay collection-scoped), none (no workspace)"
128
+ },
114
129
  "documentCount": {
115
130
  "type": "integer",
116
131
  "description": "Number of active documents",
@@ -131,12 +146,12 @@
131
146
  },
132
147
  "totalDocuments": {
133
148
  "type": "integer",
134
- "description": "Total documents across all collections",
149
+ "description": "Active documents across configured collections",
135
150
  "minimum": 0
136
151
  },
137
152
  "totalChunks": {
138
153
  "type": "integer",
139
- "description": "Total chunks across all collections",
154
+ "description": "Distinct chunks of active documents across configured collections",
140
155
  "minimum": 0
141
156
  },
142
157
  "embeddingBacklog": {
@@ -14,8 +14,11 @@ import type { WorkspaceAuditProgress } from "../../core/audit-workspace";
14
14
  import { getIndexDbPath } from "../../app/constants";
15
15
  import { loadConfig } from "../../config";
16
16
  import {
17
+ AUDIT_MAX_FINDINGS_RANGE_MESSAGE,
17
18
  auditExitCode,
18
19
  AUDIT_CATEGORIES,
20
+ parseAuditMaxFindingsInput,
21
+ resolveAuditMaxFindings,
19
22
  serializeAuditReportCanonical,
20
23
  } from "../../core/audit";
21
24
  import { runWorkspaceAudit } from "../../core/audit-workspace";
@@ -31,7 +34,8 @@ export interface AuditCommandOptions {
31
34
  collections?: string[];
32
35
  paths?: string[];
33
36
  tags?: string[];
34
- maxFindings?: number;
37
+ /** Positive integer, `all`, or the raw CLI string for either. */
38
+ maxFindings?: number | string;
35
39
  maxAgeDays?: number;
36
40
  orphanRoots?: string[];
37
41
  orphanIgnorePrefixes?: string[];
@@ -80,11 +84,18 @@ export const audit = async (
80
84
  error: "category must be links, provenance, freshness, or all",
81
85
  };
82
86
  }
83
- if (invalidPositiveInteger(options.maxFindings)) {
87
+ const maxFindings =
88
+ typeof options.maxFindings === "string"
89
+ ? parseAuditMaxFindingsInput(options.maxFindings)
90
+ : options.maxFindings;
91
+ if (
92
+ (options.maxFindings !== undefined && maxFindings === undefined) ||
93
+ !resolveAuditMaxFindings(maxFindings).ok
94
+ ) {
84
95
  return {
85
96
  success: false,
86
97
  invalid: true,
87
- error: "maxFindings must be a positive integer",
98
+ error: AUDIT_MAX_FINDINGS_RANGE_MESSAGE,
88
99
  };
89
100
  }
90
101
  if (invalidPositiveInteger(options.maxAgeDays)) {
@@ -155,7 +166,7 @@ export const audit = async (
155
166
  collectionFilters: requestedCollections,
156
167
  pathFilters: options.paths,
157
168
  tagFilters: requestedTags,
158
- maxFindings: options.maxFindings,
169
+ maxFindings,
159
170
  agePolicy:
160
171
  options.maxAgeDays === undefined
161
172
  ? undefined
@@ -188,6 +199,14 @@ export const formatAuditReport = (
188
199
  `Categories: ${report.scope.categories.join(", ")}`,
189
200
  `Rules: ${report.counts.rules.total} (${report.counts.rules.fail} failed, ${report.counts.rules.unavailable} unavailable, ${report.counts.rules.inconclusive} inconclusive)`,
190
201
  `Findings: ${report.counts.findings.total}${report.counts.findings.truncated ? ` (${report.counts.findings.returned} shown)` : ""}`,
202
+ ...(report.truncation.snapshotTruncated
203
+ ? [
204
+ "Snapshot: truncated; totals cover the bounded audit snapshot, not the whole index",
205
+ ]
206
+ : []),
207
+ ...(report.truncation.evidenceTruncated
208
+ ? ["Evidence: some finding evidence was shortened"]
209
+ : []),
191
210
  `Examined: ${report.counts.examined.documents} document/rule observations`,
192
211
  `Duration: ${report.durationMs}ms`,
193
212
  ];