@gmickel/gno 1.20.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +19 -4
  2. package/assets/skill/SKILL.md +46 -15
  3. package/package.json +1 -1
  4. package/spec/cli.md +100 -0
  5. package/spec/db/schema.sql +170 -0
  6. package/spec/mcp.md +22 -0
  7. package/spec/output-schemas/capsule-reverified-event.schema.json +47 -0
  8. package/spec/output-schemas/changes.schema.json +280 -0
  9. package/spec/output-schemas/document-diff.schema.json +185 -0
  10. package/spec/output-schemas/impact.schema.json +122 -0
  11. package/spec/output-schemas/saved-capsule-list.schema.json +16 -0
  12. package/spec/output-schemas/saved-capsule-registration.schema.json +172 -0
  13. package/spec/output-schemas/saved-capsule-reverification.schema.json +59 -0
  14. package/spec/output-schemas/saved-capsule-unwatch.schema.json +16 -0
  15. package/spec/output-schemas/saved-capsule-watch.schema.json +17 -0
  16. package/src/cli/commands/changes.ts +160 -0
  17. package/src/cli/commands/context-saved.ts +189 -0
  18. package/src/cli/options.ts +8 -0
  19. package/src/cli/program.ts +195 -0
  20. package/src/core/capsule-registry.ts +279 -0
  21. package/src/core/capsule-reverification-scheduler.ts +218 -0
  22. package/src/core/capsule-reverification.ts +289 -0
  23. package/src/core/change-diff.ts +182 -0
  24. package/src/core/change-journal.ts +228 -0
  25. package/src/core/knowledge-delta.ts +395 -0
  26. package/src/core/knowledge-impact.ts +202 -0
  27. package/src/ingestion/sync.ts +214 -165
  28. package/src/mcp/tools/changes.ts +80 -0
  29. package/src/mcp/tools/index.ts +29 -0
  30. package/src/sdk/client.ts +42 -0
  31. package/src/sdk/index.ts +7 -0
  32. package/src/sdk/types.ts +22 -0
  33. package/src/serve/doc-events.ts +12 -1
  34. package/src/serve/resident-runtime.ts +22 -0
  35. package/src/serve/routes/api.ts +13 -0
  36. package/src/serve/routes/changes.ts +102 -0
  37. package/src/serve/server.ts +34 -0
  38. package/src/serve/watch-service.ts +9 -0
  39. package/src/store/index.ts +21 -0
  40. package/src/store/migrations/015-document-change-journal.ts +85 -0
  41. package/src/store/migrations/016-saved-capsules.ts +131 -0
  42. package/src/store/migrations/017-document-change-retention-counters.ts +33 -0
  43. package/src/store/migrations/018-saved-capsule-registration-epoch.ts +24 -0
  44. package/src/store/migrations/019-saved-capsule-registration-generation.ts +53 -0
  45. package/src/store/migrations/index.ts +10 -0
  46. package/src/store/sqlite/adapter.ts +291 -7
  47. package/src/store/sqlite/capsule-registry-store.ts +534 -0
  48. package/src/store/sqlite/change-journal-store.ts +473 -0
  49. package/src/store/types.ts +262 -0
package/README.md CHANGED
@@ -94,7 +94,7 @@ gno daemon --detach # headless continuous indexing (background; --status / --st
94
94
 
95
95
  <!-- public-truth:current-version -->
96
96
 
97
- > Current release: **v1.19.0** — see [CHANGELOG.md](./CHANGELOG.md)
97
+ > Current release: **v1.21.0** — see [CHANGELOG.md](./CHANGELOG.md)
98
98
 
99
99
  <!-- /public-truth -->
100
100
 
@@ -109,6 +109,13 @@ gno daemon --detach # headless continuous indexing (background; --status / --st
109
109
  watcher, jobs, stores, and models. The packed npm smoke proves two-client
110
110
  parity, warm reuse, redacted lifecycle status, fail-closed security, restart,
111
111
  and shutdown.
112
+ - **Knowledge Delta**: `gno changes`, `gno diff`, and `gno impact` expose
113
+ bounded metadata-only history, structural change summaries, and explainable
114
+ dependency paths across CLI, REST, MCP, and SDK.
115
+ - **Saved Capsule freshness**: CLI-only `gno context watch`, `watches`,
116
+ `reverify`, and `unwatch` register caller-owned Capsule files. The resident
117
+ runtime coalesces evidence changes into canonical, non-generative freshness
118
+ receipts and closed local metadata notifications.
112
119
  - **Second-brain capture**: `gno capture`, REST `/api/capture`, SDK
113
120
  `client.capture()`, MCP `gno_capture`, and Web UI Quick Capture write
114
121
  provenance-rich notes from text, stdin, or files, including typed presets for
@@ -553,9 +560,9 @@ Connect GNO to Claude Desktop, Cursor, Raycast, and more:
553
560
 
554
561
  ![GNO MCP](./assets/screenshots/mcp.jpg)
555
562
 
556
- GNO exposes 22 tools by default via [Model Context Protocol](https://modelcontextprotocol.io),
563
+ GNO exposes 25 tools by default via [Model Context Protocol](https://modelcontextprotocol.io),
557
564
  including the core retrieval tools below. Starting MCP with `--enable-write`
558
- adds 15 opt-in mutation tools, for 37 total.
565
+ adds 15 opt-in mutation tools, for 40 total.
559
566
 
560
567
  | Tool | Description |
561
568
  | :------------------- | :------------------------------------ |
@@ -574,6 +581,9 @@ adds 15 opt-in mutation tools, for 37 total.
574
581
  | `gno_status` | Index health check |
575
582
  | `gno_trace_list` | List private local retrieval receipts |
576
583
  | `gno_trace_show` | Inspect one bounded trace receipt |
584
+ | `gno_changes` | Read retained metadata-only changes |
585
+ | `gno_diff` | Read one structural document delta |
586
+ | `gno_impact` | Trace bounded dependency impact |
577
587
 
578
588
  **Design**: Default MCP mode is read-only: retrieval, opt-in verified synthesis,
579
589
  graph, status, and job inspection. Raw retrieval tools leave synthesis to your
@@ -775,6 +785,9 @@ curl http://localhost:3000/api/health
775
785
  | `/api/ask` | POST | AI-powered Q&A |
776
786
  | `/api/context` | POST | Build evidence Capsule |
777
787
  | `/api/context/verify` | POST | Verify saved Capsule |
788
+ | `/api/changes` | GET | List retained changes |
789
+ | `/api/diff` | GET | Read structural delta |
790
+ | `/api/impact` | GET | Trace dependency impact |
778
791
  | `/api/docs` | GET | List documents |
779
792
  | `/api/docs` | POST | Create document |
780
793
  | `/api/docs/:id` | PUT | Update document content |
@@ -850,7 +863,9 @@ graph TD
850
863
  | **Local LLM** | AI answers via llama.cpp, no API keys |
851
864
  | **Remote Inference** | Optional HTTP endpoints for embedding, reranking, expansion, and generation |
852
865
  | **Privacy First** | Local by default; no telemetry; network use is explicit or model provisioning |
853
- | **MCP Server** | 10 automatic client targets; 22 read-only tools, 37 with writes enabled |
866
+ | **MCP Server** | 10 automatic client targets; 25 read-only tools, 40 with writes enabled |
867
+ | **Knowledge Delta** | Bounded metadata history, structural diffs, and dependency impact paths |
868
+ | **Context Capsules** | Deterministic evidence bundles plus saved-file freshness reverification |
854
869
  | **Collections** | Organize sources with patterns, excludes, contexts |
855
870
  | **Tag Filtering** | Frontmatter tags with hierarchical paths, filter via `--tags-any`/`--tags-all` |
856
871
  | **Note Linking** | Wiki links, backlinks, related notes, cross-collection navigation |
@@ -63,21 +63,22 @@ Recipe rules:
63
63
 
64
64
  ## Command Overview
65
65
 
66
- | Category | Commands | Description |
67
- | ------------ | ---------------------------------------------------------------- | ------------------------------------------------------ |
68
- | **Search** | `search`, `vsearch`, `query`, `ask` | Find documents by keywords, meaning, or get AI answers |
69
- | **Links** | `links`, `backlinks`, `similar`, `graph`, `graph query` | Navigate document relationships and typed connections |
70
- | **Retrieve** | `get`, `multi-get`, `ls` | Fetch document content by URI or ID |
71
- | **Index** | `init`, `collection add/list/remove`, `index`, `update`, `embed` | Set up and maintain document index |
72
- | **Tags** | `tags`, `tags add`, `tags rm` | Organize and filter documents |
73
- | **Context** | `context add/list/rm/check/build/verify` | Configure guidance or compile/verify evidence Capsules |
74
- | **Traces** | `trace list/show/label/export/replay/delete/purge` | Manage and replay private retrieval receipts |
75
- | **Models** | `models list/use/pull/clear/path` | Manage local AI models |
76
- | **Serve** | `serve`, `daemon` | One resident Web/headless gateway and watcher |
77
- | **Publish** | `publish export` | Export gno.sh publish artifacts |
78
- | **MCP** | `mcp`, `mcp install/uninstall/status` | AI assistant integration |
79
- | **Skill** | `skill install/uninstall/show/paths` | Install skill for AI agents |
80
- | **Admin** | `status`, `doctor`, `cleanup`, `reset`, `vec`, `completion` | Maintenance and diagnostics |
66
+ | Category | Commands | Description |
67
+ | ------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------ |
68
+ | **Search** | `search`, `vsearch`, `query`, `ask` | Find documents by keywords, meaning, or get AI answers |
69
+ | **Links** | `links`, `backlinks`, `similar`, `graph`, `graph query` | Navigate document relationships and typed connections |
70
+ | **Retrieve** | `get`, `multi-get`, `ls` | Fetch document content by URI or ID |
71
+ | **Index** | `init`, `collection add/list/remove`, `index`, `update`, `embed` | Set up and maintain document index |
72
+ | **Tags** | `tags`, `tags add`, `tags rm` | Organize and filter documents |
73
+ | **Context** | `context add/list/rm/check/build/verify/watch/watches/reverify/unwatch` | Configure guidance or compile, verify, and watch saved evidence Capsules |
74
+ | **Changes** | `changes`, `diff`, `impact` | Inspect bounded metadata history and dependency impact |
75
+ | **Traces** | `trace list/show/label/export/replay/delete/purge` | Manage and replay private retrieval receipts |
76
+ | **Models** | `models list/use/pull/clear/path` | Manage local AI models |
77
+ | **Serve** | `serve`, `daemon` | One resident Web/headless gateway and watcher |
78
+ | **Publish** | `publish export` | Export gno.sh publish artifacts |
79
+ | **MCP** | `mcp`, `mcp install/uninstall/status` | AI assistant integration |
80
+ | **Skill** | `skill install/uninstall/show/paths` | Install skill for AI agents |
81
+ | **Admin** | `status`, `doctor`, `cleanup`, `reset`, `vec`, `completion` | Maintenance and diagnostics |
81
82
 
82
83
  ## Search Modes
83
84
 
@@ -229,6 +230,36 @@ When using GNO through MCP, prefer this retrieval order:
229
230
  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.
230
231
  7. Use `gno_get` with `fromLine`/`lineCount` for targeted reads, or `gno_multi_get` to batch top refs.
231
232
 
233
+ For a caller-owned canonical Capsule that should stay fresh locally:
234
+
235
+ ```bash
236
+ gno context watch capsule.json --question "Who owns launch?" --notify --json
237
+ gno context watches --json
238
+ gno context reverify <registration-id> --json
239
+ gno context unwatch <registration-id> --json
240
+ ```
241
+
242
+ These lifecycle operations are CLI-only and scoped to the Capsule's index.
243
+ They persist bounded metadata and evidence hashes, not Capsule or passage
244
+ bytes. Automatic resident work starts only after settled index changes,
245
+ produces the same canonical non-generative verification receipt, and never
246
+ rewrites the saved file or invokes answer generation. A failed operation has
247
+ no receipt. Local notifications contain no question, label, path, URI, hashes,
248
+ receipt, credentials, or source content.
249
+
250
+ Use Knowledge Delta when the task asks what changed or what depends on a
251
+ changed source:
252
+
253
+ ```bash
254
+ gno changes --since 2026-07-20T00:00:00Z --json
255
+ gno diff gno://notes/plan.md --json
256
+ gno impact gno://notes/plan.md --max-depth 3 --json
257
+ ```
258
+
259
+ Treat cursors and change IDs as opaque. Journal results are bounded,
260
+ metadata-only, and retention-aware; do not infer source-body history when a
261
+ diff reports partial, expired, or unavailable history.
262
+
232
263
  Use narrower tools when the request tells you to:
233
264
 
234
265
  - `gno_search`: exact phrase, filename, identifier, stack trace, error text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
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
@@ -86,6 +86,10 @@ equivalent files fail closed as ambiguous.
86
86
  | context check | yes | no | no | yes | no | terminal |
87
87
  | context build | yes | no | no | yes | no | Markdown |
88
88
  | context verify | yes | no | no | yes | no | Markdown |
89
+ | context watch | yes | no | no | no | no | terminal |
90
+ | context watches | yes | no | no | no | no | terminal |
91
+ | context unwatch | yes | no | no | no | no | terminal |
92
+ | context reverify | yes | no | no | no | no | terminal |
89
93
  | context rm | no | no | no | no | no | terminal |
90
94
  | models list | yes | no | no | yes | no | terminal |
91
95
  | models pull | no | no | no | no | no | terminal |
@@ -1364,6 +1368,58 @@ fingerprint and deterministic recount callback before any store read; CLI
1364
1368
  runtimes without that tokenizer fail with `tokenizer_unavailable` rather than
1365
1369
  trusting saved `usedTokens`.
1366
1370
 
1371
+ ### gno context watch / watches / unwatch / reverify
1372
+
1373
+ Register an explicit canonical JSON Capsule file for local, evidence-triggered
1374
+ reverification:
1375
+
1376
+ ```bash
1377
+ gno context watch <file> [--question <text>] [--label <text>] [--notify] [--json]
1378
+ gno context watches [--json]
1379
+ gno context unwatch <registration> [--json]
1380
+ gno context reverify <registration> [--json]
1381
+ ```
1382
+
1383
+ The registration persists only the absolute file path, exact file hash,
1384
+ Capsule/index identity, optional question and label, notification preference,
1385
+ and evidence URI/hash references. Capsule bytes and evidence passages are never
1386
+ copied into the database. The file remains caller-owned and immutable to GNO.
1387
+ The Capsule's canonical index is authoritative when `watch` is invoked without
1388
+ an explicit global `--index`; an explicit mismatch fails before journal or
1389
+ evidence reads.
1390
+
1391
+ A resident `serve` or `daemon` runtime reverifies affected registrations after
1392
+ watcher work has settled. Raw journal changes are coalesced, one bounded
1393
+ reverification batch runs at a time, and the durable journal high-water mark
1394
+ prevents duplicate work after restart. An expired journal cursor triggers a
1395
+ conservative bounded pass over all registrations. Reverification uses the
1396
+ canonical `context verify` receipt. Operation failures are stored separately
1397
+ and never synthesized into a receipt.
1398
+
1399
+ `--notify` enables local metadata-only `capsule-reverified` events after the
1400
+ verification record commits. Events contain registration/Capsule identity,
1401
+ operation status, affected-question state, and timestamp; they contain no
1402
+ question, file path, URI, passage, Capsule, or receipt bytes. `context
1403
+ reverify` performs the same non-generative verification immediately.
1404
+
1405
+ `context reverify` exits `0` only when `operationStatus` is `completed`. A
1406
+ persisted `failed` operation is still rendered: terminal output includes the
1407
+ failure code and message, while `--json` writes the closed structured
1408
+ reverification object to stdout. The command then exits `2`; the structured
1409
+ failure must never be mistaken for a successful verification receipt.
1410
+
1411
+ JSON contracts are Draft-07 and closed:
1412
+
1413
+ - `watch`: `saved-capsule-watch.schema.json`; the initial verification is null.
1414
+ - `watches`: `saved-capsule-list.schema.json`.
1415
+ - `unwatch`: `saved-capsule-unwatch.schema.json`.
1416
+ - `reverify`: `saved-capsule-reverification.schema.json`; a completed
1417
+ canonical receipt and a failed operation record are mutually exclusive.
1418
+ - local SSE notification data: `capsule-reverified-event.schema.json`.
1419
+
1420
+ These registration-management surfaces are CLI-only. REST, MCP, and SDK expose
1421
+ the non-persistent `context verify` operation, not watch lifecycle mutations.
1422
+
1367
1423
  ---
1368
1424
 
1369
1425
  ### gno models list
@@ -2607,6 +2663,50 @@ derived from frontmatter relations, content-type graph hints, and backfilled
2607
2663
  wiki/markdown projections (for example `mentions`, `references`, or
2608
2664
  `related`), not the global graph export edge-type enum above.
2609
2665
 
2666
+ ### gno changes
2667
+
2668
+ List retained, metadata-only document lifecycle changes.
2669
+
2670
+ ```bash
2671
+ gno changes [--since <ISO-8601|cursor>] [--collection <name>] [--limit <n>] [--json]
2672
+ ```
2673
+
2674
+ - `--since` accepts an ISO-8601 time or an opaque cursor returned by an earlier
2675
+ call. Cursors are monotonic, stable, and must not be parsed by callers.
2676
+ - `--limit` defaults to 100 and is bounded to 1-1000.
2677
+ - JSON output uses `changes.schema.json`. It includes opaque per-change IDs,
2678
+ old/new identity and hash snapshots, normalized structural deltas, pagination,
2679
+ cursor-expiry, and retention-truncation disclosure.
2680
+ - The journal never returns source bodies.
2681
+
2682
+ ### gno diff
2683
+
2684
+ Show the latest retained structural delta for one document, or select an exact
2685
+ retained journal entry by opaque ID.
2686
+
2687
+ ```bash
2688
+ gno diff <doc> [--change <id>] [--json]
2689
+ ```
2690
+
2691
+ JSON output uses `document-diff.schema.json`. `content.status` is always
2692
+ `not_retained`; GNO does not reconstruct old bodies. `history.status` is
2693
+ `partial` when `structureDelta.truncated` discloses unavailable prior
2694
+ structure. Expired/purged IDs return `status: "expired"` without inventing
2695
+ history.
2696
+
2697
+ ### gno impact
2698
+
2699
+ Find active documents that depend on one document through inbound typed,
2700
+ wiki-link, or Markdown-link edges.
2701
+
2702
+ ```bash
2703
+ gno impact <doc> [--max-depth <n>] [--max-nodes <n>] [--max-edges <n>] [--frontier-limit <n>] [--visited-limit <n>] [--json]
2704
+ ```
2705
+
2706
+ The traversal is cycle-safe and enforces depth, node, edge, frontier, and
2707
+ visited-row caps. Every impacted document includes one deterministic
2708
+ dependency-to-root evidence path. JSON output uses `impact.schema.json`.
2709
+
2610
2710
  **Exit Codes:**
2611
2711
 
2612
2712
  - 0: Success
@@ -19,6 +19,8 @@
19
19
  -- retrieval_traces - Opt-in private retrieval trace headers
20
20
  -- retrieval_trace_runs/events/judgments - Bounded trace outcome records
21
21
  -- retrieval_trace_exports/export_traces - Export manifests and trace joins
22
+ -- document_changes - Bounded metadata-only document lifecycle journal
23
+ -- document_change_journal_state - Monotonic cursor/retention boundary
22
24
 
23
25
  -- ─────────────────────────────────────────────────────────────────────────────
24
26
  -- Schema Metadata
@@ -361,6 +363,64 @@ CREATE TABLE IF NOT EXISTS retrieval_trace_export_traces (
361
363
  -- and an absolute 100,000 subordinate-record safety cap per trace. Runtime
362
364
  -- retention applies the lower configured maxRecordsPerTrace bound.
363
365
 
366
+ -- ─────────────────────────────────────────────────────────────────────────────
367
+ -- Document Change Journal
368
+ -- ─────────────────────────────────────────────────────────────────────────────
369
+
370
+ -- Append-only while retained. Rows contain identity, hashes, active state, and
371
+ -- compact structural summaries only; source and converted bodies are excluded.
372
+
373
+ CREATE TABLE IF NOT EXISTS document_changes (
374
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT,
375
+ document_id INTEGER NOT NULL CHECK (document_id > 0),
376
+ collection TEXT NOT NULL,
377
+ change_kind TEXT NOT NULL
378
+ CHECK (change_kind IN ('create', 'update', 'rename', 'inactivate', 'reactivate')),
379
+ old_rel_path TEXT,
380
+ new_rel_path TEXT,
381
+ old_docid TEXT,
382
+ new_docid TEXT,
383
+ old_uri TEXT,
384
+ new_uri TEXT,
385
+ old_source_hash TEXT,
386
+ new_source_hash TEXT,
387
+ old_mirror_hash TEXT,
388
+ new_mirror_hash TEXT,
389
+ old_active INTEGER CHECK (old_active IS NULL OR old_active IN (0, 1)),
390
+ new_active INTEGER CHECK (new_active IS NULL OR new_active IN (0, 1)),
391
+ heading_delta_json TEXT NOT NULL DEFAULT '{"added":[],"removed":[]}',
392
+ link_delta_json TEXT NOT NULL DEFAULT '{"added":[],"removed":[]}',
393
+ typed_edge_delta_json TEXT NOT NULL DEFAULT '{"added":[],"removed":[]}',
394
+ date_delta_json TEXT NOT NULL
395
+ DEFAULT '{"added":[],"removed":[],"changed":[]}',
396
+ structure_truncated INTEGER NOT NULL DEFAULT 0
397
+ CHECK (structure_truncated IN (0, 1)),
398
+ observed_at_ms INTEGER NOT NULL CHECK (observed_at_ms >= 0),
399
+ byte_size INTEGER NOT NULL CHECK (byte_size > 0 AND byte_size <= 131072),
400
+ CHECK (length(CAST(heading_delta_json AS BLOB)) <= 16384),
401
+ CHECK (length(CAST(link_delta_json AS BLOB)) <= 16384),
402
+ CHECK (length(CAST(typed_edge_delta_json AS BLOB)) <= 16384),
403
+ CHECK (length(CAST(date_delta_json AS BLOB)) <= 16384)
404
+ );
405
+
406
+ CREATE INDEX IF NOT EXISTS idx_document_changes_collection_sequence
407
+ ON document_changes(collection, sequence);
408
+ CREATE INDEX IF NOT EXISTS idx_document_changes_document_sequence
409
+ ON document_changes(document_id, sequence);
410
+ CREATE INDEX IF NOT EXISTS idx_document_changes_retention
411
+ ON document_changes(observed_at_ms, sequence);
412
+
413
+ CREATE TABLE IF NOT EXISTS document_change_journal_state (
414
+ singleton_id INTEGER PRIMARY KEY CHECK (singleton_id = 1),
415
+ last_sequence INTEGER NOT NULL DEFAULT 0 CHECK (last_sequence >= 0),
416
+ retention_floor INTEGER NOT NULL DEFAULT 0
417
+ CHECK (retention_floor >= 0 AND retention_floor <= last_sequence),
418
+ retained_entries INTEGER NOT NULL DEFAULT 0
419
+ CHECK (retained_entries >= 0),
420
+ retained_bytes INTEGER NOT NULL DEFAULT 0
421
+ CHECK (retained_bytes >= 0)
422
+ );
423
+
364
424
  -- ─────────────────────────────────────────────────────────────────────────────
365
425
  -- Document Tags
366
426
  -- ─────────────────────────────────────────────────────────────────────────────
@@ -440,3 +500,113 @@ CREATE TABLE IF NOT EXISTS doc_edges (
440
500
 
441
501
  CREATE INDEX IF NOT EXISTS idx_doc_edges_src_type ON doc_edges(src_doc_id, edge_type);
442
502
  CREATE INDEX IF NOT EXISTS idx_doc_edges_dst_type ON doc_edges(dst_doc_id, edge_type);
503
+
504
+ -- ─────────────────────────────────────────────────────────────────────────────
505
+ -- Saved Context Capsules (metadata only)
506
+ -- ─────────────────────────────────────────────────────────────────────────────
507
+
508
+ CREATE TABLE IF NOT EXISTS saved_capsule_registrations (
509
+ registration_id TEXT PRIMARY KEY,
510
+ file_path TEXT NOT NULL UNIQUE,
511
+ file_hash TEXT NOT NULL,
512
+ capsule_id TEXT NOT NULL,
513
+ index_name TEXT NOT NULL,
514
+ question TEXT,
515
+ label TEXT,
516
+ notification_preference TEXT NOT NULL DEFAULT 'none'
517
+ CHECK (notification_preference IN ('none', 'local')),
518
+ registered_at_ms INTEGER NOT NULL CHECK (registered_at_ms >= 0),
519
+ updated_at_ms INTEGER NOT NULL CHECK (updated_at_ms >= registered_at_ms),
520
+ last_attempted_sequence INTEGER NOT NULL DEFAULT 0
521
+ CHECK (last_attempted_sequence >= 0),
522
+ registration_generation INTEGER NOT NULL DEFAULT 0
523
+ CHECK (registration_generation >= 0),
524
+ CHECK (length(registration_id) BETWEEN 1 AND 128),
525
+ CHECK (length(CAST(file_path AS BLOB)) BETWEEN 1 AND 8192),
526
+ CHECK (length(file_hash) = 64 AND file_hash NOT GLOB '*[^0-9a-f]*'),
527
+ CHECK (length(capsule_id) = 64 AND capsule_id NOT GLOB '*[^0-9a-f]*'),
528
+ CHECK (length(CAST(index_name AS BLOB)) BETWEEN 1 AND 128),
529
+ CHECK (question IS NULL OR length(CAST(question AS BLOB)) <= 8192),
530
+ CHECK (label IS NULL OR length(CAST(label AS BLOB)) <= 512)
531
+ );
532
+
533
+ CREATE INDEX IF NOT EXISTS idx_saved_capsules_index
534
+ ON saved_capsule_registrations(index_name, registration_id);
535
+
536
+ CREATE TABLE IF NOT EXISTS saved_capsule_evidence (
537
+ registration_id TEXT NOT NULL,
538
+ evidence_id TEXT NOT NULL,
539
+ canonical_uri TEXT NOT NULL,
540
+ collection TEXT NOT NULL,
541
+ source_hash TEXT NOT NULL,
542
+ mirror_hash TEXT NOT NULL,
543
+ passage_hash TEXT NOT NULL,
544
+ PRIMARY KEY (registration_id, evidence_id),
545
+ FOREIGN KEY (registration_id)
546
+ REFERENCES saved_capsule_registrations(registration_id)
547
+ ON DELETE CASCADE,
548
+ CHECK (length(evidence_id) = 64 AND evidence_id NOT GLOB '*[^0-9a-f]*'),
549
+ CHECK (length(CAST(canonical_uri AS BLOB)) BETWEEN 1 AND 8192),
550
+ CHECK (length(CAST(collection AS BLOB)) BETWEEN 1 AND 256),
551
+ CHECK (length(source_hash) = 64 AND source_hash NOT GLOB '*[^0-9a-f]*'),
552
+ CHECK (length(mirror_hash) = 64 AND mirror_hash NOT GLOB '*[^0-9a-f]*'),
553
+ CHECK (length(passage_hash) = 64 AND passage_hash NOT GLOB '*[^0-9a-f]*')
554
+ );
555
+
556
+ CREATE INDEX IF NOT EXISTS idx_saved_capsule_evidence_uri
557
+ ON saved_capsule_evidence(canonical_uri, registration_id);
558
+ CREATE INDEX IF NOT EXISTS idx_saved_capsule_evidence_source
559
+ ON saved_capsule_evidence(source_hash, registration_id);
560
+ CREATE INDEX IF NOT EXISTS idx_saved_capsule_evidence_mirror
561
+ ON saved_capsule_evidence(mirror_hash, registration_id);
562
+
563
+ CREATE TABLE IF NOT EXISTS saved_capsule_verifications (
564
+ registration_id TEXT PRIMARY KEY,
565
+ trigger_kind TEXT NOT NULL CHECK (trigger_kind IN ('manual', 'journal')),
566
+ from_sequence INTEGER NOT NULL CHECK (from_sequence >= 0),
567
+ through_sequence INTEGER NOT NULL CHECK (through_sequence >= from_sequence),
568
+ operation_status TEXT NOT NULL CHECK (operation_status IN ('completed', 'failed')),
569
+ affected_question_state TEXT NOT NULL
570
+ CHECK (affected_question_state IN ('unaffected', 'affected', 'unknown')),
571
+ affected_reasons_json TEXT NOT NULL,
572
+ receipt_json TEXT,
573
+ receipt_hash TEXT,
574
+ error_code TEXT,
575
+ error_message TEXT,
576
+ verified_at_ms INTEGER NOT NULL CHECK (verified_at_ms >= 0),
577
+ FOREIGN KEY (registration_id)
578
+ REFERENCES saved_capsule_registrations(registration_id)
579
+ ON DELETE CASCADE,
580
+ CHECK (length(CAST(affected_reasons_json AS BLOB)) <= 4096),
581
+ CHECK (receipt_json IS NULL OR length(CAST(receipt_json AS BLOB)) <= 16777216),
582
+ CHECK (receipt_hash IS NULL OR (
583
+ length(receipt_hash) = 64 AND receipt_hash NOT GLOB '*[^0-9a-f]*'
584
+ )),
585
+ CHECK (error_code IS NULL OR length(CAST(error_code AS BLOB)) <= 256),
586
+ CHECK (error_message IS NULL OR length(CAST(error_message AS BLOB)) <= 4096),
587
+ CHECK (
588
+ (
589
+ operation_status = 'completed'
590
+ AND receipt_json IS NOT NULL
591
+ AND receipt_hash IS NOT NULL
592
+ AND error_code IS NULL
593
+ AND error_message IS NULL
594
+ )
595
+ OR
596
+ (
597
+ operation_status = 'failed'
598
+ AND receipt_json IS NULL
599
+ AND receipt_hash IS NULL
600
+ AND error_code IS NOT NULL
601
+ AND error_message IS NOT NULL
602
+ )
603
+ )
604
+ );
605
+
606
+ CREATE TABLE IF NOT EXISTS saved_capsule_reverification_state (
607
+ singleton_id INTEGER PRIMARY KEY CHECK (singleton_id = 1),
608
+ last_processed_sequence INTEGER NOT NULL DEFAULT 0
609
+ CHECK (last_processed_sequence >= 0),
610
+ registration_epoch INTEGER NOT NULL DEFAULT 0
611
+ CHECK (registration_epoch >= 0)
612
+ );
package/spec/mcp.md CHANGED
@@ -1526,6 +1526,28 @@ Use for explicit relationship questions over typed edges such as `works_at`,
1526
1526
  `attended`, or `mentions` after a seed ref is known. Use `gno_query` first if
1527
1527
  the seed document is unknown.
1528
1528
 
1529
+ ### gno_changes
1530
+
1531
+ Read-only metadata journal listing. Input fields: `since` (ISO-8601 time or
1532
+ opaque cursor), `collection`, and `limit` (1-1000, default 100). Structured
1533
+ content is contract-identical to CLI/REST/SDK `changes.schema.json`; it never
1534
+ contains source bodies.
1535
+
1536
+ ### gno_diff
1537
+
1538
+ Read-only structural diff for `ref`, optionally selecting opaque `change`.
1539
+ Structured content is `document-diff.schema.json`. Expired/purged history and
1540
+ metadata-only content are explicit output states, while unavailable prior
1541
+ structure is derived from `structureDelta.truncated`.
1542
+
1543
+ ### gno_impact
1544
+
1545
+ Read-only inbound dependency traversal for `ref`. Inputs `maxDepth`,
1546
+ `maxNodes`, `maxEdges`, `frontierLimit`, and `visitedLimit` use the same bounds
1547
+ as CLI/REST/SDK. Structured content is `impact.schema.json`; each impacted
1548
+ document includes a deterministic evidence path over typed or backlink
1549
+ projections. Truncation is explicit when any cap is reached.
1550
+
1529
1551
  ---
1530
1552
 
1531
1553
  ### gno_graph_neighbors
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gno://schemas/capsule-reverified-event@1.0",
4
+ "title": "GNO Metadata-only Saved Capsule Reverification Event",
5
+ "description": "Closed local event emitted only after a saved Capsule verification result commits.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "type",
10
+ "registrationId",
11
+ "capsuleId",
12
+ "operationStatus",
13
+ "affectedQuestionState",
14
+ "changedAt"
15
+ ],
16
+ "properties": {
17
+ "type": { "const": "capsule-reverified" },
18
+ "registrationId": {
19
+ "type": "string",
20
+ "pattern": "^capsule-[a-f0-9]{40}$"
21
+ },
22
+ "capsuleId": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
23
+ "operationStatus": { "enum": ["completed", "failed"] },
24
+ "affectedQuestionState": {
25
+ "enum": ["unaffected", "affected", "unknown"]
26
+ },
27
+ "changedAt": { "type": "string", "format": "date-time" }
28
+ },
29
+ "allOf": [
30
+ {
31
+ "if": {
32
+ "properties": { "operationStatus": { "const": "completed" } },
33
+ "required": ["operationStatus"]
34
+ },
35
+ "then": {
36
+ "properties": {
37
+ "affectedQuestionState": { "enum": ["unaffected", "affected"] }
38
+ }
39
+ },
40
+ "else": {
41
+ "properties": {
42
+ "affectedQuestionState": { "const": "unknown" }
43
+ }
44
+ }
45
+ }
46
+ ]
47
+ }