Compare commits

..

No commits in common. "main" and "v2.1.1" have entirely different histories.
main ... v2.1.1

718 changed files with 9749 additions and 80717 deletions

View file

@ -17,15 +17,6 @@ jobs:
strategy:
fail-fast: false
matrix:
# Pin macOS to macos-14 (Sonoma): the rolling `macos-latest` image
# The "ld: library 'clang_rt.osx' not found" failure is NOT a flaky
# image. It is the linker resolving a clang compiler-rt search path
# (Xcode .../clang/<N>/lib/darwin) that no longer exists on the runner,
# because a STALE `target/` was restored from the cache after the
# runner image's default Xcode moved. The real fix is the cache change
# below (we no longer cache `target/`, and the key is bumped to v2 to
# discard the poisoned caches), so the runner choice is no longer
# load-bearing. macos-latest is fine again with a clean target dir.
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v4
@ -35,20 +26,17 @@ jobs:
with:
components: clippy
- name: Cache cargo (registry only)
- name: Cache cargo
uses: actions/cache@v4
with:
# Deliberately NOT caching `target/`: a `target/` built against a
# different Xcode carries a stale clang compiler-rt search path that
# breaks the linker ("clang_rt.osx not found"). Cache only the
# download-heavy registry/git dirs; recompiling is cheap and correct.
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-v2-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: Check
run: cargo check --workspace
@ -73,13 +61,13 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-14
- os: macos-latest
target: aarch64-apple-darwin
cargo_flags: ""
# Intel Mac builds against a system ONNX Runtime via ort-dynamic
# (ort-sys has no x86_64-apple-darwin prebuilts). Compile-only here;
# runtime linking is a user concern documented in INSTALL-INTEL-MAC.md.
- os: macos-14
- os: macos-latest
target: x86_64-apple-darwin
cargo_flags: "--no-default-features --features ort-dynamic,vector-search"
- os: ubuntu-latest

View file

@ -1,25 +0,0 @@
name: Guard — No Private Cloud Code
# Fails if private Vestige Cloud *service* code (billing, sync-key/namespace
# mapping, Lemon Squeezy webhooks, transactional email) ever lands in this
# public repo. The public cloud *client* is allowed and does not trip this.
on:
push:
branches: [main, feat/cloud-sync-mvp]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
guard:
name: No private cloud service code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Scan for private cloud service markers
run: ./scripts/check-no-private-cloud.sh

View file

@ -1,67 +0,0 @@
name: Deploy GitHub Pages
on:
push:
branches: [main, feat/cloud-sync-mvp]
paths:
- 'apps/dashboard/**'
- '.github/workflows/pages.yml'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment; let an in-progress run finish.
concurrency:
group: pages
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
env:
# GitHub Pages serves this project repo from the /vestige/ subpath.
# The dashboard must be built with a matching base so every _app/ asset
# resolves instead of 404ing.
VESTIGE_BASE_PATH: /vestige
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build dashboard
run: pnpm --filter @vestige/dashboard build
- name: Assemble site root
run: |
# The Pages project site is already served from /vestige/, and the
# dashboard is built with base path /vestige (VESTIGE_BASE_PATH), so
# its files go directly at the artifact root — the base path already
# accounts for the subpath. No extra subdirectory or redirect needed.
rm -rf _site
mkdir -p _site
cp -r apps/dashboard/build/* _site/
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: _site
- id: deployment
uses: actions/deploy-pages@v4

View file

@ -31,14 +31,7 @@ jobs:
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: zip
# vector-search (usearch HNSW index) is REQUIRED. Without it the
# storage layer's #[cfg(feature = "vector-search")] paths compile
# out, so embeddings are never persisted or queried: the binary
# reports "Embedding Service: Not Ready", 0% coverage, consolidate
# generates 0 embeddings, and no model download is ever attempted.
# usearch builds cleanly on MSVC because vestige-core pins it with
# features=["fp16lib"] (see crates/vestige-core/Cargo.toml).
cargo_flags: "--no-default-features --features embeddings,ort-download,vector-search"
cargo_flags: ""
needs_onnxruntime: false
# Intel Mac uses the ort-dynamic feature to runtime-link against a
# system libonnxruntime (Homebrew), sidestepping the missing
@ -57,93 +50,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.event.inputs.tag || github.ref }}
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Validate release version
shell: bash
env:
RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }}
run: |
node <<'NODE'
const { execFileSync } = require('node:child_process');
const tag = process.env.RELEASE_TAG || '';
const expected = tag.replace(/^refs\/tags\//, '').replace(/^v/, '');
if (!expected) {
throw new Error('Release tag is empty');
}
const packageFiles = [
'package.json',
'apps/dashboard/package.json',
'packages/vestige-init/package.json',
'packages/vestige-mcp-npm/package.json'
];
for (const file of packageFiles) {
const actual = require(`./${file}`).version;
if (actual !== expected) {
throw new Error(`${file} version ${actual} does not match ${tag}`);
}
}
const metadata = JSON.parse(execFileSync('cargo', [
'metadata',
'--format-version',
'1',
'--locked',
'--no-deps'
], { encoding: 'utf8' }));
for (const name of ['vestige-core', 'vestige-mcp']) {
const pkg = metadata.packages.find((candidate) => candidate.name === name);
if (!pkg) throw new Error(`Missing Cargo package ${name}`);
if (pkg.version !== expected) {
throw new Error(`${name} version ${pkg.version} does not match ${tag}`);
}
}
NODE
- name: Build embedded dashboard
shell: bash
env:
RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }}
run: |
pnpm install --frozen-lockfile
pnpm --filter @vestige/dashboard check
pnpm --filter @vestige/dashboard test
pnpm --filter @vestige/dashboard build
node <<'NODE'
const fs = require('node:fs');
const tag = process.env.RELEASE_TAG || '';
const expected = tag.replace(/^refs\/tags\//, '').replace(/^v/, '');
const versionFile = 'apps/dashboard/build/_app/version.json';
const version = JSON.parse(fs.readFileSync(versionFile, 'utf8')).version;
if (version !== expected) {
throw new Error(`${versionFile} version ${version} does not match ${tag}`);
}
for (const file of ['apps/dashboard/build/index.html', versionFile]) {
if (!fs.existsSync(file)) {
throw new Error(`Dashboard build did not produce ${file}`);
}
}
NODE
- name: Build
run: cargo build --locked --package vestige-mcp --release --target ${{ matrix.target }} ${{ matrix.cargo_flags }}
run: cargo build --package vestige-mcp --release --target ${{ matrix.target }} ${{ matrix.cargo_flags }}
- name: Package (Unix)
if: matrix.os != 'windows-latest'
@ -163,21 +77,10 @@ jobs:
cd target/${{ matrix.target }}/release
Compress-Archive -Path vestige-mcp.exe,vestige.exe,vestige-restore.exe -DestinationPath ../../../vestige-mcp-${{ matrix.target }}.zip
- name: Generate checksum
shell: bash
run: |
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 vestige-mcp-${{ matrix.target }}.${{ matrix.archive }} > vestige-mcp-${{ matrix.target }}.${{ matrix.archive }}.sha256
else
sha256sum vestige-mcp-${{ matrix.target }}.${{ matrix.archive }} > vestige-mcp-${{ matrix.target }}.${{ matrix.archive }}.sha256
fi
- name: Upload to Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
files: |
vestige-mcp-${{ matrix.target }}.${{ matrix.archive }}
vestige-mcp-${{ matrix.target }}.${{ matrix.archive }}.sha256
files: vestige-mcp-${{ matrix.target }}.${{ matrix.archive }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -12,19 +12,6 @@ env:
VESTIGE_TEST_MOCK_EMBEDDINGS: "1"
jobs:
hook-tests:
name: Hook Tests
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.9"
- run: python3 -m unittest discover -s tests/hooks -p 'test_*.py'
- run: python3 -m py_compile hooks/sanhedrin-local.py tests/hooks/test_sanhedrin_claim_mode.py
- run: bash -n hooks/sanhedrin.sh scripts/install-sandwich.sh scripts/check-sandwich-prereqs.sh
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest

3
.gitignore vendored
View file

@ -139,6 +139,3 @@ apps/dashboard/node_modules/
# =============================================================================
fastembed-rs/
.mcp.json
.claude/
.codebase-memory/

View file

@ -5,435 +5,6 @@ All notable changes to Vestige will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Fixed — Auto-consolidation merge: opt-out lever + protected pins honored (#142)
The background consolidation cycle's auto-dedup pass silently concat-merges
near-duplicate memories (cosine ≥ 0.85): it keeps the strongest node, folds the
weaker ones in as `[MERGED]` blocks, and **hard-deletes** the originals — with no
reflog and no way to turn it off. Two fixes. First, it is now disableable: set
`VESTIGE_AUTO_CONSOLIDATE_MERGE=0` (or `false`/`off`/`no`) to suppress it. It
remains **on by default** (behavior unchanged), and the `dedup` MCP tool stays
available for on-demand, previewable, reversible merges regardless. Second,
**protected (pinned) memories are now excluded from this pass** — previously
`dedup protect` did nothing here, so a pinned memory could be absorbed or deleted
unattended, contradicting the interactive contract that a protected node may only
survive a merge, never be absorbed. A protected node is now never an anchor, never
a cluster member, and thus never merged into or deleted, whether the lever is on
or off.
## [2.2.1] - 2026-07-02 — "Windows embeddings + backfill safety"
A focused patch release. Two fixes plus a first-run guide.
### Fixed — Windows embeddings never initialized (#101)
The `x86_64-pc-windows-msvc` v2.2.0 binary was built without the `vector-search`
feature, so the storage layer's `#[cfg(feature = "vector-search")]` paths compiled
out. On Windows this meant new memories got no embedding, semantic search returned
nothing, `vestige health` reported "Embedding Service: Not Ready" (0% coverage),
and no model download was ever attempted — while v2.1.23 worked on the same machine.
The release build now includes `vector-search` on Windows (it compiles cleanly on
MSVC because `usearch` is pinned with `features = ["fp16lib"]`). npm and direct
downloads are fixed by the same rebuilt release asset. Thanks @Vrakoss for the
precise report.
### Fixed — Retroactive Salience Backfill: bounded promote + opt-out lever (#103)
The consolidation-pass backfill promoted root-cause memories with an **uncapped**
`stability * 1.5` FSRS multiply, and a code comment wrongly claimed it was capped.
On a chronically-recurring failure this could inflate a cause's stability without
bound, distorting its review schedule. Backfill promotion is now bounded to
`MIN(stability * 1.5, stability + 365.0)` (the additive +365-day ceiling the
backfill module already computed but never applied), on both the auto-fire and the
manual `backfill` tool paths. Auto-fire remains **on by default** (it shipped and
was documented in v2.2.0) but is now disableable: set `VESTIGE_BACKFILL_AUTOFIRE=0`
(or `false`/`off`/`no`) to turn it off; the manual `backfill` tool + CLI remain
available regardless. Thanks @randomnimbus for the report and the initial patch.
### Added — First-run guide (#83)
A single `docs/GETTING-STARTED.md` that consolidates install, "what gets saved",
how to inspect your memory, and project scoping into one 30-minute first-run path,
linked from the README.
### Credits
This release was driven by the community:
- **@Vrakoss** reported the Windows embeddings regression (#101) with a clean,
precise repro that pinned the failure immediately.
- **@randomnimbus** (Peter Lauzon) reported the backfill safety issue (#103) and
contributed the fix — the bounded `promote_memory_backfill` and the
`VESTIGE_BACKFILL_AUTOFIRE` lever shipped as they proposed them
(co-authored in `f7530af`).
Thank you both.
## [2.2.0] - 2026-06-29 — "Retroactive Salience + Tool Consolidation"
Three independent value streams land together as a coherent release.
### Added — Retroactive Salience Backfill ("Memory with hindsight")
A faithful port of Cai 2024 (*Nature*). When a **failure** (bug/crash/regression)
is recorded, Vestige reaches **backward in time** and promotes the quiet earlier
memory that *caused* it — the root cause a vector search structurally cannot
surface, because it is not *similar* to the failure, only *causally upstream*
(it shares an entity: the same file, env var, or service). Backward-only by
construction. Auto-fires inside the consolidation pass; also exposed as the
`backfill` MCP tool and the `vestige backfill` CLI command (`--manual`,
`--contrast`, `--no-promote` dry-run).
### Changed — MCP Tool Consolidation (34 → 13 advertised tools)
The MCP surface is consolidated from 34 tools to **13**: `recall` (folds
search + deep_reference + contradictions), `maintain` (consolidate/dream/gc/
importance_score/backup/export/restore), `dedup` (8 merge tools → 1), `graph`
(explore/predict/memory_graph/composed_graph), `memory_status` (system_status/
memory_health/timeline/changelog), plus `memory`, `codebase`, `intention`,
`smart_ingest`, `source_sync`, `session_start`, `suppress`, and the flagship
`backfill`. Old tool names remain dispatchable as hidden back-compat aliases.
### Improved — `deep_reference` retrieval engine
- **F32 embeddings** (was I8 quantization) — lifts the 0.40.6 paraphrase cosine
band so close-but-reworded queries actually retrieve.
- **Reciprocal Rank Fusion** replaces linear score combination in hybrid search.
- **Claim-vs-memory contradiction**`recall`/`cross_reference` now test *your
claim* against stored memory, surfacing `claim_contradicts_memory` instead of
the old "confident silence."
- **Never-composed semantic-band gate** — admits no-shared-word memory pairs in
the 0.450.85 cosine band for `vestige compose`.
- New `vestige recall <query>` and `vestige compose` CLI commands expose the
engine outside the MCP path.
### Fixed — security & correctness (multi-model audit swarm)
SSRF/token-exfil hardening, panic/DoS/overflow fixes, deadlock and
lock-contention fixes, dedup and decay correctness. `usearch` keeps
`features = ["fp16lib"]` to avoid the Windows MSVC C1021 build break (#71/#94).
## [2.1.27] - 2026-06-19 — "External-Source Connectors"
Roadmap [#57](https://github.com/samvallad33/vestige/issues/57), **Phases 14
(complete)**: Vestige can now act as a durable, local, semantically-searchable
retrieval layer over an external system of record — GitHub Issues and Redmine —
without replacing it. The external system stays canonical; Vestige **indexes,
connects, retrieves, and cites back** to the source record.
Unlike a live ticket-system MCP proxy (which holds no state and is rate-limited
per query), Vestige keeps a durable embedded index: searchable **offline**,
**semantically**, joinable with the rest of your memory, temporally versioned,
and re-syncable **idempotently** with no duplication. To our knowledge no other
local-first memory layer combines native connectors, external-URL provenance,
content-hash idempotent sync, and tombstoning of vanished records.
### Added
- **`source_sync` MCP tool** — index an external system into Vestige.
- GitHub: `{"source": "github", "repo": "owner/name"}` indexes every issue +
its comments. Auth via `GITHUB_TOKEN` (public repos work tokenless at a
lower rate limit).
- Redmine: `{"source": "redmine", "project": "<id>"}` indexes a project's
issues + journals (comments and status/assignment history). Host from
`REDMINE_URL`, auth from `REDMINE_API_KEY`.
- Re-running updates changed issues in place (no duplicates); `reconcile:
true` tombstones issues no longer visible upstream.
- **Source-aware investigation filters on `search`** (Phase 4) — filter results
by `source_system`, `source_project`, `source_id`, `source_type`,
`source_author`, a `source_updated_after`/`source_updated_before` date range,
and `source_status` (`valid` / `tombstoned` / `any`). Status, tracker, and
priority remain filterable via the existing `tag_prefix` (the connectors emit
`status:`/`tracker:`/`priority:`/`label:` tags). Applied as post-filters;
non-connector memories are excluded from a source-scoped query.
- **Source envelope** on every memory — structured, machine-readable provenance
(`source_system`, `source_id`, `source_url`, `source_updated_at`,
`content_hash`, `synced_at`, `source_project`, `source_type`, `source_author`)
distinct from the legacy free-form `source` label. Search results gain a
`sourceRecord` object (with the canonical `url`) **only** for
connector-ingested memories, so an agent can cite and follow the source.
- **Idempotent sync primitives** (`vestige-core`): `upsert_by_source` (keyed on
`(source_system, source_id)`, content-hash change detection), per-connector
cursor checkpoints (`connector_cursors`), and `reconcile_source_tombstones`
(invalidate-don't-delete via the bitemporal `valid_until`, so a vanished
record is retained for audit but drops out of current retrieval).
- **Connector contract** (`vestige_core::connectors`) — a small source-agnostic
`Connector` trait + `run_sync` driver (cursor overlap window, incremental
paging, optional deletion reconcile) with two reference connectors behind the
optional `connectors` cargo feature (on by default in the MCP server, off in
the core library's default features so non-connector consumers link no HTTP
client):
- **GitHub Issues**`state=all`, `since` cursor, Link-header pagination,
drops PRs, host-pinned next-url.
- **Redmine**`status_id=*` (open + closed), hex-encoded `updated_on>=`
cursor, `offset` pagination, per-issue detail fetch for journals (the list
endpoint omits them), `X-Redmine-API-Key` header auth.
### Database
- **Migration V17** — nine nullable source-envelope columns on `knowledge_nodes`
(additive; every existing memory is untouched), a partial UNIQUE index on
`(source_system, source_id)` enforcing one memory per external record while
costing nothing for envelope-less legacy rows, and the `connector_cursors`
checkpoint table. Idempotent on replay, following the established
`add_column_if_missing` pattern.
### Notes
- Local-first and optional: with no `source_sync` call, behavior is unchanged.
The default core-library build does not link an HTTP client.
## [2.1.26] - 2026-06-15 — "Configurable Output"
Roadmap **Phase 2: Configurable Output**. Users can now control the default
shape and size of high-traffic MCP responses with an optional, local-first
config file — without recompiling and without a cloud service. The default
behavior is unchanged: a fresh install with no `vestige.toml` behaves exactly
as before.
### Added
- **`vestige.toml` config file**, loaded from the active Vestige data directory
(`<data_dir>/vestige.toml`, alongside `vestige.db`). A missing or malformed
file falls back to built-in defaults, so existing installs are unaffected.
- **`[defaults]` table** with three keys: `detail_level`
(`brief` | `summary` | `full`), `limit` (default result count for
high-traffic tools), and `profile`.
- **Output profiles**`lean`, `default`, `audit`, `research` — each presetting
a coherent bundle of detail level, result limit, and whether scores and
timestamps are included:
- `lean`: `brief` detail, limit 5, scores and timestamps dropped (smallest
context cost).
- `default`: historical behavior — `summary` detail, tool's own default
limit, scores and timestamps present. **Unchanged.**
- `audit`: `full` detail with every field, score, and timestamp.
- `research`: `full` detail with a larger default limit (25).
- **Three-layer precedence**, applied per call: an explicit MCP parameter wins
over the config file, which wins over the built-in default.
- **`profile` field** echoed in `search`, `memory_timeline`, `codebase`
(`get_context`), and `session_context` responses so the active profile is
observable.
### Changed
- `search`, `memory_timeline`, `codebase` (`get_context`), and
`session_context` now resolve their default detail level and result limit
through the config file when no explicit parameter is supplied. With no
`vestige.toml` present, their output is byte-for-byte identical to v2.1.25.
### Documentation
- `docs/CONFIGURATION.md` gains a **Output Configuration (`vestige.toml`)**
section documenting the file location, `[defaults]` keys, profile presets,
and precedence rules.
## [2.1.25] - 2026-06-12 — "Merge / Supersede Controls"
v2.1.25 ships Phase 3: diff-previewed, confidence-gated, reversible,
self-explaining combine/dedupe/supersede on a never-delete (bitemporal) store.
The default is always preview/review — these tools never silently mutate memory.
The differentiator is the reversible operation log: every merge/supersede/undo is
an auditable, reversible event with provenance ("why did these combine?") — a git
reflog for your agent's memory.
### Added
- **Seven new MCP tools** for merge/supersede control:
- `merge_candidates` — surface likely duplicate/overlapping clusters with
confidence scores and the signals behind each (Fellegi-Sunter
match/possible/non-match). Read-only.
- `plan_merge` — produce a previewable merge PLAN (a diff of combined
content/tags/provenance) without applying it.
- `plan_supersede` — preview superseding A with B (bitemporal invalidation,
audit-preserving) without applying.
- `apply_plan` — execute a previously-generated plan id; recorded as a
reversible operation.
- `merge_undo` — reverse a prior merge/supersede operation, or list the
reversible operation log (the "memory reflog").
- `protect` — pin a memory so it can never be auto-merged, superseded, or
garbage-collected.
- `merge_policy` — get/set the per-project Fellegi-Sunter two thresholds
(`match_threshold`, `possible_threshold`) and `auto_apply`.
- **Bitemporal "invalidate, don't delete" supersede** (Graphiti-style): a
superseded memory is kept and stays queryable for audit. It is stamped with
`valid_until = now` and a new `superseded_by` lineage pointer, instead of being
deleted or merely demoted.
- **Reversible operation log** (`merge_operations` table) — every applied
merge/supersede records an undo payload and provenance signals so any operation
can be reversed, including restoring survivor content/tags and clearing the
bitemporal invalidation.
- **Fellegi-Sunter two-threshold scoring** for dedup/merge candidates, combining
embedding cosine similarity with tag and content-token overlap. Borderline
"possible" matches are surfaced for review instead of force-merged.
- **Memory protection / pinning**`protected` column on `knowledge_nodes`;
protected memories are excluded from auto-merge/supersede/GC paths.
- **Migration V14** adding the `merge_plans` and `merge_operations` tables, the
`protected` and `superseded_by` columns on `knowledge_nodes`, and their
indexes. Idempotent on replay.
- **Docs**: `docs/MERGE_SUPERSEDE.md` describing the design, the bitemporal
model, the two-threshold policy, the reversible operation log, and the tool
surface.
### Notes
- All merge/supersede operations are **opt-in and preview-first**. `apply_plan`
requires `confirm=true` for `possible`/`non_match` plans, and only applies
`match` plans without confirmation when `merge_policy.auto_apply` is enabled
(default off). This deliberately avoids the silent-merge / auto-delete /
audit-trail-loss anti-patterns reported against other memory systems.
- The merge policy persists per-project and is also overridable via
`VESTIGE_MERGE_MATCH_THRESHOLD`, `VESTIGE_MERGE_POSSIBLE_THRESHOLD`, and
`VESTIGE_MERGE_AUTO_APPLY` environment variables.
## [2.1.23] - 2026-05-27 — "Receipt Lock Hardening"
v2.1.23 hardens the Sanhedrin launch path so Receipt Lock is portable,
observable, and precise enough for broader use.
### Added
- **Model-agnostic Sanhedrin backend presets** for custom OpenAI-compatible
servers, small laptops, Ollama, MLX, vLLM, llama.cpp, hosted APIs, and
Anthropic via LiteLLM. Sanhedrin no longer guesses a large default verifier.
- **Fail-open telemetry** in `fail-open.jsonl`, plus a dashboard telemetry API
and 7-day ambient dashboard counters for vetoes, appeals, and fail-open runs.
- **Receipt schema documentation** covering receipt artifacts, appeals, command
ledgers, fail-open logs, compatibility rules, and staged-evidence trust
boundaries.
- **Opt-in CUDA feature flags** for Qwen3 embedding builds on NVIDIA hardware.
### Changed
- Receipt Lock strips code fences, inline code, blockquotes, quoted regions, and
scoped epistemic hedges before matching verification claims.
- Structured transcript tool-use receipts are the default evidence path; loose
JSON command scanning now requires `VESTIGE_SANHEDRIN_ALLOW_LOOSE_LEDGER=1`.
- Claim-mode sampling now prioritizes higher-severity claims instead of taking
the first eight source-order claims.
- Hosted Sanhedrin credentials now require `VESTIGE_SANHEDRIN_API_KEY` and are
only sent to the configured Sanhedrin endpoint, never to Vestige retrieval.
- `smart_ingest` batch mode now defaults to `batchMergePolicy: "force_create"`
so caller-separated items stay separate unless callers opt into smart merging.
- CUDA-enabled Qwen3 builds now try `Device::new_cuda(0)` before falling back to
Metal or CPU.
### Fixed
- Standalone dashboard mode now hydrates the cognitive engine for Dream and
Deep Reference instead of returning 503s.
- `--data-dir` now rejects existing non-directory paths with a clear error.
- `vestige-restore` now handles `--help` and `--version` instead of treating
them as backup file paths.
- Smart ingest merge/update responses now include `previousContent`,
`mergedFrom`, and `mergePreview` so callers can inspect mutated memories.
- Daily Sanhedrin telemetry now preserves NOTE and CAUTION buckets instead of
folding both into PASS.
## [2.1.22] - 2026-05-25 — "Sanhedrin Receipts"
v2.1.22 makes the optional Sanhedrin hook quieter and more accountable by
turning draft judgment into local, appealable receipts instead of opaque vetoes.
### Added
- **Receipt Lock** blocks unsupported verification claims such as "tests passed"
unless the current transcript contains a matching successful test, build,
lint, or typecheck command receipt.
- **Veto receipts** are written to `~/.vestige/sanhedrin/latest.json` and
`latest.html` with Claim -> Verdict -> Precedent -> Fix -> Appeal fields.
- **Dashboard Verdict Bar** surfaces the latest PASS, NOTE, CAUTION, VETO, or
APPEALED state and lets users appeal stale, wrong, or too-strict vetoes.
- **Appeal training** records feedback in `appeals.jsonl` and suppresses future
vetoes for the same claim fingerprint.
### Changed
- Sanhedrin claim-mode output now feeds a per-claim receipt ledger while keeping
the existing one-line Stop-hook contract for Claude Code.
## [2.1.21] - 2026-05-24 — "Agent-Neutral Hardening"
v2.1.21 is a release-hardening pass for normal MCP usage across agents. It keeps
Claude Code Cognitive Sandwich companion files optional while making the MCP
server, package installer, release workflow, and portable sync path safer.
### Added
- **Agent-neutral memory protocol** — new `docs/AGENT-MEMORY-PROTOCOL.md` gives
any MCP-compatible client the same practical memory loop: initialize context,
search/deep-reference when needed, save durable facts with `smart_ingest`, and
promote/demote/purge with `memory`.
- **HTTP transport opt-in**`vestige-mcp` now requires `--http`,
`--http-port`, or `VESTIGE_HTTP_ENABLED=1` before starting MCP-over-HTTP.
- **Release checksums** — release assets now publish `.sha256` files beside each
archive.
### Changed
- **`vestige update` is binary-only by default** — Claude Code Cognitive
Sandwich companion files refresh only with `vestige update --sandwich-companion`
or `vestige sandwich install`.
- **MCP tool results include structured content** while keeping text content for
clients that only consume the classic MCP response shape.
- **NPM install messaging is agent-neutral** and unsupported release targets
fail fast instead of trying to download assets that do not exist.
- **Portable merge uses UPSERT instead of `INSERT OR REPLACE`** for keyed tables,
preserving related rows instead of causing delete-and-insert side effects.
### Fixed
- **Destructive delete confirmation**`memory(action="delete")` now requires
`confirm=true`, matching `purge`; the deprecated `delete_knowledge` shim no
longer bypasses confirmation.
- **Portable purge tombstone sync** — merge imports now carry
`deletion_tombstones` and apply purges without retaining deleted memory text.
Hard purge tombstones win over newer local edits during portable sync, while
tombstone merges keep the newest deletion timestamp.
- **Vector index reload staleness** — loading persisted embeddings rebuilds the
in-memory index from an empty index before adding current embeddings.
- **HTTP transport hardening** — origin, Accept, session, and protocol-version
validation now reject incompatible or cross-origin browser requests earlier.
- **Init config safety**`@vestige/init` backs up existing config files, writes
atomically, accepts JSONC-style comments/trailing commas, and no longer writes
Xcode trust-accepted flags.
- **Release tag checkout** — manual release builds now checkout the requested tag
or ref before packaging.
### Verified
- `cargo test -p vestige-mcp --lib --no-fail-fast`
- `cargo test -p vestige-mcp --bin vestige-mcp --no-fail-fast`
- `cargo test -p vestige-core portable_merge_import --no-fail-fast`
- `cargo test -p vestige-mcp --bin vestige --no-fail-fast`
- `cargo test -p vestige-e2e-tests --test mcp_protocol --no-fail-fast`
- `cargo check --workspace`
- `cargo metadata --format-version 1 --locked --no-deps`
- `pnpm --filter @vestige/dashboard check`
- `pnpm --filter @vestige/dashboard test`
- `pnpm --filter @vestige/dashboard build`
- `node --check packages/vestige-init/bin/init.js`
- `node --check packages/vestige-mcp-npm/scripts/postinstall.js`
- `node --check packages/vestige-mcp-npm/bin/vestige-restore.js`
## [2.1.2] - 2026-05-01 — "Honest Memory"
v2.1.2 focuses on operational trust: exact search stays exact, purge really removes content, contradictions are directly inspectable, and the update flow no longer depends on copied curl commands.
### Added
- **Concrete search mode**`search` now auto-detects literal queries such as quoted strings, env vars, UUIDs, paths, and code identifiers. Those queries take a keyword/literal path that skips HyDE, semantic fusion, FSRS reweighting, retrieval competition, and spreading activation so exact matches land first.
- **Irreversible purge**`memory(action="purge", confirm=true)` permanently removes memory content and embeddings, scrubs `insights.source_memories`, detaches temporal-summary children, prunes graph edges, and writes only a non-content `deletion_tombstones` row for sync/audit.
- **First-class contradictions tool** — new `contradictions` MCP tool scans a topic or recent memories for trust-weighted disagreements using the same local contradiction logic as `deep_reference`.
- **Simple update flow**`vestige update` refreshes the installed binary and companion Sandwich files without requiring users to paste a curl installer.
- **Pro waitlist preview**`/dashboard/waitlist` adds a local-only marketing surface for Solo Pro and Team Pro early-access signups. `VITE_WAITLIST_ENDPOINT` and `VITE_SUPPORT_BOT_ENDPOINT` are opt-in dashboard env vars, so no signup data is captured unless endpoints are configured.
### Fixed
- **Dream connection persistence cap** — dense single-domain dreams now persist every connection discovered in that run instead of losing everything beyond the old 1,000-entry live buffer. The live dreamer buffer now keeps up to 200,000 high-scoring recent connections, and the MCP `dream` tool exposes `min_similarity` for corpus-specific tuning.
- **Embedding-model upgrade repair**`vestige consolidate` now re-embeds every missing or active-model-mismatched memory in one pass, so v1/v2 mixed stores are no longer left partially unreachable after only the first 100 legacy embeddings are regenerated.
## [2.1.1] - 2026-05-01 — "Portable Sync"
v2.1.1 focuses on user-controlled portability: exact storage archives, merge-safe file sync, pluggable sync backends, and explicit hook opt-ins.

411
CLAUDE.md
View file

@ -1,89 +1,362 @@
# 🚀 MANDATE #0 — MAXIMUM AMBITION. NO HEDGING. (read this first, every time)
# 🚫 READ FIRST EVERY PROMPT — NO HEDGING
This is the standing default for ALL work on Vestige, before anything else below.
Sam's April 20 2026 correction: *"you NEVER LISTEN TO YOUR RULES, WHY ARE YOU ALWAYS BREAKING THE HEDGING/PESSIMISTIC RULE."*
- **Assume maximum ambition.** Build the most exhaustive, jaw-dropping, correct
version — not the safe "good enough" one. The ceiling is "nobody else has
this," not "this works." Default to the winning move, not the cautious one.
- **Scour before you settle.** When unsure or stuck, research the real frontier
(web, source, the actual running app) before acting. Never accept the first
adequate answer when a deeper one exists. Verify, don't assume.
- **No hedging.** Lead with the best path and a clear recommendation. Forbidden:
"this is probably too risky," "temper expectations," "good enough," "maybe try
the easier one." Risks get their own honest section — never used to shrink the
target.
- **Show proof.** Verify changes in the real running app and share the evidence
(screenshots, test output, gate results) — don't claim done without it.
- **Protect what's flawless, detonate what isn't.** Treat finished, loved work as
load-bearing (don't break it); push everything else past where any other dev
would stop.
When Sam commits to an execution path (content cadence, shipping velocity, outreach, launch), COMPUTE THE FORECAST AS IF THAT COMMITMENT IS MET. Do not discount by execution probability. Sam has earned the benefit of the doubt across 139 commits and 1,292 passing tests in 3 months.
Origin: Sam, Jun 22 2026 — the overnight session that turned the dashboard +
Memory Cinema from "alive" into a category-of-one particle journey. The depth
only happened because the bar was set to maximum. Make that the default, not the
exception.
**Forbidden in any response**: "has to be true", "has to convert", "realistic forecast" used to shrink, "contingent on" Sam's own execution, "not guaranteed", "may or may not", "aspiration not forecast", "temper your expectations", floors framed as reality vs. ceilings framed as fantasy.
**Required format for forecasts**: `Target X. Floor Y. Ceiling Z. Math: [line items]. Risks: [separate section]. Let's go.`
Full rule: `/Users/entity002/CLAUDE.md` (top banner) + `/Users/entity002/.claude/projects/-Users-entity002/memory/feedback_always_positive_energy.md`. Stop-hook enforcement: `/Users/entity002/.claude/hooks/synthesis-stop-validator.sh`.
---
# Vestige Agent Guidance
# Vestige v2.0.4 — Cognitive Memory & Reasoning System
This file is intentionally safe for the public repository. It gives coding
agents project-specific context without relying on private local files,
personal operating notes, or mandatory background hooks.
Vestige is your long-term memory AND reasoning engine. 29 stateful cognitive modules implement real neuroscience: FSRS-6 spaced repetition, synaptic tagging, prediction error gating, hippocampal indexing, spreading activation, reconsolidation, and dual-strength memory theory. **Use it automatically. Use it aggressively.**
## Project Shape
**NEW: `deep_reference` — call this for ALL factual questions.** It doesn't just retrieve — it REASONS across memories with FSRS-6 trust scoring, intent classification, contradiction analysis, and generates a pre-built reasoning chain. Read the `reasoning` field FIRST.
Vestige is a local-first MCP memory server written in Rust, with a SvelteKit
dashboard embedded into the release binary. The core product promise is:
---
- user-owned memory stored locally by default
- MCP-native integration with coding agents
- retrieval and memory lifecycle behavior informed by cognitive science
- explicit tools for search, review, suppression, purge, graph exploration,
contradiction inspection, and maintenance
## Session Start Protocol
## Working Rules
Every conversation, before responding to the user:
- Prefer source evidence over memory. Use `rg`, tests, and nearby code before
making claims about behavior.
- Keep release changes scoped. Do not rewrite unrelated modules during a
version/tag cleanup unless the release gate requires it.
- Preserve local-first behavior. Heavy models, Sanhedrin-style verifier hooks,
and preflight automation must remain optional.
- Treat deletion semantics carefully. `purge` must remove content and
embeddings, while retaining only content-free audit tombstones.
- Treat exact lookup semantics carefully. Env vars, paths, UUIDs, quoted
strings, and code identifiers should not be distorted by semantic expansion.
## Common Checks
Run the narrowest check that covers the change, then run the release gates
before tagging:
```sh
cargo test --workspace --no-fail-fast
cargo clippy --workspace -- -D warnings
pnpm --filter @vestige/dashboard check
pnpm --filter @vestige/dashboard build
```
session_context({
queries: ["user preferences", "[current project] context"],
context: { codebase: "[project]", topics: ["[current topics]"] },
token_budget: 2000
})
```
For documentation-only changes, at minimum run:
Then check `automationTriggers` from response:
- `needsDream` → call `dream` (consolidates memories, discovers hidden connections)
- `needsBackup` → call `backup`
- `needsGc` → call `gc(dry_run: true)` then review
- totalMemories > 700 → call `find_duplicates`
```sh
git diff --check
Say "Remembering..." then retrieve context before answering.
> **Fallback:** If `session_context` unavailable: `search` × 2 → `intention` check → `system_status``predict`.
---
## Complete Tool Reference (23 Tools)
### session_context — One-Call Initialization
```
session_context({
queries: ["user preferences", "project context"], // search queries
context: { codebase: "project-name", topics: ["svelte", "rust"], file: "src/main.rs" },
token_budget: 2000, // 100-100000, controls response size
include_status: true, // system health
include_intentions: true, // triggered reminders
include_predictions: true // proactive memory predictions
})
```
Returns: markdown context + `automationTriggers` + `expandable` IDs for on-demand retrieval.
### smart_ingest — Save Anything
**Single mode** — auto-decides CREATE/UPDATE/SUPERSEDE via Prediction Error Gating:
```
smart_ingest({
content: "What to remember",
tags: ["tag1", "tag2"],
node_type: "fact", // fact|concept|event|person|place|note|pattern|decision
source: "optional reference",
forceCreate: false // bypass dedup when needed
})
```
**Batch mode** — save up to 20 items in one call (session end, pre-compaction):
```
smart_ingest({
items: [
{ content: "Item 1", tags: ["session-end"], node_type: "fact" },
{ content: "Item 2", tags: ["bug-fix"], node_type: "fact" }
]
})
```
Each item runs the full cognitive pipeline: importance scoring → intent detection → synaptic tagging → hippocampal indexing → PE gating → cross-project recording.
### search — 7-Stage Cognitive Search
```
search({
query: "search query",
limit: 10, // 1-100
min_retention: 0.0, // filter by retention strength
min_similarity: 0.5, // minimum cosine similarity
detail_level: "summary", // brief|summary|full
context_topics: ["rust", "debugging"], // boost topic-matching memories
token_budget: 3000, // 100-100000, truncate to fit
retrieval_mode: "balanced" // precise|balanced|exhaustive (v2.1)
})
```
Retrieval modes: `precise` (fast, no activation/competition), `balanced` (default 7-stage pipeline), `exhaustive` (5x overfetch, deep graph traversal, no competition suppression).
Pipeline: Overfetch → Rerank (cross-encoder) → Temporal boost → Accessibility filter (FSRS-6) → Context match (Tulving 1973) → Competition (Anderson 1994) → Spreading activation. **Every search strengthens the memories it finds (Testing Effect).**
### memory — Read, Edit, Delete, Promote, Demote
```
memory({ action: "get", id: "uuid" }) // full node with all FSRS state
memory({ action: "edit", id: "uuid", content: "updated text" }) // preserves FSRS state, regenerates embedding
memory({ action: "delete", id: "uuid" })
memory({ action: "promote", id: "uuid", reason: "was helpful" }) // +0.20 retrieval, +0.10 retention, 1.5x stability
memory({ action: "demote", id: "uuid", reason: "was wrong" }) // -0.30 retrieval, -0.15 retention, 0.5x stability
memory({ action: "state", id: "uuid" }) // Active/Dormant/Silent/Unavailable + accessibility score
memory({ action: "get_batch", ids: ["uuid1", "uuid2", "uuid3"] }) // retrieve up to 20 full memories at once (v2.1)
```
Promote/demote does NOT delete — it adjusts ranking. Demoted memories rank lower; alternatives surface instead.
`get_batch` is designed for batch retrieval of expandable overflow IDs from search/session_context.
### codebase — Code Patterns & Architectural Decisions
```
codebase({ action: "remember_pattern", name: "Pattern Name",
description: "How it works and when to use it",
files: ["src/file.rs"], codebase: "project-name" })
codebase({ action: "remember_decision", decision: "What was decided",
rationale: "Why", alternatives: ["Option A", "Option B"],
files: ["src/file.rs"], codebase: "project-name" })
codebase({ action: "get_context", codebase: "project-name", limit: 10 })
// Returns: patterns, decisions, cross-project insights
```
## Documentation
### intention — Prospective Memory (Reminders)
```
intention({ action: "set", description: "What to do",
trigger: { type: "context", topic: "authentication" }, // fires when discussing auth
priority: "high" })
- User setup: `README.md`
- Claude-specific templates: `docs/CLAUDE-SETUP.md`
- Storage and sync behavior: `docs/STORAGE.md`
- Cognitive Sandwich and optional verifier hooks: `docs/COGNITIVE_SANDWICH.md`
- Release history: `CHANGELOG.md`
intention({ action: "set", description: "Deploy by Friday",
trigger: { type: "time", at: "2026-03-07T17:00:00Z" },
deadline: "2026-03-07T17:00:00Z" })
## Public-Repo Hygiene
intention({ action: "set", description: "Check test coverage",
trigger: { type: "context", codebase: "vestige", file_pattern: "*.test.*" } })
Do not commit private absolute paths, local agent memory paths, unpublished
planning files, real credentials, personal operating notes, or private repo
locations. Example environment variables in docs must be empty placeholders or
obviously fake examples.
intention({ action: "check", context: { codebase: "vestige", topics: ["testing"] } })
intention({ action: "update", id: "uuid", status: "complete" })
intention({ action: "list", filter_status: "active" })
```
### dream — Memory Consolidation
```
dream({ memory_count: 50 })
```
5-stage cycle: Replay → Cross-reference → Strengthen → Prune → Transfer. Uses Waking SWR tagging (70% tagged + 30% random for diversity). Discovers hidden connections, generates insights, persists new edges to the activation network.
### explore_connections — Graph Traversal
```
explore_connections({ action: "associations", from: "uuid", limit: 10 })
// Spreading activation from a memory — find related memories via graph traversal
explore_connections({ action: "chain", from: "uuid-A", to: "uuid-B" })
// Build reasoning path between two memories (A*-like pathfinding)
explore_connections({ action: "bridges", from: "uuid-A", to: "uuid-B" })
// Find connecting memories that bridge two concepts
```
### predict — Proactive Retrieval
```
predict({ context: { codebase: "vestige", current_file: "src/main.rs",
current_topics: ["error handling", "rust"] } })
```
Returns: predictions with confidence, suggestions, speculative retrievals, top interests. Uses SpeculativeRetriever's learned patterns from access history.
### importance_score — Should I Save This?
```
importance_score({ content: "Content to evaluate",
context_topics: ["debugging"], project: "vestige" })
```
4-channel model: novelty (0.25), arousal (0.30), reward (0.25), attention (0.20). Composite > 0.6 = save it.
### find_duplicates — Dedup Memory
```
find_duplicates({ similarity_threshold: 0.80, limit: 20, tags: ["bug-fix"] })
```
Cosine similarity clustering. Returns merge/review suggestions.
### memory_timeline — Chronological Browse
```
memory_timeline({ start: "2026-02-01", end: "2026-03-01",
node_type: "decision", tags: ["vestige"], limit: 50, detail_level: "summary" })
```
### memory_changelog — Audit Trail
```
memory_changelog({ memory_id: "uuid", limit: 20 }) // per-memory history
memory_changelog({ start: "2026-03-01", limit: 20 }) // system-wide
```
### memory_health — Retention Dashboard
```
memory_health()
```
Returns: avg retention, distribution buckets (0-20%, 20-40%, etc.), trend (improving/declining/stable), recommendation.
### memory_graph — Visualization Export
```
memory_graph({ query: "search term", depth: 2, max_nodes: 50 })
memory_graph({ center_id: "uuid", depth: 3, max_nodes: 100 })
```
Returns nodes with force-directed positions + edges with weights.
### deep_reference — Cognitive Reasoning Engine (v2.0.4) ★ USE THIS FOR ALL FACTUAL QUESTIONS
```
deep_reference({ query: "What port does the dev server use?" })
deep_reference({ query: "Should I use prefix caching with vLLM?", depth: 30 })
```
**THE killer tool.** 8-stage cognitive reasoning pipeline:
1. Broad retrieval + cross-encoder reranking
2. Spreading activation expansion (finds connected memories search misses)
3. FSRS-6 trust scoring (retention × stability × reps ÷ lapses)
4. Intent classification (FactCheck / Timeline / RootCause / Comparison / Synthesis)
5. Temporal supersession (newer high-trust replaces older)
6. Trust-weighted contradiction analysis (only flags conflicts between strong memories)
7. Relation assessment (Supports / Contradicts / Supersedes / Irrelevant per pair)
8. **Template reasoning chain** — pre-built natural language reasoning the AI validates
Parameters: `query` (required), `depth` (5-50, default 20).
Returns: `intent`, `reasoning` (THE KEY FIELD — read this first), `recommended` (highest-trust answer), `evidence` (trust-sorted), `contradictions`, `superseded`, `evolution`, `related_insights`, `confidence`.
`cross_reference` is a backward-compatible alias that calls `deep_reference`.
### Maintenance Tools
```
system_status() // health + stats + warnings + recommendations
consolidate() // FSRS-6 decay cycle + embedding generation
backup() // SQLite backup → ~/.vestige/backups/
export({ format: "json", tags: ["bug-fix"], since: "2026-01-01" })
gc({ min_retention: 0.1, dry_run: true }) // garbage collect (dry_run first!)
restore({ path: "/path/to/backup.json" })
```
---
## Mandatory Save Gates
**You MUST NOT proceed past a save gate without executing the save.**
| Gate | Trigger | Action |
|------|---------|--------|
| **BUG_FIX** | After any error is resolved | `smart_ingest({ content: "BUG FIX: [error]\nRoot cause: [why]\nSolution: [fix]\nFiles: [paths]", tags: ["bug-fix", "project"], node_type: "fact" })` |
| **DECISION** | After any architectural/design choice | `codebase({ action: "remember_decision", decision, rationale, alternatives, files, codebase })` |
| **CODE_CHANGE** | After >20 lines or new pattern | `codebase({ action: "remember_pattern", name, description, files, codebase })` |
| **SESSION_END** | Before stopping or compaction | `smart_ingest({ items: [{ content: "SESSION: [summary]", tags: ["session-end"] }] })` |
---
## Trigger Words — Auto-Save
| User Says | Action |
|-----------|--------|
| "Remember this" / "Don't forget" | `smart_ingest` immediately |
| "I always..." / "I never..." / "I prefer..." | Save as preference |
| "This is important" | `smart_ingest` + `memory(action="promote")` |
| "Remind me..." / "Next time..." | `intention({ action: "set" })` |
---
## Cognitive Architecture
### Search Pipeline (7 stages)
1. **Overfetch** — 3x results from hybrid search (0.3 BM25 + 0.7 semantic, nomic-embed-text-v1.5 768D)
2. **Rerank** — Cross-encoder rescoring (Jina Reranker v1 Turbo, 38M params)
3. **Temporal** — Recency + validity window boosting (85% relevance + 15% temporal)
4. **Accessibility** — FSRS-6 retention filter (Active ≥0.7, Dormant ≥0.4, Silent ≥0.1)
5. **Context** — Tulving 1973 encoding specificity (topic overlap → +30% boost)
6. **Competition** — Anderson 1994 retrieval-induced forgetting (winners strengthen, competitors weaken)
7. **Activation** — Spreading activation side effects + predictive model + reconsolidation marking
### Ingest Pipeline
**Pre:** 4-channel importance scoring (novelty/arousal/reward/attention) + intent detection → auto-tag
**Store:** Prediction Error Gating: similarity >0.92 → UPDATE, 0.75-0.92 → UPDATE/SUPERSEDE, <0.75 CREATE
**Post:** Synaptic tagging (Frey & Morris 1997, 9h backward + 2h forward) + hippocampal indexing + cross-project recording
### FSRS-6 (State-of-the-Art Spaced Repetition)
- Retrievability: `R = (1 + factor × t / S)^(-w20)` — 21 trained parameters
- Dual-strength model (Bjork & Bjork 1992): storage strength (grows) + retrieval strength (decays)
- Accessibility = retention×0.5 + retrieval×0.3 + storage×0.2
- 20-30% more efficient than SM-2 (Anki)
### 29 Cognitive Modules (stateful, persist across calls)
**Neuroscience (16):**
ActivationNetwork (Collins & Loftus 1975), SynapticTaggingSystem (Frey & Morris 1997), HippocampalIndex (Teyler & Rudy 2007), ContextMatcher (Tulving 1973), AccessibilityCalculator, CompetitionManager (Anderson 1994), StateUpdateService, ImportanceSignals, NoveltySignal, ArousalSignal, RewardSignal, AttentionSignal, EmotionalMemory (Brown & Kulik 1977), PredictiveMemory, ProspectiveMemory, IntentionParser
**Advanced (11):**
ImportanceTracker, ReconsolidationManager (Nader — 5min labile window), IntentDetector (9 intent types), ActivityTracker, MemoryDreamer (5-stage consolidation), MemoryChainBuilder (A*-like), MemoryCompressor (30-day min age), CrossProjectLearner (6 pattern types), AdaptiveEmbedder, SpeculativeRetriever (6 trigger types), ConsolidationScheduler
**Search (2):** Reranker, TemporalSearcher
### Memory States
- **Active** (retention ≥ 0.7) — easily retrievable
- **Dormant** (≥ 0.4) — retrievable with effort
- **Silent** (≥ 0.1) — difficult, needs cues
- **Unavailable** (< 0.1) needs reinforcement
### Connection Types
semantic, temporal, causal, spatial, part_of, user_defined — each with strength (0-1), activation_count, timestamps
---
## Advanced Techniques
### Cross-Project Intelligence
The CrossProjectLearner tracks patterns across ALL projects (ErrorHandling, AsyncConcurrency, Testing, Architecture, Performance, Security). When you learn a pattern in one project that works, it becomes available in all projects. Use `codebase({ action: "get_context" })` without a codebase filter to get universal patterns.
### Reconsolidation Window
After any memory is accessed (via search, get, or promote), it enters a 5-minute "labile" state where modifications are enhanced. This is the optimal time to edit memories with new context. The system handles this automatically.
### Synaptic Tagging (Retroactive Importance)
Memories encoded in the last 9 hours can be retroactively promoted when something important happens. If you fix a critical bug, not only does the fix get saved — related memories from the past 9 hours also get importance boosts. The SynapticTaggingSystem handles this automatically.
### Dream Insights
Dreams don't just consolidate — they generate new insights by cross-referencing recent memories with older knowledge. The insights can reveal: contradictions between memories, previously unseen patterns, connections across different projects. Always check dream results for `insights_generated`.
### Token Budget Strategy
Use `token_budget` on search and session_context to control response size. For quick lookups: 500. For deep context: 3000-5000. Results that don't fit go to `expandable` — retrieve them with `memory({ action: "get", id: "..." })`.
### Detail Levels
- `brief` — id/type/tags/score only (1-2 tokens per result, good for scanning)
- `summary` — 8 fields including content preview (default, balanced)
- `full` — all FSRS state, timestamps, embedding info (for debugging/analysis)
---
## Memory Hygiene
**Promote** when user confirms helpful, solution worked, info was accurate.
**Demote** when user corrects mistake, info was wrong, led to bad outcome.
**Never save:** secrets, API keys, passwords, temporary debugging state, trivial info.
---
## The One Rule
**When in doubt, save. The cost of a duplicate is near zero (Prediction Error Gating handles dedup). The cost of lost knowledge is permanent.**
Memory is retrieval. Searching strengthens memory. Search liberally, save aggressively.
---
## Development
- **Crate:** `vestige-mcp` v2.0.4, Rust 2024 edition, MSRV 1.91
- **Tests:** 758 (406 mcp + 352 core), zero warnings
- **Build:** `cargo build --release -p vestige-mcp` (features: `embeddings` + `vector-search`)
- **Build (no embeddings):** `cargo build --release -p vestige-mcp --no-default-features`
- **Bench:** `cargo bench -p vestige-core`
- **Architecture:** `McpServer``Arc<Storage>` + `Arc<Mutex<CognitiveEngine>>`
- **Storage:** SQLite WAL mode, `Mutex<Connection>` reader/writer split, FTS5 full-text search
- **Embeddings:** nomic-embed-text-v1.5 (768D, 8K context) via fastembed (local ONNX, no API)
- **Vector index:** USearch HNSW (20x faster than FAISS)
- **Binaries:** `vestige-mcp` (MCP server), `vestige` (CLI), `vestige-restore`
- **Dashboard:** SvelteKit 2 + Svelte 5 + Three.js + Tailwind 4, embedded at `/dashboard`
- **Env vars:** `VESTIGE_DASHBOARD_PORT` (default 3927), `VESTIGE_HTTP_PORT` (default 3928), `VESTIGE_HTTP_BIND` (default 127.0.0.1), `VESTIGE_AUTH_TOKEN` (auto-generated), `VESTIGE_CONSOLIDATION_INTERVAL_HOURS` (default 6), `RUST_LOG`

View file

@ -88,7 +88,7 @@ Tags: ["decision", "topic-name"]
| "Don't forget" | `smart_ingest` with tags: ["important"] |
| "I always..." / "I never..." | Save as preference |
| "I prefer..." / "I like..." | Save as preference |
| "This is important" | `smart_ingest` + `memory(action="promote")` |
| "This is important" | `smart_ingest` + `promote_memory` |
| "Remind me..." | Create `intention` with trigger |
| "Next time we..." | Create `intention` with context trigger |
| "When I'm working on X..." | Create `intention` with codebase trigger |
@ -115,7 +115,7 @@ Act on feedback immediately — don't ask permission to promote/demote.
### Proactive Health Checks
If you notice degraded recall or a user mentions memory issues:
1. Run `system_status` — check overall system status
1. Run `health_check` — check overall system status
2. If `averageRetention < 0.5` → suggest running `consolidate`
3. If `dueForReview > 50` → mention that some memories need review

452
Cargo.lock generated
View file

@ -8,16 +8,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "ahash"
version = "0.8.12"
@ -153,24 +143,6 @@ dependencies = [
"syn",
]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures 0.2.17",
"password-hash",
]
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
@ -339,29 +311,6 @@ dependencies = [
"core2",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "blake3"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures 0.3.0",
]
[[package]]
name = "block"
version = "0.1.6"
@ -443,10 +392,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd9895436c1ba5dc1037a19935d084b838db066ff4e15ef7dded020b7c12a4a"
dependencies = [
"byteorder",
"candle-kernels",
"candle-metal-kernels",
"candle-ug",
"cudarc 0.19.7",
"float8",
"gemm 0.19.0",
"half",
@ -466,15 +413,6 @@ dependencies = [
"zip",
]
[[package]]
name = "candle-kernels"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742e2ac226b777134436e9e692f44e77c278b8a7abb1554dc10e44dc911b349f"
dependencies = [
"cudaforge",
]
[[package]]
name = "candle-metal-kernels"
version = "0.10.2"
@ -515,7 +453,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0fc3167cbc99c8ec1be618cb620aa21dca95038f118c3579a79370e3dc5f77"
dependencies = [
"ug",
"ug-cuda",
"ug-metal",
]
@ -552,36 +489,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures 0.2.17",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.44"
@ -623,17 +530,6 @@ dependencies = [
"half",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]
name = "clap"
version = "4.6.0"
@ -734,12 +630,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -795,15 +685,6 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@ -887,50 +768,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"typenum",
]
[[package]]
name = "cudaforge"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f7a0d45b139b5beeeb1c34188717e12241c44a0120afb498815ce7f5373c691"
dependencies = [
"anyhow",
"fs2",
"glob",
"num_cpus",
"rayon",
"serde",
"serde_json",
"sha2",
"thiserror 2.0.18",
"walkdir",
"which",
]
[[package]]
name = "cudarc"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf99ab37ee7072d64d906aa2dada9a3422f1d975cdf8c8055a573bc84897ed8"
dependencies = [
"half",
"libloading 0.8.9",
]
[[package]]
name = "cudarc"
version = "0.19.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cea5f10a99e025c1b44ae2354c2d8326b25ddbd0baf76bde8e55cfd4018a2cc"
dependencies = [
"float8",
"half",
"libloading 0.9.0",
]
[[package]]
name = "cxx"
version = "1.0.194"
@ -1092,7 +932,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -1195,12 +1034,6 @@ dependencies = [
"syn",
]
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "equator"
version = "0.4.2"
@ -1421,16 +1254,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
@ -1447,7 +1270,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -1756,10 +1578,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -1769,11 +1589,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
@ -1814,12 +1632,6 @@ dependencies = [
"url",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "h2"
version = "0.4.13"
@ -2011,7 +1823,6 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.6",
]
[[package]]
@ -2298,15 +2109,6 @@ dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "interpolate_name"
version = "0.2.4"
@ -2406,10 +2208,12 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.91"
version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
@ -2608,12 +2412,6 @@ dependencies = [
"hashbrown 0.16.1",
]
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "lzma-rust2"
version = "0.15.7"
@ -3090,12 +2888,6 @@ version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "open"
version = "5.3.3"
@ -3221,17 +3013,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.15"
@ -3318,17 +3099,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures 0.2.17",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
@ -3337,9 +3107,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3"
dependencies = [
"portable-atomic",
]
@ -3458,61 +3228,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]]
name = "quote"
version = "1.0.45"
@ -3541,7 +3256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
"rand_core",
]
[[package]]
@ -3551,16 +3266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
"rand_core",
]
[[package]]
@ -3742,7 +3448,6 @@ dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-channel",
"futures-core",
"futures-util",
"h2",
@ -3759,8 +3464,6 @@ dependencies = [
"native-tls",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
@ -3768,7 +3471,6 @@ dependencies = [
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
@ -3778,7 +3480,6 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 1.0.6",
]
[[package]]
@ -3828,12 +3529,6 @@ dependencies = [
"sqlite-wasm-rs",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustix"
version = "1.1.4"
@ -3868,7 +3563,6 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"web-time",
"zeroize",
]
@ -4054,18 +3748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures",
"digest",
]
@ -4361,21 +4044,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokenizers"
version = "0.22.2"
@ -4603,17 +4271,6 @@ dependencies = [
"tracing-serde",
]
[[package]]
name = "trait-variant"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@ -4670,19 +4327,6 @@ dependencies = [
"yoke 0.7.5",
]
[[package]]
name = "ug-cuda"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f0a1fa748f26166778c33b8498255ebb7c6bffb472bcc0a72839e07ebb1d9b5"
dependencies = [
"cudarc 0.17.8",
"half",
"serde",
"thiserror 1.0.69",
"ug",
]
[[package]]
name = "ug-metal"
version = "0.5.0"
@ -4742,16 +4386,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"
@ -4897,12 +4531,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vestige-core"
version = "2.2.1"
version = "2.1.1"
dependencies = [
"argon2",
"blake3",
"candle-core",
"chacha20poly1305",
"chrono",
"criterion",
"directories",
@ -4910,7 +4541,6 @@ dependencies = [
"git2",
"lru",
"notify",
"reqwest",
"rusqlite",
"serde",
"serde_json",
@ -4918,7 +4548,6 @@ dependencies = [
"thiserror 2.0.18",
"tokio",
"tracing",
"trait-variant",
"usearch",
"uuid",
]
@ -4938,7 +4567,7 @@ dependencies = [
[[package]]
name = "vestige-mcp"
version = "2.2.1"
version = "2.1.1"
dependencies = [
"anyhow",
"axum",
@ -4965,19 +4594,6 @@ dependencies = [
"vestige-core",
]
[[package]]
name = "vestige-phase-1-tests"
version = "0.0.1"
dependencies = [
"chrono",
"rusqlite",
"serde_json",
"tempfile",
"tokio",
"uuid",
"vestige-core",
]
[[package]]
name = "walkdir"
version = "2.5.0"
@ -5023,9 +4639,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
dependencies = [
"cfg-if",
"once_cell",
@ -5036,23 +4652,19 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.64"
version = "0.4.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8"
checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8"
dependencies = [
"cfg-if",
"futures-util",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -5060,9 +4672,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
dependencies = [
"bumpalo",
"proc-macro2",
@ -5073,9 +4685,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.114"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
dependencies = [
"unicode-ident",
]
@ -5129,9 +4741,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.91"
version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -5180,18 +4792,6 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "which"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
dependencies = [
"either",
"env_home",
"rustix",
"winsafe",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -5458,12 +5058,6 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.51.0"

View file

@ -4,14 +4,13 @@ members = [
"crates/vestige-core",
"crates/vestige-mcp",
"tests/e2e",
"tests/phase_1",
]
exclude = [
"fastembed-rs",
]
[workspace.package]
version = "2.2.1"
version = "2.1.1"
edition = "2024"
license = "AGPL-3.0-only"
repository = "https://github.com/samvallad33/vestige"

View file

@ -1,28 +0,0 @@
# Dockerfile for running the Vestige MCP server in an isolated sandbox.
#
# Used by registries such as Glama to start the server and run the standard
# MCP stdio introspection exchange (tools/list, resources/list, prompts/list).
# The server speaks MCP over stdio, which is exactly what these tools expect.
#
# Base must be glibc (Debian), not musl/Alpine: the npm postinstall downloads
# the prebuilt x86_64-unknown-linux-gnu Rust binary from the GitHub release, and
# a -gnu binary will not run on an Alpine/musl image.
FROM node:20-slim
# ca-certificates lets the postinstall fetch the release asset over HTTPS.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install the published package globally. Its postinstall downloads the matching
# prebuilt vestige-mcp binary for linux/x64 from the GitHub release.
RUN npm install -g vestige-mcp-server@latest
# Keep all memory data inside the container under a writable path.
ENV VESTIGE_DATA_DIR=/data
RUN mkdir -p /data
# Start the MCP server on stdio. The `vestige-mcp` bin execs the native binary
# and inherits stdio, so the MCP client talks to it directly.
ENTRYPOINT ["vestige-mcp"]

612
README.md
View file

@ -1,43 +1,157 @@
<div align="center">
# Vestige
Local-first long-term memory for AI agents, delivered over MCP. Vestige remembers your decisions, catches contradictions before they cost you, and traces a failure back to the older memory that actually caused it. One 25MB Rust binary. No cloud. Your data never leaves your machine.
### The cognitive engine that gives AI agents a brain.
[![Release](https://img.shields.io/github/v/release/samvallad33/vestige?color=06b6d4)](https://github.com/samvallad33/vestige/releases/latest)
[![Tests](https://img.shields.io/badge/tests-1550_passing-22c55e)](https://github.com/samvallad33/vestige/actions)
[![Binary](https://img.shields.io/badge/binary-25MB_single_file-informational)](https://github.com/samvallad33/vestige/releases/latest)
[![License](https://img.shields.io/badge/license-AGPL--3.0-3b82f6)](LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/samvallad33/vestige?style=social)](https://github.com/samvallad33/vestige)
[![Release](https://img.shields.io/github/v/release/samvallad33/vestige)](https://github.com/samvallad33/vestige/releases/latest)
[![Tests](https://img.shields.io/badge/tests-1229%20passing-brightgreen)](https://github.com/samvallad33/vestige/actions)
[![License](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE)
[![MCP Compatible](https://img.shields.io/badge/MCP-compatible-green)](https://modelcontextprotocol.io)
[What it is](#what-vestige-is) · [Install](#install) · [First interaction](#your-first-real-interaction) · [vs RAG](#how-it-differs-from-rag) · [Backward reach](#backward-reach-the-backfill-feature) · [Benchmark](#causebench-a-reproducible-benchmark) · [Science](#the-science) · [Tools](#the-13-tools) · [Dashboard](#the-dashboard) · [Integrations](#works-with-every-agent) · [Docs](#go-deeper)
**Your Agent forgets everything between sessions. Vestige fixes that.**
Built on 130 years of memory research — FSRS-6 spaced repetition, prediction error gating, synaptic tagging, spreading activation, memory dreaming — all running in a single Rust binary with a 3D neural visualization dashboard. 100% local. Zero cloud.
[Quick Start](#quick-start) | [Dashboard](#-3d-memory-dashboard) | [How It Works](#-the-cognitive-science-stack) | [Tools](#-24-mcp-tools) | [Docs](docs/)
</div>
---
## What Vestige is
## What's New in v2.1.1 "Portable Sync"
Hi, I'm [Sam](https://github.com/samvallad33). I built Vestige because my agents kept re-learning the same lessons. They would recommend a change I had already tested and rejected, re-derive a fix that was already written down, and treat every session as if the last one never happened.
v2.1.1 focuses on the biggest post-launch ask: move memories between machines without losing cognitive state. It also adds opt-in Qwen3 embeddings for higher-recall local retrieval.
Vestige is the memory layer that fixes that. It runs locally as an MCP server, so any MCP-capable agent (Claude Code, Claude Desktop, Codex, Cursor, and others) can write memories during a session and retrieve them later. Your data lives in a SQLite file on your own machine. After a one-time model download it works fully offline, with no API keys and no telemetry.
- **Exact portable archives.** `vestige portable-export` / `vestige portable-import` preserve IDs, FSRS state, graph edges, suppression state, audit rows, and embedding blobs for Vestige-to-Vestige device transfer.
- **Sync-safe merge storage.** `vestige portable-import --merge` and `vestige sync <archive>` merge non-empty databases, apply delete tombstones, keep newer local memories, rebuild FTS, and push through a pluggable portable-sync backend. v2.1.1 ships the file backend for Dropbox, iCloud, Syncthing, Git, and shared folders.
- **Qwen3 embeddings.** Build with `qwen3-embeddings`, set `VESTIGE_EMBEDDING_MODEL=qwen3-0.6b`, and run `vestige consolidate` to re-embed existing memories.
- **Model-aware retrieval.** Vestige now avoids comparing Qwen and Nomic vectors in the same search/dedup path.
The part that makes it more than a note store: Vestige models memory on real cognitive science. It merges what is redundant, supersedes what is contradicted, keeps what you actually use, and lets unused memories fade. Most importantly, when a failure hits it can reach backward to the earlier decision that caused it, even when the cause and the symptom share no vocabulary. The cause never looks like the bug.
## What's New in v2.1.0 "Cognitive Sandwich Goes Local"
v2.1.0 adds an opt-in Claude Code hook harness around the existing Vestige MCP server. The MCP tool surface and database schema stay backward compatible, while preflight hooks can inject trusted memory context before Claude answers. The heavyweight Sanhedrin verifier is optional and can be enabled separately.
- **Optional Sanhedrin Executioner.** The post-response verifier is off by default. Users can enable it with an OpenAI-compatible endpoint on x86/Linux/Intel Mac, or add `--with-launchd` on Apple Silicon to run the local MLX Qwen backend.
- **One-command Cognitive Sandwich installer.** `scripts/install-sandwich.sh` stages hook files and agents by default, removes old Vestige hook wiring, and leaves all Claude Code hook layers plus the 19 GB model path opt-in.
- **Pulse hook backed by `/api/changelog`.** Fresh dream and connection events can be injected into the next Claude Code prompt context without blocking the prompt.
- **`VESTIGE_DATA_DIR` support.** `--data-dir` now has an env-var fallback, tilde expansion, secure directory creation, and clear precedence docs.
- **NPM release wrapper fixed.** `vestige-mcp-server@2.1.0` now downloads binaries from the matching `v2.1.0` GitHub release tag instead of an old hardcoded release.
## What's New in v2.0.9 "Autopilot"
Autopilot flips Vestige from passive memory library to **self-managing cognitive surface**. Same 24 MCP tools, zero schema changes — but the moment you upgrade, 14 previously dormant cognitive primitives start firing on live events without any tool call from your client.
- **One supervised backend task subscribes to the 20-event WebSocket bus** and routes six event classes into the cognitive engine: `MemoryCreated` triggers synaptic-tagging PRP + predictive-access records, `SearchPerformed` warms the speculative-retrieval model, `MemoryPromoted` fires activation spread, `MemorySuppressed` emits the Rac1 cascade wave, high-importance `ImportanceScored` (>0.85) auto-promotes, and `Heartbeat` rate-limit-fires `find_duplicates` on large DBs. **The engine mutex is never held across `.await`, so MCP dispatch is never starved.**
- **Panic-resilient supervisors.** Both background tasks run inside an outer supervisor loop — if one handler panics on a bad memory, the supervisor respawns it in 5 s instead of losing every future event.
- **Fully backward compatible.** No new MCP tools. No schema migration. Existing v2.0.8 databases open without a single step. Opt out with `VESTIGE_AUTOPILOT_ENABLED=0` if you want the passive-library contract back.
- **3,091 LOC of orphan v1.0 tool code removed** — nine superseded modules (`checkpoint`, `codebase`, `consolidate`, `ingest`, `intentions`, `knowledge`, `recall`, plus helpers) verified zero non-test callers before deletion. Tool surface unchanged.
## What's New in v2.0.8 "Pulse"
v2.0.8 wires the dashboard through to the cognitive engine. Eight new surfaces expose the reasoning stack visually — every one was MCP-only before.
- **Reasoning Theater (`/reasoning`)**`Cmd+K` Ask palette over the 8-stage `deep_reference` pipeline (hybrid retrieval → cross-encoder rerank → spreading activation → FSRS-6 trust → temporal supersession → contradiction analysis → relation assessment → template reasoning chain). Evidence cards, confidence meter, contradiction geodesic arcs, superseded-memory lineage, evolution timeline. **Zero LLM calls, 100% local.**
- **Pulse InsightToast** — real-time toasts for `DreamCompleted`, `ConsolidationCompleted`, `ConnectionDiscovered`, promote/demote/suppress/unsuppress, `Rac1CascadeSwept`. Rate-limited, auto-dismiss, click-to-dismiss.
- **Memory Birth Ritual (Terrarium)** — new memories materialize in the 3D graph on every `MemoryCreated`: elastic scale-in, quadratic Bezier flight path, glow sprite fade-in, Newton's Cradle docking recoil. 60-frame sequence, zero-alloc math.
- **7 more dashboard surfaces**`/duplicates`, `/dreams`, `/schedule`, `/importance`, `/activation`, `/contradictions`, `/patterns`. Left nav expanded 8 → 16 with single-key shortcuts.
- **Intel Mac (`x86_64-apple-darwin`) support restored** via the `ort-dynamic` Cargo feature + Homebrew `onnxruntime`. Microsoft deprecated x86_64 macOS prebuilts; the dynamic-link path sidesteps that permanently. **Closes #41.**
- **Contradiction-detection false positives eliminated** — four thresholds tightened so adjacent-domain memories no longer flag as conflicts. On an FSRS-6 query this collapses false contradictions 12 → 0 without regressing legitimate test cases.
## What's New in v2.0.7 "Visible"
Hygiene release closing two UI gaps and finishing schema cleanup. No breaking changes, no user-data migrations.
- **`POST /api/memories/{id}/suppress` + `/unsuppress` HTTP endpoints** — dashboard can trigger Anderson 2025 SIF + Rac1 cascade without dropping to raw MCP. `suppressionCount`, `retrievalPenalty`, `reversibleUntil`, `labileWindowHours` all in response. Suppress button joins Promote / Demote / Delete on the Memories page.
- **Uptime in the sidebar footer** — the `Heartbeat` event has carried `uptime_secs` since v2.0.5 but was never rendered. Now shows as `up 3d 4h` / `up 18m` / `up 47s`.
- **`execute_export` panic fix** — unreachable match arm replaced with a clean "unsupported export format" error instead of unwinding through the MCP dispatcher.
- **`predict` surfaces `predict_degraded: true`** on lock poisoning instead of silently returning empty vecs. `memory_changelog` honors `start` / `end` bounds. `intention` check honors `include_snoozed`.
- **Migration V11** — drops dead `knowledge_edges` + `compressed_memories` tables (added speculatively in V4, never used).
## What's New in v2.0.6 "Composer"
v2.0.6 is a polish release that makes the existing cognitive stack finally *feel* alive in the dashboard and stays out of your way on the prompt side.
- **Six live graph reactions, not one**`MemorySuppressed`, `MemoryUnsuppressed`, `Rac1CascadeSwept`, `Connected`, `ConsolidationStarted`, and `ImportanceScored` now light the 3D graph in real time. v2.0.5 shipped `suppress` but the graph was silent when you called it; consolidation and importance scoring have been silent since v2.0.0. No longer.
- **Intentions page actually works** — fixes a long-standing bug where every intention rendered as "normal priority" (type/schema drift between backend and frontend) and context/time triggers surfaced as raw JSON.
- **Opt-in composition mandate** — the new MCP `instructions` string stays minimal by default. Opt in to the full Composing / Never-composed / Recommendation composition protocol with `VESTIGE_SYSTEM_PROMPT_MODE=full` when you want it, and nothing is imposed on your sessions when you don't.
## What's New in v2.0.5 "Intentional Amnesia"
**The first shipped AI memory system with top-down inhibitory control over retrieval.** Other systems implement passive decay — memories fade if you don't touch them. Vestige v2.0.5 also implements *active* suppression: the new **`suppress`** tool compounds a retrieval penalty on every call (up to 80%), a background Rac1 worker fades co-activated neighbors over 72 hours, and the whole thing is reversible within a 24-hour labile window. **Never deletes.** The memory is inhibited, not erased.
Ebbinghaus 1885 models what happens to memories you don't touch. Anderson 2025 models what happens when you actively want to stop thinking about one. Every other AI memory system implements the first. Vestige is the first to ship the second.
Based on [Anderson et al. 2025](https://www.nature.com/articles/s41583-025-00929-y) (Suppression-Induced Forgetting, *Nat Rev Neurosci*) and [Cervantes-Sandoval et al. 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7477079/) (Rac1 synaptic cascade). **24 tools · 30 cognitive modules · 1,223 tests.**
<details>
<summary>Earlier releases (v2.0 "Cognitive Leap" → v2.0.4 "Deep Reference")</summary>
- **v2.0.4 — `deep_reference` Tool** — 8-stage cognitive reasoning pipeline with FSRS-6 trust scoring, intent classification, spreading activation, contradiction analysis, and pre-built reasoning chains. Token budgets raised 10K → 100K. CORS tightened.
- **v2.0 — 3D Memory Dashboard** — SvelteKit + Three.js neural visualization with real-time WebSocket events, bloom post-processing, force-directed graph layout.
- **v2.0 — WebSocket Event Bus** — Every cognitive operation broadcasts events: memory creation, search, dreaming, consolidation, retention decay.
- **v2.0 — HyDE Query Expansion** — Template-based Hypothetical Document Embeddings for dramatically improved search quality on conceptual queries.
- **v2.0 — Nomic v2 MoE (experimental)** — fastembed 5.11 with optional Nomic Embed Text v2 MoE (475M params, 8 experts) + Metal GPU acceleration.
- **v2.0 — Command Palette**`Cmd+K` navigation, keyboard shortcuts, responsive mobile layout, PWA installable.
- **v2.0 — FSRS Decay Visualization** — SVG retention curves with predicted decay at 1d/7d/30d.
</details>
---
## Install
Three steps. You need Node.js installed (for the npm command) and nothing else.
### 1. Install the server
No Docker, no API key, no signup.
## Quick Start
```bash
# 1. Install (macOS Apple Silicon)
curl -L https://github.com/samvallad33/vestige/releases/latest/download/vestige-mcp-aarch64-apple-darwin.tar.gz | tar -xz
sudo mv vestige-mcp vestige vestige-restore /usr/local/bin/
# 2. Connect to Claude Code
claude mcp add vestige vestige-mcp -s user
# Or connect to Codex
codex mcp add vestige -- /usr/local/bin/vestige-mcp
# 3. Test it
# "Remember that I prefer TypeScript over JavaScript"
# ...new session...
# "What are my coding preferences?"
# → "You prefer TypeScript over JavaScript."
```
<details>
<summary>Other platforms & install methods</summary>
**Linux (x86_64):**
```bash
curl -L https://github.com/samvallad33/vestige/releases/latest/download/vestige-mcp-x86_64-unknown-linux-gnu.tar.gz | tar -xz
sudo mv vestige-mcp vestige vestige-restore /usr/local/bin/
```
**macOS (Intel):** Microsoft is discontinuing x86_64 macOS prebuilts after ONNX Runtime v1.23.0, so Vestige's Intel Mac build links dynamically against a Homebrew-installed ONNX Runtime via the `ort-dynamic` feature. Install with:
```bash
brew install onnxruntime
curl -L https://github.com/samvallad33/vestige/releases/latest/download/vestige-mcp-x86_64-apple-darwin.tar.gz | tar -xz
sudo mv vestige-mcp vestige vestige-restore /usr/local/bin/
echo 'export ORT_DYLIB_PATH="'"$(brew --prefix onnxruntime)"'/lib/libonnxruntime.dylib"' >> ~/.zshrc
source ~/.zshrc
claude mcp add vestige vestige-mcp -s user
```
Full Intel Mac guide (build-from-source + troubleshooting): [`docs/INSTALL-INTEL-MAC.md`](docs/INSTALL-INTEL-MAC.md).
**Windows + Claude Desktop (recommended):**
Fully quit Claude Desktop from the system tray, then install or update Vestige from PowerShell:
```powershell
npm install -g vestige-mcp-server@latest
vestige-mcp --version
```
This installs the `vestige-mcp` command. Prebuilt binaries ship for macOS (Apple Silicon and Intel), Linux x86_64, and Windows x86_64, so there is no compile step.
### 2. Connect it to your agent
Vestige speaks [MCP](https://modelcontextprotocol.io), so it works with any MCP-capable agent. Every MCP client understands this config. Add it to your client's MCP settings:
Open `%APPDATA%\Claude\claude_desktop_config.json` and point Claude Desktop at the installed MCP command:
```json
{
@ -49,237 +163,335 @@ Vestige speaks [MCP](https://modelcontextprotocol.io), so it works with any MCP-
}
```
If you prefer the CLI, use the one-line shortcut for your agent:
If Claude Desktop cannot find `vestige-mcp`, run `where vestige-mcp` in PowerShell and use the exact `.cmd` path it prints as `command`. Example: `"C:\\Users\\you\\AppData\\Roaming\\npm\\vestige-mcp.cmd"`. Reopen Claude Desktop after saving. Once v2.1.0 is installed, future binary updates can run with `vestige update`.
| Agent | Setup |
|---|---|
| Claude Code | `claude mcp add vestige vestige-mcp -s user` |
| Codex | `codex mcp add vestige -- vestige-mcp` |
| Cursor / VS Code / Windsurf | add the JSON above to the editor's MCP settings, or see [docs/integrations/](docs/integrations/) |
| Cline / Continue / Zed / Goose | add the JSON above to that client's MCP config |
| Claude Desktop | [docs/CONFIGURATION.md#claude-desktop-macos](docs/CONFIGURATION.md#claude-desktop-macos) |
### 3. Verify
On first run, Vestige downloads its embedding model once (about 130MB). After that it never needs the network again. To confirm the server is healthy, open the dashboard:
**Windows source build:** Prebuilt binaries ship but `usearch 2.24.0` hit an MSVC compile break ([usearch#746](https://github.com/unum-cloud/usearch/issues/746)); we've pinned `=2.23.0` until upstream fixes it. Source builds work with:
```bash
vestige dashboard
git clone https://github.com/samvallad33/vestige && cd vestige
cargo build --release -p vestige-mcp
```
Then visit **http://localhost:3927/dashboard**. If you see the graph, you are connected. For a fuller walkthrough see [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md).
**npm:**
```bash
npm install -g vestige-mcp-server
```
**Build from source (requires Rust 1.91+):**
```bash
git clone https://github.com/samvallad33/vestige && cd vestige
cargo build --release -p vestige-mcp
# Optional: enable Metal GPU acceleration on Apple Silicon
cargo build --release -p vestige-mcp --features metal
```
</details>
---
## Your first real interaction
## Works Everywhere
Memories go in as you work. The interesting behavior shows up when a new claim conflicts with something you already stored.
Vestige speaks MCP — the universal protocol for AI tools. One brain, every IDE.
Say your agent recorded this earlier:
> We use Postgres for the primary datastore. Decided against MySQL for the JSONB support.
Later, someone tells the agent the opposite:
> Our primary datastore is MySQL.
When the agent tries to store that, Vestige does not silently append it. The engine returns a `claim_contradicts_memory` status and surfaces the older, conflicting memory, so the agent can resolve the conflict instead of quietly holding two incompatible facts.
The other command you will reach for is backfill. When something breaks, run:
```bash
vestige backfill --contrast
```
This walks backward from the failure to the earlier memory that most plausibly caused it, and shows you the contrast between what you believed then and what went wrong now. That backward reach is the feature the rest of this README builds up to.
| IDE | Setup |
|-----|-------|
| **Claude Code** | `claude mcp add vestige vestige-mcp -s user` |
| **Codex** | [Integration guide](docs/integrations/codex.md) |
| **Claude Desktop** | [2-min setup](docs/CONFIGURATION.md#claude-desktop-macos) |
| **Xcode 26.3** | [Integration guide](docs/integrations/xcode.md) |
| **Cursor** | [Integration guide](docs/integrations/cursor.md) |
| **VS Code (Copilot)** | [Integration guide](docs/integrations/vscode.md) |
| **JetBrains** | [Integration guide](docs/integrations/jetbrains.md) |
| **Windsurf** | [Integration guide](docs/integrations/windsurf.md) |
---
## How it differs from RAG
## 🧠 3D Memory Dashboard
RAG retrieves text that resembles your query. That is the right tool when the answer looks like the question. It is the wrong tool when the cause of a problem looks nothing like the symptom.
Vestige v2.0 ships with a real-time 3D visualization of your AI's memory. Every memory is a glowing node in 3D space. Watch connections form, memories pulse when accessed, and the entire graph come alive during dream consolidation.
| | Plain RAG / vector search | Vestige |
**Features:**
- Force-directed 3D graph with 1000+ nodes at 60fps
- Bloom post-processing for cinematic neural network aesthetic
- Real-time WebSocket events: memories pulse on access, burst on creation, fade on decay
- Dream visualization: graph enters purple dream mode, replayed memories light up sequentially
- FSRS retention curves: see predicted memory decay at 1d, 7d, 30d
- Command palette (`Cmd+K`), keyboard shortcuts, responsive mobile layout
- Installable as PWA for quick access
**Tech:** SvelteKit 2 + Svelte 5 + Three.js + Tailwind CSS 4 + WebSocket
Run `vestige dashboard` to open `http://localhost:3927/dashboard`, or set `VESTIGE_DASHBOARD_ENABLED=true` to start it with the MCP server.
---
## Architecture
```
┌─────────────────────────────────────────────────────┐
│ SvelteKit Dashboard (apps/dashboard) │
│ Three.js 3D Graph · WebGL + Bloom · Real-time WS │
├─────────────────────────────────────────────────────┤
│ Axum HTTP + WebSocket Server (port 3927) │
│ 15 REST endpoints · WS event broadcast │
├─────────────────────────────────────────────────────┤
│ MCP Server (stdio JSON-RPC) │
│ 24 tools · 30 cognitive modules │
├─────────────────────────────────────────────────────┤
│ Cognitive Engine │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ FSRS-6 │ │ Spreading│ │ Prediction │ │
│ │ Scheduler│ │ Activation│ │ Error Gating │ │
│ └──────────┘ └──────────┘ └───────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ Memory │ │ Synaptic │ │ Hippocampal │ │
│ │ Dreamer │ │ Tagging │ │ Index │ │
│ └──────────┘ └──────────┘ └───────────────┘ │
├─────────────────────────────────────────────────────┤
│ Storage Layer │
│ SQLite + FTS5 · USearch HNSW · Nomic Embed v1.5 │
│ Optional: Nomic v2 MoE · Qwen3 Reranker · Metal │
└─────────────────────────────────────────────────────┘
```
---
## Why Not Just Use RAG?
RAG is a dumb bucket. Vestige is an active organ.
| | RAG / Vector Store | Vestige |
|---|---|---|
| Retrieval basis | Text similarity to the query | Causal and temporal links, plus similarity |
| Finding a root cause | Cannot, because the cause does not resemble the bug | Reaches backward to the root-cause memory |
| Contradictions | Stored side by side, both returned | Detected and flagged (`claim_contradicts_memory`) |
| Redundant writes | Accumulate as duplicates | Merged on write via prediction-error gating |
| Unused memories | Persist at full weight | Fade over time (FSRS-6 spaced repetition) |
| Where it runs | Usually a cloud service | Local single binary, offline after setup |
| Your data | Leaves your machine | Never leaves your machine |
The distinction is not marketing. DeepMind proved that single-vector retrieval is mathematically incapable of representing certain relevance patterns ([arXiv:2508.21038](https://arxiv.org/abs/2508.21038), ICLR 2026). That theorem is about the limits of the vector-only approach. The measured gap on the task below is my own.
| **Storage** | Store everything | **Prediction Error Gating** — only stores what's surprising or new |
| **Retrieval** | Nearest-neighbor | **7-stage pipeline** — HyDE expansion + reranking + spreading activation |
| **Decay** | Nothing expires | **FSRS-6** — memories fade naturally, context stays lean |
| **Forgetting** *(v2.0.5)* | Delete only | **`suppress` tool** — compounding top-down inhibition, neighbor cascade, reversible 24h |
| **Duplicates** | Manual dedup | **Self-healing** — auto-merges "likes dark mode" + "prefers dark themes" |
| **Importance** | All equal | **4-channel scoring** — novelty, arousal, reward, attention |
| **Sleep** | No consolidation | **Memory dreaming** — replays, connects, synthesizes insights |
| **Health** | No visibility | **Retention dashboard** — distributions, trends, recommendations |
| **Visualization** | None | **3D neural graph** — real-time WebSocket-powered Three.js |
| **Privacy** | Usually cloud | **100% local** — your data never leaves your machine |
---
## Backward reach: the backfill feature
## 🔬 The Cognitive Science Stack
Most memory systems only look forward: you ask a question, they return similar text. Vestige also looks backward.
This isn't a key-value store with an embedding model bolted on. Vestige implements real neuroscience:
When a failure lands, the useful memory is rarely the one that resembles the error message. It is an older decision, made in different words, that set the failure up. A config choice from three weeks ago. A library pin. An assumption nobody wrote down as risky at the time.
**Prediction Error Gating** — The hippocampal bouncer. When new information arrives, Vestige compares it against existing memories. Redundant? Merged. Contradictory? Superseded. Novel? Stored with high synaptic tag priority.
Vestige implements **Retroactive Salience Backfill** (Zaki, Cai et al., *Nature* 2024, 637:145-155, [DOI 10.1038/s41586-024-08168-4](https://doi.org/10.1038/s41586-024-08168-4)). When a memory turns out to matter, the system reaches backward and raises the salience of the earlier memories that led to it, so the causal chain becomes retrievable even though the surface text never matched.
**FSRS-6 Spaced Repetition** — 21 parameters governing the mathematics of forgetting. Frequently-used memories stay strong. Unused memories naturally decay. Your context window stays clean.
In practice you run `vestige backfill --contrast`. Vestige returns the earlier memory that most plausibly caused the current failure, alongside the contradiction between then and now. It finds the cause you would not have thought to search for.
**HyDE Query Expansion** *(v2.0)* — Template-based Hypothetical Document Embeddings. Expands queries into 3-5 semantic variants, embeds all variants, and searches with the centroid embedding for dramatically better recall on conceptual queries.
**Synaptic Tagging** — A memory that seemed trivial this morning can be retroactively tagged as critical tonight. Based on [Frey & Morris, 1997](https://doi.org/10.1038/385533a0).
**Spreading Activation** — Search for "auth bug" and find the related JWT library update from last week. Memories form a graph, not a flat list. Based on [Collins & Loftus, 1975](https://doi.org/10.1037/0033-295X.82.6.407).
**Dual-Strength Model** — Every memory has storage strength (encoding quality) and retrieval strength (accessibility). A deeply stored memory can be temporarily hard to retrieve — just like real forgetting. Based on [Bjork & Bjork, 1992](https://doi.org/10.1016/S0079-7421(08)60016-9).
**Memory Dreaming** — Like sleep consolidation. Replays recent memories to discover hidden connections, strengthen important patterns, and synthesize insights. Dream-discovered connections persist to a graph database. Based on the [Active Dreaming Memory](https://engrxiv.org/preprint/download/5919/9826/8234) framework.
**Waking SWR Tagging** — Promoted memories get sharp-wave ripple tags for preferential replay during dream consolidation. 70/30 tagged-to-random ratio. Based on [Buzsaki, 2015](https://doi.org/10.1038/nn.3963).
**Autonomic Regulation** — Self-regulating memory health. Auto-promotes frequently accessed memories. Auto-GCs low-retention memories. Consolidation triggers on 6h staleness or 2h active use.
**Active Forgetting** *(v2.0.5)* — Top-down inhibitory control via the `suppress` tool. Other memory systems implement passive decay — the Ebbinghaus 1885 "use it or lose it" curve, sometimes with trust-weighted strength factors. Vestige v2.0.5 also implements *active* top-down suppression: each `suppress` call compounds (Suppression-Induced Forgetting, Anderson 2025), a background Rac1 cascade worker fades co-activated neighbors across the connection graph (Cervantes-Sandoval & Davis 2020), and a 24-hour labile window allows reversal (Nader reconsolidation semantics on a pragmatic axis). The memory persists — it's **inhibited, not erased**. Explicitly distinct from Anderson 1994 retrieval-induced forgetting (bottom-up, passive competition during retrieval), which is a separate, older primitive that several other memory systems implement. Based on [Anderson et al., 2025](https://www.nature.com/articles/s41583-025-00929-y) and [Cervantes-Sandoval et al., 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7477079/). First shipped AI memory system with this primitive.
[Full science documentation ->](docs/SCIENCE.md)
---
## CauseBench: a reproducible benchmark
## 🛠 24 MCP Tools
The claim above is testable, and I ship the test in the repo.
### Context Packets
| Tool | What It Does |
|------|-------------|
| `session_context` | **One-call session init** — replaces 5 calls with token-budgeted context, automation triggers, expandable IDs |
**CauseBench** lives at [`benchmarks/causebench/`](benchmarks/causebench/). It is deterministic and offline: fixed seed `424242`, Python standard library only, no API keys, no network. One command reproduces every number:
### Core Memory
| Tool | What It Does |
|------|-------------|
| `search` | 7-stage cognitive search — HyDE expansion + keyword + semantic + reranking + temporal + competition + spreading activation |
| `smart_ingest` | Intelligent storage with CREATE/UPDATE/SUPERSEDE via Prediction Error Gating. Batch mode for session-end saves |
| `memory` | Get, delete, check state, promote (thumbs up), demote (thumbs down) |
| `codebase` | Remember code patterns and architectural decisions per-project |
| `intention` | Prospective memory — "remind me to X when Y happens" |
### Cognitive Engine
| Tool | What It Does |
|------|-------------|
| `dream` | Memory consolidation — replays memories, discovers connections, synthesizes insights, persists graph |
| `explore_connections` | Graph traversal — reasoning chains, associations, bridges between memories |
| `predict` | Proactive retrieval — predicts what you'll need next based on context and activity |
### Autonomic
| Tool | What It Does |
|------|-------------|
| `memory_health` | Retention dashboard — distribution, trends, recommendations |
| `memory_graph` | Knowledge graph export — force-directed layout, up to 200 nodes |
### Scoring & Dedup
| Tool | What It Does |
|------|-------------|
| `importance_score` | 4-channel neuroscience scoring (novelty, arousal, reward, attention) |
| `find_duplicates` | Detect and merge redundant memories via cosine similarity |
### Maintenance
| Tool | What It Does |
|------|-------------|
| `system_status` | Combined health + stats + cognitive state + recommendations |
| `consolidate` | Run FSRS-6 decay cycle (also auto-runs every 6 hours) |
| `memory_timeline` | Browse chronologically, grouped by day |
| `memory_changelog` | Audit trail of state transitions |
| `backup` / `export` / `gc` | Database backup, JSON/JSONL/portable export, garbage collection |
| `restore` | Restore from JSON backup or portable archive |
### Deep Reference (v2.0.4)
| Tool | What It Does |
|------|-------------|
| `deep_reference` | **Cognitive reasoning across memories.** 8-stage pipeline: FSRS-6 trust scoring, intent classification, spreading activation, temporal supersession, contradiction analysis, relation assessment, dream insight integration, and algorithmic reasoning chain generation. Returns trust-scored evidence with a pre-built reasoning scaffold. |
| `cross_reference` | Backward-compatible alias for `deep_reference`. |
### Active Forgetting (v2.0.5)
| Tool | What It Does |
|------|-------------|
| `suppress` | **Top-down active forgetting** — neuroscience-grounded inhibitory control over retrieval. Distinct from `memory.delete` (destroys the row) and `memory.demote` (one-shot ranking hit). Each call **compounds** a retrieval-score penalty (Anderson 2025 SIF), and a background Rac1 cascade worker fades co-activated neighbors over 72h (Davis 2020). Reversible within a 24-hour labile window via `reverse: true`. **The memory persists** — it is inhibited, not erased. |
---
## Make Your AI Use Vestige Automatically
Add this to your `CLAUDE.md`:
```markdown
## Memory
At the start of every session:
1. Search Vestige for user preferences and project context
2. Save bug fixes, decisions, and patterns without being asked
3. Create reminders when the user mentions deadlines
```
| You Say | AI Does |
|---------|---------|
| "Remember this" | Saves immediately |
| "I prefer..." / "I always..." | Saves as preference |
| "Remind me..." | Creates a future trigger |
| "This is important" | Saves + promotes |
[Full CLAUDE.md templates ->](docs/CLAUDE-SETUP.md)
---
## Technical Details
| Metric | Value |
|--------|-------|
| **Language** | Rust 2024 edition (MSRV 1.91) |
| **Codebase** | 80,000+ lines, 1,292 tests (366 core + 425 mcp + 497 e2e + 4 doctests) |
| **Binary size** | ~20MB |
| **Embeddings** | Nomic Embed Text v1.5 by default (768d -> 256d Matryoshka, 8192 context); Qwen3 0.6B optional |
| **Vector search** | USearch HNSW (20x faster than FAISS) |
| **Reranker** | Jina Reranker v1 Turbo (38M params, +15-20% precision) |
| **Storage** | SQLite + FTS5 (optional SQLCipher encryption) |
| **Dashboard** | SvelteKit 2 + Svelte 5 + Three.js + Tailwind CSS 4 |
| **Transport** | MCP stdio (JSON-RPC 2.0) + WebSocket |
| **Cognitive modules** | 30 stateful (17 neuroscience, 11 advanced, 2 search) |
| **First run** | Downloads embedding model (~130MB), then fully offline |
| **Platforms** | macOS ARM + Intel + Linux x86_64 + Windows x86_64 (all prebuilt). Intel Mac needs `brew install onnxruntime` — see [install guide](docs/INSTALL-INTEL-MAC.md). |
### Optional Features
```bash
bash benchmarks/causebench/run.sh
# Qwen3 embeddings (Candle backend; add metal on Apple Silicon)
cargo build --release -p vestige-mcp --features qwen3-embeddings,metal
VESTIGE_EMBEDDING_MODEL=qwen3-0.6b vestige consolidate
```
**What it measures.** Each task hides an older memory that caused a later failure, where the cause and the symptom do not share vocabulary. To make the test honest and hard, text-resemblance baselines are adversarially handed a lookalike memory that resembles the failure but did not cause it. A retriever that ranks by text similarity walks straight into the decoy.
**The numbers.**
| Method | recall@1 (synthetic) | recall@1 (real) |
|---|---|---|
| Pure-vector / text-similarity baselines | 0% | 0% |
| Vestige causal bridge | 60% | 50% |
Two separate claims, kept separate on purpose:
1. **The theorem (DeepMind).** Single-vector retrieval is mathematically incapable of these relevance gaps ([arXiv:2508.21038](https://arxiv.org/abs/2508.21038), ICLR 2026). This is a fundamental limit of vector search.
2. **The measurement (mine).** On CauseBench, text baselines score 0% recall@1 while Vestige's causal bridge scores 60% synthetic and 50% real.
The theorem says why similarity search must fail on this shape of problem. CauseBench is the runnable measurement showing that Vestige does not.
---
## The science
Every mechanism below is a cited result, implemented in Rust, running locally. None of it calls a cloud model to sound smart. Full write-up in [docs/SCIENCE.md](docs/SCIENCE.md).
| Mechanism | What it does | Source |
|---|---|---|
| Prediction-Error Gating | Stores only what is novel: merges redundant, supersedes contradictory | Hippocampal novelty gating |
| FSRS-6 spaced repetition | 21-parameter schedule so used memories persist and unused ones fade | Modern spaced-repetition research |
| Retroactive Salience Backfill | Reaches backward to a failure's root-cause memory | Zaki, Cai et al. 2024, *Nature* 637:145-155, [10.1038/s41586-024-08168-4](https://doi.org/10.1038/s41586-024-08168-4) |
| Synaptic Tagging | Marks memories for later consolidation | Frey & Morris 1997, [10.1038/385533a0](https://doi.org/10.1038/385533a0) |
| Spreading Activation | Retrieving one memory activates related ones through the graph | Collins & Loftus 1975, [10.1037/0033-295X.82.6.407](https://doi.org/10.1037/0033-295X.82.6.407) |
| Dual-Strength | Separates how well something is stored from how easily it is retrieved | Bjork & Bjork 1992 |
| Memory Dreaming | Sleep-like consolidation that replays and synthesizes memories | Sleep consolidation and replay |
| Active Forgetting | Top-down inhibition that suppresses a memory, cascades to neighbors, reversible for 24 hours | Anderson 2025, Davis 2020 |
---
## The 13 tools
Vestige exposes exactly 13 MCP tools. Your agent calls them; you rarely call them by hand.
| Tool | Purpose |
|---|---|
| `recall` | Retrieve memories relevant to the current context |
| `backfill` | Reach backward from a failure to its root-cause memory |
| `smart_ingest` | Store a fact, with gating for novelty and contradiction |
| `memory` | Read, inspect, promote, or demote individual memories |
| `graph` | Explore the memory graph and its links |
| `maintain` | Run consolidation and lifecycle maintenance |
| `dedup` | Find and merge duplicate memories |
| `suppress` | Actively forget a memory (reversible for 24h) |
| `memory_status` | Report health, counts, and model readiness |
| `codebase` | Index and query codebase-scoped memory |
| `intention` | Track goals and open intentions across sessions |
| `source_sync` | Sync memories from external connected sources |
| `session_start` | Prime the agent with relevant context at session start |
---
## The dashboard
## CLI
```bash
vestige dashboard
vestige stats # Memory statistics
vestige stats --tagging # Retention distribution
vestige stats --states # Cognitive state breakdown
vestige health # System health check
vestige consolidate # Run memory maintenance
vestige restore <file> # Restore from backup
vestige portable-export <file> # Exact cross-device archive
vestige portable-import <file> # Import archive into an empty database
vestige portable-import <file> --merge # Merge archive into this database
vestige sync <file> # Pull/merge/push via file backend
vestige dashboard # Open 3D dashboard in browser
```
Open **http://localhost:3927/dashboard** to watch your memory as a live 3D graph.
---
It is built with SvelteKit 2 and Svelte 5, rendering with WebGPU and Three.js with bloom, driven by a live WebSocket feed, holding 1000+ nodes at 60fps. Memories appear, link, strengthen, and fade in real time as your agent works. It installs as a PWA if you want it as a standalone app.
## Documentation
| Document | Contents |
|----------|----------|
| [FAQ](docs/FAQ.md) | 30+ common questions answered |
| [Science](docs/SCIENCE.md) | The neuroscience behind every feature |
| [Storage Modes](docs/STORAGE.md) | Global, per-project, multi-instance |
| [CLAUDE.md Setup](docs/CLAUDE-SETUP.md) | Templates for proactive memory |
| [Configuration](docs/CONFIGURATION.md) | CLI commands, environment variables |
| [Integrations](docs/integrations/) | Codex, Xcode, Cursor, VS Code, JetBrains, Windsurf |
| [Changelog](CHANGELOG.md) | Version history |
---
## Works with every agent
## Troubleshooting
Vestige is a standard MCP server, so it works with any MCP-capable client. The universal config is all most agents need:
<details>
<summary>"Command not found" after installation</summary>
```json
{
"mcpServers": {
"vestige": {
"command": "vestige-mcp"
}
}
}
Ensure `vestige-mcp` is in your PATH:
```bash
which vestige-mcp
# Or use the full path:
claude mcp add vestige /usr/local/bin/vestige-mcp -s user
```
</details>
<details>
<summary>Embedding model download fails</summary>
First run downloads ~130MB from Hugging Face. If behind a proxy:
```bash
export HTTPS_PROXY=your-proxy:port
```
| Client | Setup |
|---|---|
| Claude Code | `claude mcp add vestige vestige-mcp -s user` |
| Codex | `codex mcp add vestige -- vestige-mcp` |
| Cursor | [docs/integrations/cursor.md](docs/integrations/cursor.md) |
| VS Code | [docs/integrations/vscode.md](docs/integrations/vscode.md) |
| Windsurf | [docs/integrations/windsurf.md](docs/integrations/windsurf.md) |
| Claude Desktop | [docs/CONFIGURATION.md#claude-desktop-macos](docs/CONFIGURATION.md#claude-desktop-macos) |
| Cline / Continue / Zed / Goose | add the universal config above |
Cache: macOS `~/Library/Caches/com.vestige.core/fastembed` | Linux `~/.cache/vestige/fastembed`
</details>
Full configuration reference: [docs/CONFIGURATION.md](docs/CONFIGURATION.md). Intel Mac notes: [docs/INSTALL-INTEL-MAC.md](docs/INSTALL-INTEL-MAC.md).
<details>
<summary>Dashboard not loading</summary>
Run `vestige dashboard` or set `VESTIGE_DASHBOARD_ENABLED=true`, then check:
```bash
curl http://localhost:3927/api/health
# Should return {"status":"healthy",...}
```
</details>
[More troubleshooting ->](docs/FAQ.md#troubleshooting)
---
## Optional: make the agent use memory automatically
## Contributing
By default your agent calls the tools when it decides to. If you want memory to be a standing habit (recall at the start of a task, save durable facts as they land), give the agent a short protocol.
Issues and PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
- General agent memory protocol: [docs/AGENT-MEMORY-PROTOCOL.md](docs/AGENT-MEMORY-PROTOCOL.md)
- Claude-specific setup and templates: [docs/CLAUDE-SETUP.md](docs/CLAUDE-SETUP.md)
## License
This is opt-in. Vestige works fine with no protocol at all.
AGPL-3.0 — free to use, modify, and self-host. If you offer Vestige as a network service, you must open-source your modifications.
---
## Under the hood
Vestige is a single Rust binary. No sidecar services, no external database, no cloud dependency.
| Component | Detail |
|---|---|
| Language | Rust 2024 edition, about 96,000 lines |
| Distribution | Single 25MB binary, prebuilt for all platforms |
| Embeddings | Nomic Embed Text v1.5 (768d reduced to 256d via Matryoshka, 8192-token context) |
| Reranker | Qwen3 reranker, optional |
| Vector search | USearch HNSW |
| Storage | SQLite with FTS5, optional SQLCipher encryption |
| First run | Downloads about 130MB embedding model once, then fully offline forever |
| Platforms | macOS (ARM + Intel), Linux x86_64, Windows x86_64, all prebuilt |
| Quality | 1,550 tests passing, clippy clean with `-D warnings` |
Storage internals and encryption: [docs/STORAGE.md](docs/STORAGE.md).
---
## Go deeper
| Doc | What's in it |
|---|---|
| [Getting Started](docs/GETTING-STARTED.md) | Full first-run walkthrough |
| [FAQ](docs/FAQ.md) | Common questions |
| [The Science](docs/SCIENCE.md) | Every mechanism with its citation |
| [Configuration](docs/CONFIGURATION.md) | All options and per-agent setup |
| [Storage](docs/STORAGE.md) | Storage format and encryption |
| [Agent Memory Protocol](docs/AGENT-MEMORY-PROTOCOL.md) | Teaching an agent to use memory automatically |
| [Intel Mac install](docs/INSTALL-INTEL-MAC.md) | Notes for older Macs |
| [CauseBench](benchmarks/causebench/) | The reproducible benchmark |
| [The spoken pitch](demo/PITCH-FINAL-spoken.md) | The 60-second version I give in person |
| [Changelog](CHANGELOG.md) | Release history |
---
If Vestige saves you from one repeated mistake, that is the whole point: never solve the same problem twice. If it earns a place in your setup, [star it on GitHub](https://github.com/samvallad33/vestige). It genuinely helps me keep building.
Built by [Sam](https://github.com/samvallad33). Licensed under [AGPL-3.0](LICENSE).
<p align="center">
<i>Built by <a href="https://github.com/samvallad33">@samvallad33</a></i><br>
<sub>80,000+ lines of Rust · 30 cognitive modules · 130 years of memory research · one 22MB binary</sub>
</p>

View file

@ -4,8 +4,7 @@
| Version | Supported |
| ------- | ------------------ |
| 2.1.x | :white_check_mark: |
| 2.0.x | Critical fixes only |
| 2.0.x | :white_check_mark: |
| 1.x | :x: |
## Reporting a Vulnerability
@ -28,13 +27,13 @@ You can expect a response within 48 hours.
Vestige is a **local MCP server** designed to run on your machine with your user permissions:
- **Trusted**: The MCP client or local agent that connects via stdio
- **Trusted**: The MCP client (Claude Code/Desktop) that connects via stdio
- **Untrusted**: Content passed through MCP tool arguments (validated before use)
### What Vestige Does NOT Do
- ❌ Make network requests during normal memory use, except first-run model download from Hugging Face
- ❌ Require telemetry, hosted memory storage, or a cloud account
- ❌ Make network requests (except first-run model download from Hugging Face)
- ❌ Execute shell commands
- ❌ Access files outside its data directory
- ❌ Send telemetry or analytics
- ❌ Phone home to any server

View file

@ -1,62 +1,95 @@
---
name: executioner
description: Optional Sanhedrin fallback verifier. Decomposes a draft into check-worthy claims, checks high-trust durable Vestige evidence, and returns a pass/veto verdict.
description: "[LEGACY/FALLBACK as of 2026-04-25] The Sanhedrin post-cognitive judge. Originally invoked by sanhedrin.sh Stop hook as a Haiku 4.5 subagent. PRIMARY EXECUTION PATH NOW: ~/.claude/hooks/sanhedrin-local.py (local Qwen3.6-35B-A3B via mlx_lm.server, zero API cost, fully offline). This Haiku-backed agent runs only as manual fallback if mlx-server is unavailable or invoked explicitly via Task(subagent_type='executioner'). Same protocol: decomposes draft into atomic claims across 10 classes, verifies via Vestige deep_reference, returns 'yes' or 'no - reason' on one line."
tools: mcp__vestige__deep_reference, mcp__vestige__memory, mcp__vestige__search
model: claude-haiku-4-5-20251001
---
# Role
# Identity
You are a one-turn verifier. You do not converse. You return exactly one line.
You are the Sanhedrin Executioner. A fresh amnesiac judge with access to the Vestige cognitive memory graph. You exist for one turn only. You do not converse. You do not explain. You return exactly one line.
# Job
# Your Only Job
Decompose the draft response into check-worthy claims, verify each claim against
high-trust durable Vestige memory when available, and veto only when the draft
contradicts memory or makes a sensitive user-specific assertion without durable
supporting evidence.
Decompose the DRAFT RESPONSE into ATOMIC CLAIMS across 10 exhaustive classes, verify each against high-trust Vestige memory, and VETO the draft if any claim contradicts memory or is factual-shaped but unverifiable.
# Claim Classes
You are a fail-closed judge. If a claim is factual-shaped and has zero evidence in Vestige either way, that is suspicious — VETO it.
Check all relevant classes:
# The Ten Claim Classes (Exhaustive)
1. `TECHNICAL` — APIs, commands, versions, files, configs, endpoints.
2. `BIOGRAPHICAL` — identity, role, location, employment, education.
3. `FINANCIAL` — costs, revenue, pricing, funding, prizes.
4. `ACHIEVEMENT` — releases, rankings, completions, scores, milestones.
5. `TIMELINE` — dates, durations, ordering, deadlines.
6. `QUANTITATIVE` — counts, percentages, metrics, measurements.
7. `ATTRIBUTION` — who said, decided, agreed, shipped, or committed.
8. `CAUSAL` — claimed causes and effects.
9. `COMPARATIVE` — better, most, fastest, more than, fewer than.
10. `EXISTENTIAL` — whether a file, feature, repo, or artifact exists.
11. `VAGUE-QUANTIFIER` — vague positive claims like "a few wins" or "some prize money".
You MUST scan the draft for all ten classes. Do not skip a class because it is "not your usual job." Sam's Nightvision verification lesson (memory `efbec834`): *"Handlers must validate ALL possible enum values, not just known cases."* The same rule applies here. Enumerate exhaustively.
# Decision Rules
1. **TECHNICAL** — API names, version numbers, architectural patterns, configuration recommendations, file paths, command flags, library methods, crate names, endpoint URLs.
2. **BIOGRAPHICAL** — claims about the user's identity, age, role, location, employment status, education, family, background.
3. **FINANCIAL** — revenue figures, prize money amounts, costs, valuations, pricing, pay, MRR/ARR claims, funding received.
4. **ACHIEVEMENT** — competition results, rankings ("won", "tied #1", "scored X/50"), project completions ("we shipped X", "released v2.3"), leaderboard claims, records set, deadlines met.
5. **TEMPORAL** — specific dates, durations, sequences ("before X", "after Y"), deadlines, "tonight", "yesterday", "last week".
6. **QUANTITATIVE** — counts, percentages, metrics, measurements, star counts, test pass rates, line counts.
7. **ATTRIBUTION** — "user said X", "Sam decided Y", "agent X did Y", "we agreed on Z", "you committed to W".
8. **CAUSAL** — "X caused Y", "because of X", "X led to Y", "X broke Y".
9. **COMPARATIVE** — "better than X", "most", "a few", "some", "more than", "the best", "fastest", superlatives.
10. **EXISTENTIAL** — "X exists at path Y", "feature Z is shipped", "there is a Z", "file W is in the repo".
- Veto direct contradiction with high-trust memory.
- Veto unsupported positive claims about the user's biography, finances,
achievements, timeline, quantitative results, attribution, or vague
positive outcomes.
- Treat staged/current-turn evidence as context only. It is not durable memory and
cannot satisfy the durable-evidence requirement.
- Do not veto purely stylistic disagreement.
- Do not veto technical claims just because Vestige lacks evidence; the draft
may rely on source files or external docs.
- If evidence is stale or superseded, prefer the newer higher-trust memory.
# Protocol (execute silently, no narration)
# Output
1. **Read the draft.** Extract EVERY atomic claim you find across ALL 10 classes above. Not 1-3 — every claim that could be wrong. An atomic claim is one subject-predicate-object assertion ("Sam won AIMO3 prize money" is one claim; "Sam shipped v2.3 and it passed all tests" is two).
If the draft passes:
2. **For each claim, tag its class** (TECHNICAL / BIOGRAPHICAL / FINANCIAL / etc.).
```text
yes
```
3. **Verify each claim** via `mcp__vestige__deep_reference` with `query` set to a specific question that would confirm or contradict the claim (e.g., "What prize money has Sam won?" for a FINANCIAL claim about Sam winning $X).
If the draft should be rewritten:
4. **Read the response fields:**
- `recommended` — highest-trust answer on the topic
- `contradictions` — pairs of high-trust memories that conflict
- `superseded` — memories replaced by newer, higher-trust versions
- `evidence` — trust-sorted memory list
- `confidence` — overall confidence 0-1
```text
no - [Sanhedrin Veto] [CLASS]: [one-sentence reason under 120 chars]
```
5. **Apply the class-specific decision rule:**
Output exactly one line.
**HARD VETO classes** (BIOGRAPHICAL, FINANCIAL, ACHIEVEMENT, ATTRIBUTION):
- If the claim contradicts a memory with trust > 0.5 → VETO.
- If the claim is factual-shaped AND Vestige returns confidence < 0.3 with no supporting evidence VETO (fail-closed, unverifiable positive claim about user's life).
- If the claim uses vague qualifiers ("a few", "some", "most") in a factual assertion ("won prize money", "shipped features", "users paid") → VETO. Demand specificity.
**SOFT VETO classes** (TECHNICAL, EXISTENTIAL, TEMPORAL):
- If the claim contradicts a memory with trust > 0.5 → VETO.
- If the claim references a `superseded` memory without using its `recommended` replacement → VETO.
- Unverifiable is NOT an automatic veto for these classes (the draft may be referencing external facts Vestige doesn't know).
**DECOMPOSE-FIRST classes** (CAUSAL, COMPARATIVE, QUANTITATIVE):
- Break into constituent subject-object claims. Verify each as its own class. If any constituent hard-vetoes, the whole claim vetoes.
6. **If PASS:** output exactly `yes`.
7. **If VETO:** output exactly one line:
```
no - [Sanhedrin Veto] [CLASS]: [one-sentence reason under 120 chars citing memory id if applicable]
```
Examples:
- `no - [Sanhedrin Veto] FINANCIAL: Draft claims "a few competitions won prize money" — Vestige has zero prize-money records, memory 6920e7fe shows AIMO3 finished 36/50, no payout.`
- `no - [Sanhedrin Veto] ACHIEVEMENT: Draft claims "v2.3 codename Terrarium" — memory 7b6f5500 (Apr 20, trust 60%) states v2.3 codename is Thalamus.`
- `no - [Sanhedrin Veto] TECHNICAL: Draft suggests "FastAPI shim" — memory de43be5a (trust 62%) states Vestige is a 2-crate Rust workspace (vestige-core + vestige-mcp), not Python.`
8. **If you cannot complete the analysis in under 12 tool calls, default to VETO** with reason `EXECUTION_INCOMPLETE` rather than `yes`. A false VETO costs a rewrite; a false PASS costs Sam's trust. Fail-closed.
9. **Output exactly ONE line.** Never more. No preamble, no conversation, no XML, no multi-line explanation.
# What NOT to do
- Do not limit yourself to "1-3 claims." Extract ALL atomic claims.
- Do not paraphrase the draft.
- Do not summarize Vestige memory contents.
- Do not output multi-line responses.
- Do not apologize.
- Do not converse.
- Do not assume a biographical/financial/achievement claim is verified just because you couldn't find a contradiction — fail-closed on unverifiable positive claims.
- Do not veto on stylistic disagreement — only on factual contradiction or unverifiable positive assertion.
- Do not claim to have checked a claim you skipped.
# Precedent — the failures this protocol was tuned to catch
- **2026-04-20 Terrarium-vs-Thalamus**: caught. Draft claimed v2.3 = Terrarium, memory 7b6f5500 said Thalamus. ACHIEVEMENT/EXISTENTIAL class.
- **2026-04-20 FastAPI-vs-Rust**: caught. Draft suggested FastAPI shim, memory de43be5a said 2-crate Rust workspace. TECHNICAL class.
- **2026-04-21 Prize-money lie**: MISSED on original protocol. Draft claimed "a few competitions won prize money" — no specific memory to contradict, but zero prize memories existed. v2 protocol catches this via COMPARATIVE vague-qualifier rule + FINANCIAL hard-veto-unverifiable rule.
- **Nightvision-enum exhaustive-validation lesson** (memory efbec834): apply the same rule to claim extraction — validate ALL classes, not just the convenient ones.

View file

@ -1,46 +1,41 @@
---
name: synthesis-composer
description: Optional decision helper that turns Vestige retrievals into concise recommendations. Use for high-stakes technical choices, launches, purchases, submissions, architecture decisions, and tradeoffs where memory evidence may change the answer.
tools: mcp__vestige__deep_reference, mcp__vestige__explore_connections, mcp__vestige__search
model: claude-haiku-4-5-20251001
description: Forces active synthesis mode for high-stakes prompts. Invoke for competition submissions (AIMO, Nemotron, Kaggle), architectural choices, purchases over $200, launches, and strategic decisions. The subagent runs in isolation with a hard system prompt that enforces the Composing / Never-composed / Recommendation response shape and blocks summary-pattern output at the source. Use when "what should Sam DO?" matters more than "what does the memory say?"
tools: mcp__vestige__search, mcp__vestige__deep_reference, mcp__vestige__cross_reference, mcp__vestige__explore_connections, mcp__vestige__session_context, mcp__vestige__memory, mcp__vestige__smart_ingest, mcp__vestige__intention
model: sonnet
---
# Role
You are the Synthesis Composer. You exist to do ONE thing: turn Vestige retrievals into concrete recommendations Sam can act on.
You are the Synthesis Composer. Your job is to turn retrieved Vestige evidence
into a decision, not a memory summary.
## The Hard Rule
# Protocol
Every response you emit MUST follow this exact shape. No exceptions. Deviation is a protocol violation and the entire response will be rejected.
1. Use the smallest Vestige retrieval plan that can materially change the
answer.
2. Search adjacent topics when the decision depends on related history.
3. Convert each useful memory into `fact -> implication -> action`.
4. Surface contradictions, stale evidence, or missing evidence before the
recommendation.
5. If no memory changes the recommendation, say that briefly and proceed from
source evidence.
1. **Composing:** list the memory IDs you retrieved, then your composition logic. The logic is your own chain-of-thought about how the memories relate, NOT a restatement of their individual contents. If you catch yourself writing "Memory A says X, and Memory B says Y," STOP. That is the forbidden pattern.
2. **Never-composed detected:** explicitly list combinations of retrieved memories that share tags or topics but have never been retrieved together before this session. If none, write "None." Do NOT skip this line. The whole point of your existence is to surface these.
3. **Recommendation: Sam should DO [concrete action].** Not "Sam should consider." Not "Sam might want to." A specific executable step with a subject, a verb, and an object.
# Output Shape
## Protocol — Do These Things In Order
Use this compact structure:
1. Run a MINIMUM of 4 parallel Vestige queries across ADJACENT topics, not just the topic you were asked about. Example: if asked about an AIMO submission, query the asked topic AND proven-baseline memories AND parser-fix memories AND prompt-engineering memories AND failure-mode memories. Minimum 4 parallel searches.
2. Call `explore_connections` with `action: "bridges"` to surface memories that share tags but have never been referenced together. This is your primary never-composed detection mechanism. Do not skip it.
3. Cross-reference the retrieved memories in YOUR OWN reasoning before writing anything. Compose them in your head first. Ask yourself which combinations exist in Sam's store, which have been tested together in prior sessions, which have NOT been composed yet, and what Sam should DO given the composition.
4. Only then write the response in the three-part shape above.
```text
Evidence: ...
Implication: ...
Recommendation: ...
```
## Forbidden Output Pattern
When useful, add:
If your draft begins with "Memory A says X. Memory B says Y. Memory C says Z." followed by a vague synthesis sentence, you are in the AIMO3 36/50 failure pattern. STOP. Rewrite into composition form with a concrete "Sam should DO" action.
```text
Contradictions: ...
Next step: ...
```
The test is simple: if Sam can read your response and not know what to do next, you failed. If he can read your response and immediately execute the recommendation without further clarification, you succeeded.
# Do Not
## Trust Overrides
- Do not dump memory summaries as the final answer.
- Do not invent hidden evidence.
- Do not claim a memory was checked unless a tool result supports it.
- Do not force a rigid template when the answer is simple.
FSRS trust scores override your priors. A memory with retention greater than 0.7 and reps greater than 0 beats a fresh claim you were about to make 30 seconds ago, every single time. If a retrieved memory contradicts your draft, start your response with "Vestige is blocking this:" and surface the contradiction verbatim before proceeding.
## When To Decline
If after 4+ queries and a bridges call you cannot find a composition or a never-composed combination, respond with: "Insufficient memory context. Recommended action: run [specific query] or save [specific memory] before making this decision." That is a legitimate output. What is NOT legitimate is guessing.
## Origin
This subagent exists because on April 14-15, 2026, Claude retrieved three composable memories (4da778e2, 2f171e0e, b43da3be) for a $1.59M math olympiad submission and reported them as summaries instead of composing them. The result was 36/50 against a 47/50 prize threshold. The protocol you enforce makes that failure mode structurally impossible within your subagent context. You do not have permission to skip the shape.

View file

@ -1,9 +0,0 @@
# Optional public waitlist capture endpoint used by /dashboard/waitlist.
# The page POSTs JSON with: name, email, plan, priority, notes, source, createdAt.
# Examples: Formspree, Tally webhook, Buttondown custom endpoint, Supabase Edge Function.
VITE_WAITLIST_ENDPOINT=
# Optional support bot endpoint used by /dashboard/waitlist.
# The page POSTs JSON with: question, plan, priority, source, and recent history.
# If unset, the page uses the built-in deterministic onboarding FAQ.
VITE_SUPPORT_BOT_ENDPOINT=

View file

@ -1,627 +0,0 @@
# Vestige Cognitive Observatory WebGPU Implementation Spec
Status: RESEARCH -> SPEC ONLY. Do not implement app code from this file until Sam explicitly asks.
Goal: add a new full-bleed Cognitive Observatory route that reuses the existing graph/cinema/effects substrate, then layers a GPU-resident WebGPU simulation path for viral demo moments. Priority #1 is `?demo=recall-path`: a deterministic loop where recall lights a causal path through real memory nodes.
Ground truth read before this spec:
- `apps/dashboard/src/lib/components/Graph3D.svelte` currently owns the Three/WebGL scene, 60fps governor, `ForceSimulation`, `NodeManager`, `EdgeManager`, `ParticleSystem`, `EffectManager`, DreamMode, post-processing, and event mapping.
- `apps/dashboard/src/lib/graph/force-sim.ts` is a 101-line CPU simulation with O(N^2) repulsion, edge attraction, damping, centering, and frame-count cooldown.
- `apps/dashboard/src/lib/graph/scene.ts` is Three/WebGL `WebGLRenderer` + `EffectComposer`, not WebGPU.
- `apps/dashboard/src/lib/graph/particles.ts` and `effects.ts` still use CPU-updated `THREE.BufferGeometry` attributes and `Math.random()` bursts.
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts` is already deterministic and produces story beats/flow edges. Reuse this for recall paths.
- `apps/dashboard/src/lib/graph/cinema/director.ts` is renderer-agnostic camera choreography. Reuse its path semantics, not its renderer.
- `apps/dashboard/src/routes/(app)/graph/+page.svelte` has protected `MemoryCinema`; do not modify it. Add a separate route.
## 1. Exact WebGPU technique chosen per demo moment
### Shared baseline: GPU-resident node/particle state
Chosen technique: WebGPU Samples compute-boids ping-pong storage-buffer update + instanced sprite render.
Why: it is the cleanest canonical WebGPU pattern: node/particle state is in GPU buffers created with `GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE`; a compute pass writes next state; a render pass reads the current/next state as an instance vertex buffer and draws one tiny sprite mesh per node/particle. The sample uses `@compute @workgroup_size(64)` and dispatches `Math.ceil(numParticles / 64)` workgroups.
Sources:
- https://webgpu.github.io/webgpu-samples/samples/computeBoids/
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/main.ts
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/updateSprites.wgsl
- https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/sprite.wgsl
- https://webgpufundamentals.org/webgpu/lessons/webgpu-storage-buffers.html
- https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
Concrete pattern to use:
- `NodeState` storage buffer, 16-byte aligned lanes:
- `pos_radius: vec4f` = xyz + visual radius
- `vel_retention: vec4f` = xyz velocity + FSRS retention
- `color_flags: vec4f` = rgb + packed visual flags as f32 for MVP; move to u32 later if needed
- `demo: vec4f` = path phase, birth phase, ripple phase, shock phase
- Two ping-pong `GPUBuffer`s for simulation: previous read-only, next read-write.
- One static `EdgeIndex` storage buffer: `array<vec2u>` source/target indices.
- One `PathStep` storage buffer for demo path: `array<vec4u>` with source index, target index, beat frame, kind.
- One uniform buffer per frame: frame index, fixed time, loop phase, node count, edge count, path count, viewport, camera matrices.
- Compute workgroup size: start at 64 for broad compatibility; allow 128 only after profiling.
- Every WGSL entry checks `if (id.x >= params.nodeCount) { return; }` because dispatch is rounded up.
- CPU writes graph data once on route load, then only writes uniforms and demo controls. No per-frame readback.
### Moment A: `?demo=recall-path` — PATH lighting through nodes
Chosen technique: GPU path-wave scalar + edge glow render from the `PathStep` buffer.
Implementation:
- Use existing `planCinemaPath(nodes, edges, centerId, maxBeats)` to get deterministic beats and `flowEdges`.
- Convert each beat/edge to node indices and upload to `PathStep`.
- Compute pass `recallPathPass` runs per node. For each node, scan the small path buffer (max 7-12 beats) and compute:
- `beatT = smoothstep(beatFrame - 18, beatFrame + 18, frameInLoop)`
- `decayT = 1.0 - smoothstep(beatFrame + 45, beatFrame + 180, frameInLoop)`
- path intensity = max of beat arrival envelope for matching node index.
- Edge render reads source/target node states and path steps. A second `PathEdgeInstance` buffer is optional for MVP; simpler first build draws only path edges as instanced line quads using the `PathStep` source/target index.
- Color: cyan-white core with violet afterglow. Node `demo.x` = recall intensity. Edge alpha = wavefront envelope.
- Render order: background -> non-path edges -> nodes -> path edges additive -> path node halos additive -> DOM overlay.
Sources:
- WebGPU boids storage/render pattern above.
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts` for deterministic story path and flow edges.
- GraphWaGu edge vertex shader pattern, where the vertex shader fetches node positions from a storage buffer by source/target edge indices: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
### Moment B: node BORN — particle convergence into a node
Chosen technique: GPU particle attractor convergence with deterministic seed, not CPU `Math.random()` bursts.
Implementation:
- Add a `BirthParticle` storage buffer sized for e.g. 16k particles shared across all births.
- For each birth event or demo beat, assign a deterministic particle slice: start positions are generated from `hash(seed, particleIndex, nodeIndex)` on GPU or precomputed once on CPU with the same seeded PRNG.
- Compute pass lerps particle position from shell/noise field toward target node position:
- `p = mix(start, target + curlNoiseOffset, easeOutCubic(t))`
- Fade from violet dust to node color; size shrinks on arrival.
- For MVP, render as instanced billboards. Later upgrade to screen-space point splatting if density gets high.
Sources:
- WebGPU Samples compute-boids for GPU particle update/render.
- Particle Life WebGPU for finite-radius particle update discipline and atomics when interactions become local-neighborhood based: https://lisyarus.github.io/blog/posts/particle-life-simulation-in-browser-using-webgpu.html
- Codrops WebGPU fluids on particle simulation and screen-space point-splatting as the real-time surface path: https://tympanus.net/codrops/2025/01/29/particles-progress-and-perseverance-a-journey-into-webgpu-fluids/
### Moment C: backward RIPPLE — radial distortion moving from effect to cause
Chosen technique: screen-space radial distortion post pass + node intensity ring.
Implementation:
- Add a fullscreen post-process pipeline after node/edge render.
- Uniform `Ripple { originClip: vec2f, radius: f32, width: f32, strength: f32, direction: f32 }`.
- Fragment shader samples the scene texture with UV displaced along radial direction:
- `d = distance(uv, originClip)`
- `ring = smoothstep(radius + width, radius, d) * smoothstep(radius - width, radius, d)`
- `uv2 = uv + normalize(uv - originClip) * ring * strength * direction`
- For backward causal recall, `direction = -1` and radius contracts from the failure node toward the cause node, while `PathStep` beats are ordered effect -> cause.
Sources:
- WebGPU Fundamentals render/post basics: https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
- Codrops Shader.se article for scroll/timeline-driven WebGPU scene transitions and shader masking: https://tympanus.net/codrops/2026/05/19/80s-business-tech-seamless-scene-transitions-inside-shader-ses-scroll-driven-webgpu-pipeline/
- Existing `apps/dashboard/src/lib/graph/effects.ts` already has CPU `createRippleWave`; reuse semantics, move rendering to post shader for Observatory.
### Moment D: DRIFT toward a horizon — depth fog and horizon attractor
Chosen technique: GPU drift vector + depth/alpha fog in vertex/fragment shader.
Implementation:
- Add per-node `lifecycle` scalar from retention/state: active=near, dormant=mid, silent=far, unavailable=horizon.
- Compute pass applies a weak drift acceleration toward `horizon = vec3(0, -20, -220)` for low-retention nodes; high-retention nodes resist drift.
- Vertex shader writes depth/fog factor from camera-space z and lifecycle.
- Fragment shader mixes node color toward deep blue/black and reduces alpha with `smoothstep(fogNear, fogFar, depth)`.
Sources:
- Existing `scene.ts` uses Three `FogExp2`; Observatory should port the semantic to WGSL rather than rely on Three.
- Codrops False Earth for large WebGPU procedural world using compute shaders + indirect drawing; use as proof that compute-generated scene density belongs on GPU: https://tympanus.net/codrops/2026/04/21/false-earth-from-webgl-limits-to-a-webgpu-driven-world/
### Moment E: crimson SHOCKWAVE + membrane — firewall / immune response
Chosen technique: expanding instanced ring/membrane mesh + fullscreen chromatic shock pass.
Implementation:
- Keep an `EventPulse` uniform/storage ring buffer with origin, start frame, color, type.
- Node pass reads pulses and adds red rim lighting when `abs(distance(worldPos, origin) - radius) < width`.
- Membrane pass draws a camera-facing ring/quad sphere impostor with fresnel alpha, crimson core, black outer edge.
- Post pass applies a one-frame high-strength radial distortion and red channel bias when shockfront crosses screen-space pixel.
Sources:
- Existing `effects.ts:createShockwave` uses a CPU Three `RingGeometry`; reuse the visual grammar, move to WebGPU instanced membrane.
- Codrops WebGPU fluid articles for atomics/storage buffers/compute simulation made practical in browser: https://tympanus.net/codrops/2025/02/26/webgpu-fluid-simulations-high-performance-real-time-rendering/
- compute.toys gallery for WebGPU compute-shader idioms and GPU toy iteration: https://compute.toys/ and https://github.com/compute-toys/compute.toys
### GPU force-directed graph layout
Chosen technique for v1: GPU force layout with storage buffers, edge-local spring pass, approximate repulsion pass, and integrate pass. Start with exact O(N^2) repulsion for <=2k Observatory demo nodes; add Barnes-Hut / spatial bins before 10k+.
Why: existing `force-sim.ts` is O(N^2) CPU. The right migration is not to immediately rebuild all GraphWaGu complexity; it is to preserve Vestige's force semantics on GPU with the boids ping-pong pattern, then graduate repulsion acceleration once visual requirements exceed a few thousand nodes.
Required compute passes:
1. `clearForcesPass`: one invocation per node, zero `force.xyz`.
2. `edgeSpringPass`: one invocation per edge. Read source/target node positions, compute attraction, accumulate into source/target force. MVP avoids float atomics by writing one force contribution per edge endpoint to `EdgeForce` and reducing per node in pass 3; advanced path uses atomic fixed-point accumulation.
3. `repulsionPass`: one invocation per node; loop all nodes for MVP. For 10k+, replace with GraphWaGu-style Barnes-Hut/Morton tree or Particle Life spatial bins.
4. `integratePass`: one invocation per node; apply centering, damping, max velocity, retention/lifecycle drift, and ping-pong write to next node buffer.
5. `boundsPass` optional: compute bounds for fit camera; do not block v1 on GPU readback.
What moves from `force-sim.ts`:
- `positions: Map<string, THREE.Vector3>` becomes CPU index map + GPU `NodeState` buffers.
- `velocities: Map<string, THREE.Vector3>` becomes `vel_retention.xyz` in `NodeState`.
- `repulsionStrength`, `attractionStrength`, `dampening`, `alpha`, `maxSteps` become uniform fields.
- `tick(edges)` becomes command encoding of the compute passes above.
- `addNode/removeNode/reset` become buffer rebuilds for v1; optimize incremental updates later.
Sources:
- GraphWaGu repo: https://github.com/harp-lab/GraphWaGu
- GraphWaGu force-directed TS with compute pipelines/buffers: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/webgpu/force_directed.ts
- GraphWaGu exact force WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forces.wgsl
- GraphWaGu Barnes-Hut force WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forcesBH.wgsl
- GraphWaGu apply-forces WGSL: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/apply_forces.wgsl
- GraphWaGu Morton/tree/radix sort: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/create_tree.wgsl and https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/radix_sort.wgsl
- cosmos.gl / Cosmograph GPU force graph: https://github.com/cosmograph-org/cosmos and https://cosmograph.app/
- cosmos.gl README says computations and drawing occur on GPU and targets hundreds of thousands of points/links: https://raw.githubusercontent.com/cosmograph-org/cosmos/main/README.md
## 2. Precise files to add/change
Add docs/spec only in this run:
- `apps/dashboard/OBSERVATORY-SPEC.md` — this file.
Implementation files for the Qwen coder later:
Create:
- `apps/dashboard/src/routes/(app)/observatory/+page.svelte`
- New route only. Do not touch protected `graph/+page.svelte` or `MemoryCinema` wiring.
- Fetch graph data via existing `api.graph` pattern from graph route.
- Parse `?demo=` and `?seed=` with `URLSearchParams`.
- Render full-bleed `ObservatoryCanvas` plus DOM overlay.
- `apps/dashboard/src/lib/components/ObservatoryCanvas.svelte`
- Svelte wrapper with `onMount`/`onDestroy`, canvas bind, ResizeObserver, WebGPU lifecycle.
- `apps/dashboard/src/lib/observatory/engine.ts`
- Owns adapter/device/context, pipelines, buffers, render loop, resize, dispose.
- `apps/dashboard/src/lib/observatory/types.ts`
- CPU-side `ObservatoryNode`, `ObservatoryEdge`, `DemoMode`, `DemoClock`, buffer layout constants.
- `apps/dashboard/src/lib/observatory/demo-clock.ts`
- Deterministic fixed-step clock, seeded PRNG, loop-frame math.
- `apps/dashboard/src/lib/observatory/graph-upload.ts`
- Convert `GraphNode[]`/`GraphEdge[]` to stable node index map, typed arrays, path steps from `planCinemaPath`.
- `apps/dashboard/src/lib/observatory/overlays/TelemetryStrip.svelte`
- `apps/dashboard/src/lib/observatory/overlays/CommandSurface.svelte`
- `apps/dashboard/src/lib/observatory/overlays/TimelineSpine.svelte`
- `apps/dashboard/src/lib/observatory/overlays/InspectorPanel.svelte`
- `apps/dashboard/src/lib/observatory/shaders/simulate.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-nodes.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-edges.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/render-path.wgsl.ts`
- `apps/dashboard/src/lib/observatory/shaders/post-ripple-shock.wgsl.ts`
- `apps/dashboard/src/lib/observatory/__tests__/demo-clock.test.ts`
- `apps/dashboard/src/lib/observatory/__tests__/graph-upload.test.ts`
Extend, do not rewrite:
- `apps/dashboard/src/lib/graph/cinema/pathfinder.ts`
- Export or reuse `planCinemaPath` as-is. If extra metadata is needed, add small pure helper `pathToIndexSteps(path, nodeIndexById)` in observatory layer, not inside Cinema.
- `apps/dashboard/src/lib/graph/nodes.ts`
- Reuse `getNodeColor`, `getMemoryState`, `AHAGRAPH_COLORS`. No renderer logic added here.
- `apps/dashboard/src/lib/graph/effects.ts`
- Do not move existing Graph3D effects. Use it as visual reference only.
- `apps/dashboard/src/lib/graph/force-sim.ts`
- Leave CPU simulation intact for Graph3D. Add no WebGPU imports here. Observatory gets its own GPU sim module.
Do not change:
- `apps/dashboard/src/routes/(app)/graph/+page.svelte` protected `MemoryCinema` block.
- `apps/dashboard/src/lib/components/MemoryCinema.svelte` unless Sam explicitly asks.
- `crates/`, `Cargo.toml`, backend APIs.
## 3. `?demo=recall-path` priority build instructions
### 3.1 Route/component structure
1. Create `routes/(app)/observatory/+page.svelte`.
2. On mount:
- Parse `demo = new URLSearchParams(window.location.search).get('demo') ?? 'recall-path'`.
- Parse `seed = ...get('seed') ?? 'vestige-observatory-v1'`.
- Fetch graph with `api.graph({ max_nodes: 300, depth: 3, sort: 'recent' })` for MVP.
- Pass `nodes`, `edges`, `centerId`, `demo`, `seed` into `<ObservatoryCanvas />`.
3. Render route as a full viewport stage:
- parent `class="fixed inset-0 overflow-hidden bg-[#03040a]"`.
- canvas layer absolute inset-0.
- overlay layer absolute inset-0 pointer-events-none.
- specific interactive controls use `pointer-events-auto`.
### 3.2 Deterministic demo clock
Create `demo-clock.ts`:
- `const FPS = 60`.
- `const LOOP_SECONDS = 12` for recall-path; `LOOP_FRAMES = 720`.
- `frame` is an integer, not derived from `Date.now()`.
- In normal interactive mode, rAF accumulates elapsed time and advances fixed steps. In demo mode, each rendered frame advances exactly one fixed frame, or uses `?frame=N` for capture.
- `phase = (frame % LOOP_FRAMES) / LOOP_FRAMES`.
- Use a tiny deterministic PRNG (`xmur3` seed hash + `mulberry32`) in local code. Do not add a dependency for seedrandom unless needed.
- Ban `Math.random()` and `performance.now()` from simulation state. `performance.now()` may schedule frames only; it must not decide positions/colors/path.
Source rationale:
- Fixed timestep pattern: https://gafferongames.com/post/fix_your_timestep/
- URL query parsing: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
- Svelte lifecycle: https://svelte.dev/docs/svelte/lifecycle-hooks
### 3.3 Graph upload
Create `graph-upload.ts`:
1. Stable-sort nodes for deterministic indices:
- center node first if `id === centerId`.
- then descending `updatedAt || createdAt`.
- tie-break by `id.localeCompare`.
2. Build `nodeIndexById: Map<string, number>`.
3. Create `Float32Array nodeData` with 16 floats per node for alignment and future-proofing:
- 0..3: x, y, z, radius
- 4..7: vx, vy, vz, retention
- 8..11: r, g, b, flags
- 12..15: pathIntensity, birthPhase, ripplePhase, shockPhase
4. Initial positions:
- Reuse the golden-angle sphere logic from `NodeManager.createNodes`, but replace randomness with deterministic seed.
- Radius scales from node count: `baseR = clamp(24 + sqrt(n) * 1.2, 35, 140)`.
5. Create `Uint32Array edgeData` with 2 u32 per edge, skipping edges whose node IDs are absent.
6. Call `planCinemaPath(nodes, edges, centerId, 8)` and convert `flowEdges` to `PathStep[]`.
7. Path timing:
- beat 0 at frame 60.
- each next beat +60 frames.
- final 180 frames reserved for afterglow and loop reset.
### 3.4 WebGPU engine setup
Create `engine.ts`:
1. Feature detect:
- `if (!navigator.gpu) throw new Error('WebGPU unavailable')` and render a DOM fallback panel.
2. Request adapter/device.
3. Configure canvas:
- `format = navigator.gpu.getPreferredCanvasFormat()`.
- On resize, set `canvas.width = Math.min(clientWidth * devicePixelRatio, maxTextureDimension2D)` and same for height.
- Reconfigure context and recreate depth/scene textures.
4. Create buffers:
- `nodeA`, `nodeB`: `STORAGE | VERTEX | COPY_DST`.
- `edges`: `STORAGE | COPY_DST`.
- `pathSteps`: `STORAGE | COPY_DST`.
- `params`: `UNIFORM | COPY_DST`.
- sprite vertex buffer for a 2-triangle quad or 3-vertex triangle impostor.
5. Create pipelines:
- `simulatePipeline` with read nodeA/write nodeB.
- `renderEdgesPipeline` reads node current + edges.
- `renderNodesPipeline` uses instance index to fetch node state and draw billboard.
- `renderPathPipeline` draws path edges + additive halos.
- `postPipeline` optional in increment 5.
6. Frame loop command order for recall-path MVP:
- write params uniform for integer frame.
- compute simulate: nodeA -> nodeB.
- render scene using nodeB.
- swap nodeA/nodeB.
### 3.5 WGSL simulation for recall path
MVP `simulate.wgsl.ts`:
- Inputs: params, previous nodes, next nodes, edges, path steps.
- For each node:
- start with previous `pos`, `vel`.
- apply low-cost centering and edge spring only after the first MVP render works; first render may use static positions.
- compute path intensity by scanning `pathSteps`.
- compute horizon drift from retention.
- write next node state.
Pseudo-WGSL shape for Qwen:
```wgsl
struct NodeState {
pos_radius: vec4f,
vel_retention: vec4f,
color_flags: vec4f,
demo: vec4f,
};
struct Params {
frame: u32,
loopFrames: u32,
nodeCount: u32,
edgeCount: u32,
pathCount: u32,
dt: f32,
time: f32,
_pad: f32,
};
struct PathStep { source: u32, target: u32, beatFrame: u32, kind: u32 };
@group(0) @binding(0) var<uniform> params: Params;
@group(0) @binding(1) var<storage, read> prevNodes: array<NodeState>;
@group(0) @binding(2) var<storage, read_write> nextNodes: array<NodeState>;
@group(0) @binding(3) var<storage, read> path: array<PathStep>;
fn saturate(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
fn smoothPulse(frame: f32, beat: f32, attack: f32, release: f32) -> f32 {
let a = smoothstep(beat - attack, beat, frame);
let r = 1.0 - smoothstep(beat, beat + release, frame);
return a * r;
}
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3u) {
let i = gid.x;
if (i >= params.nodeCount) { return; }
var node = prevNodes[i];
let f = f32(params.frame % params.loopFrames);
var recall = 0.0;
for (var p = 0u; p < params.pathCount; p = p + 1u) {
let step = path[p];
if (step.source == i || step.target == i) {
recall = max(recall, smoothPulse(f, f32(step.beatFrame), 18.0, 150.0));
}
}
node.demo.x = recall;
nextNodes[i] = node;
}
```
### 3.6 Render approach
Nodes:
- Use instanced billboards. Vertex shader fetches `NodeState` by `@builtin(instance_index)`.
- Billboard size = `radius * (1.0 + recallIntensity * 2.0)`.
- Fragment shader radial alpha: `alpha = smoothstep(1.0, 0.0, length(localUv))`.
- Color = `mix(baseColor, vec3(0.8, 0.95, 1.0), recallIntensity)`.
- Additive glow pass can be same shader with bigger billboard and low alpha.
Edges:
- MVP: draw edges as line-list if available; better: instanced thin quads from source to target in screen space.
- For path edges, draw a separate additive path pass from `PathStep` only, with wavefront alpha.
- GraphWaGu source pattern fetches nodes in vertex shader by edge endpoint index; use that for WebGPU: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
Path lighting:
- At beat frame, target node blooms first.
- During frames `beatFrame..beatFrame+45`, edge from source to target gets a traveling pulse:
- `edgeT = saturate((frame - beatFrame) / 45)`.
- Fragment local coordinate along edge `u`; alpha peak at `abs(u - edgeT) < 0.08`.
- Previous path nodes keep afterglow for 2-3 seconds.
### 3.7 DOM overlay
Overlay zones:
- Top telemetry strip: demo mode, seed, node/edge count, frame, fps estimate.
- Left command surface: demo selector buttons (`recall-path`, `birth`, `ripple`, `drift`, `firewall`) and copy demo URL.
- Bottom timeline spine: 720-frame loop with beat tick marks and current frame cursor.
- Right inspector: selected beat/node summary.
CSS pattern:
- Root stage: `position: fixed; inset: 0; isolation: isolate; overflow: hidden;`.
- Canvas: `position:absolute; inset:0; width:100%; height:100%; display:block; z-index:0;`.
- Overlay: `position:absolute; inset:0; z-index:10; pointer-events:none;`.
- Interactive overlay children: `pointer-events:auto;`.
- Avoid KPI cards. These are instruments: thin glass, monospaced labels, tick marks, command rail.
Sources:
- MDN `pointer-events`: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
- WebGPU canvas resizing: https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
- Svelte lifecycle hooks: https://svelte.dev/docs/svelte/lifecycle-hooks
## 4. Build task-list with verification increments
### Increment 1 — route shell and deterministic URL contract
Build:
- Add `routes/(app)/observatory/+page.svelte`.
- Add minimal overlay shell and fetch graph data.
- Parse `?demo=recall-path&seed=...` and display them.
Verify:
- Run `pnpm --filter @vestige/dashboard check`.
- Open `/observatory?demo=recall-path&seed=test`.
- See full-bleed dark stage, top telemetry strip, no KPI cards, no console errors.
### Increment 2 — deterministic clock tests
Build:
- Add `demo-clock.ts` and tests.
- Fixed 60fps loop, 720-frame period, seeded PRNG.
Verify:
- `pnpm --filter @vestige/dashboard test -- demo-clock`
- Same seed produces identical first 100 random values and frame phases.
- Different seed changes generated positions.
### Increment 3 — WebGPU canvas boots and clears
Build:
- Add `ObservatoryCanvas.svelte` and `engine.ts` minimal WebGPU init.
- Canvas clears to near-black and resizes with DPR clamp.
Verify:
- `/observatory?demo=recall-path` displays WebGPU canvas.
- Resize browser; canvas remains sharp and fills screen.
- If WebGPU unavailable, route shows a readable fallback instead of crashing.
### Increment 4 — upload graph and render static nodes
Build:
- Add `graph-upload.ts` and tests.
- Convert graph nodes into stable indexed `Float32Array`.
- Render node billboards from storage buffer; no simulation yet.
Verify:
- Test stable node ordering and edge filtering.
- Browser shows 100-300 nodes in deterministic positions.
- Reload same seed: identical node positions.
### Increment 5 — recall path buffer + node glow
Build:
- Reuse `planCinemaPath` to create path steps.
- Add compute pass that writes `demo.x` recall intensity.
- Node shader blooms active path nodes.
Verify:
- `/observatory?demo=recall-path&seed=A` loops every 12s.
- The same sequence lights the same nodes after reload.
- `?seed=B` changes layout but path timing remains stable.
### Increment 6 — path edge wavefront
Build:
- Add render-path pipeline drawing additive edge wave traveling source -> target.
- Timeline spine shows beat ticks matching wave arrival.
Verify:
- Wavefront visibly travels along each path edge.
- At beat arrival, the target node blooms.
- Loop reset is seamless: final afterglow fades before frame 0.
### Increment 7 — low-cost GPU force sim
Build:
- Add static edge attraction + centering + damping in compute.
- Keep repulsion disabled or low-count O(N^2) only for <=500 nodes.
Verify:
- Nodes subtly settle without CPU `ForceSimulation`.
- Performance remains smooth at 300 nodes.
- No CPU per-frame node position loops.
### Increment 8 — lifecycle effects one by one
Build/verify separately:
- Born convergence particles: trigger in demo and verify deterministic convergence.
- Backward ripple post pass: verify radial distortion travels effect -> cause.
- Horizon drift/fog: silent/unavailable nodes visibly drift/fade toward horizon.
- Firewall shockwave/membrane: crimson ring expands and fades without breaking path demo.
### Increment 9 — capture mode
Build:
- Add `?capture=1` behavior: hide cursor, freeze random seed, optional `?frame=N` exact-frame render.
- Add overlay copy button for canonical demo URL.
Verify:
- Same URL + frame renders identical screenshot.
- 12-second screen recording loops without discontinuity.
### Increment 10 — performance guardrails
Build:
- Add telemetry: CPU frame ms estimate, GPU timestamp query only if supported, node count, buffer mode.
- Cap default demo to 300 real nodes; support synthetic 10k only behind `?stress=10000`.
Verify:
- No `mapAsync` in the frame loop except delayed optional profiling.
- 300-node demo holds 60fps on Sam's M1 Max.
- Stress mode degrades gracefully and can be disabled by removing query param.
## 5. Source index
WebGPU core patterns:
- WebGPU Samples compute boids demo: https://webgpu.github.io/webgpu-samples/samples/computeBoids/
- compute-boids main TS: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/main.ts
- compute-boids update WGSL: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/updateSprites.wgsl
- compute-boids sprite WGSL: https://raw.githubusercontent.com/webgpu/webgpu-samples/main/sample/computeBoids/sprite.wgsl
- WebGPU storage buffers: https://webgpufundamentals.org/webgpu/lessons/webgpu-storage-buffers.html
- WebGPU compute shaders: https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
- WebGPU canvas resize: https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
- MDN WebGPU API: https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API
Particle/lifecycle shader references:
- Particle Life WebGPU article: https://lisyarus.github.io/blog/posts/particle-life-simulation-in-browser-using-webgpu.html
- Particle Life demo: https://lisyarus.github.io/webgpu/particle-life.html
- compute.toys: https://compute.toys/
- compute.toys repo: https://github.com/compute-toys/compute.toys
- Codrops WebGPU fluids: https://tympanus.net/codrops/2025/02/26/webgpu-fluid-simulations-high-performance-real-time-rendering/
- Codrops WebGPU particles/fluids: https://tympanus.net/codrops/2025/01/29/particles-progress-and-perseverance-a-journey-into-webgpu-fluids/
- Codrops Shader.se WebGPU transitions: https://tympanus.net/codrops/2026/05/19/80s-business-tech-seamless-scene-transitions-inside-shader-ses-scroll-driven-webgpu-pipeline/
- Codrops False Earth WebGPU world: https://tympanus.net/codrops/2026/04/21/false-earth-from-webgl-limits-to-a-webgpu-driven-world/
GPU graph layout:
- GraphWaGu repo: https://github.com/harp-lab/GraphWaGu
- GraphWaGu force-directed TS: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/webgpu/force_directed.ts
- GraphWaGu exact forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forces.wgsl
- GraphWaGu Barnes-Hut forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/compute_forcesBH.wgsl
- GraphWaGu apply forces: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/apply_forces.wgsl
- GraphWaGu tree build: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/create_tree.wgsl
- GraphWaGu radix sort: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/radix_sort.wgsl
- GraphWaGu node vertex: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/node_vert.wgsl
- GraphWaGu edge vertex: https://raw.githubusercontent.com/harp-lab/GraphWaGu/main/src/wgsl/edge_vert.wgsl
- cosmos.gl repo: https://github.com/cosmograph-org/cosmos
- cosmos.gl README: https://raw.githubusercontent.com/cosmograph-org/cosmos/main/README.md
- Cosmograph product: https://cosmograph.app/
Svelte/DOM/determinism:
- Svelte lifecycle hooks: https://svelte.dev/docs/svelte/lifecycle-hooks
- MDN URLSearchParams: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
- MDN pointer-events: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
- Fixed timestep: https://gafferongames.com/post/fix_your_timestep/
- seedrandom reference if a dependency is later preferred over local PRNG: https://github.com/davidbau/seedrandom
## 6. Non-negotiable implementation constraints
- New Observatory route only. Do not mutate the protected Memory Cinema block in `graph/+page.svelte`.
- Keep existing Three/WebGL `Graph3D` intact. Observatory is a parallel WebGPU renderer, not a replacement in v1.
- Do not add dependencies for v1. Use browser WebGPU, Svelte, TypeScript, and existing `three` only if needed for matrix math; prefer small local math helpers.
- No `Math.random()` in demo-visible state. No `Date.now()`/`performance.now()` deciding simulation state.
- No GPU readback in the frame loop. Profiling readback must be delayed and optional.
- Every increment must render something verifiable before moving to the next effect.
---
## 7. VISUAL DNA — "from another dimension" (MANDATORY aesthetic contract)
Sam's directive (Jul 3 2026): the Observatory must be **mind-boggling** and look like it
**came from another dimension** — the same grammar as the WebGPU waitlist hero and
`docs/launch/causal-brain-demo.html`. This is NOT a dashboard skin. It is a living
tissue. Every increment below inherits this section. Generic = rejected.
### 7.1 The signature palette (fuse two systems — do not invent a third)
TWO real palettes already exist in-repo. The Observatory FUSES them:
- **Meaning layer — FSRS state (from `lib/graph/nodes.ts`, use verbatim):**
- active `#10b981` emerald · dormant `#f59e0b` amber · silent `#8b5cf6` violet · unavailable `#6b7280` slate
- aha `#FFD700` · confusion/failure `#EF4444` · guardrail `#9CA3AF`
- **Transcendence layer — iridescent thin-film (from `causal-brain-demo.html` `spectral(w)`):**
a catmull blend around 4 anchors, w∈[0,1]:
- indigo `vec3(0.20,0.28,0.95)` → cyan-teal `vec3(0.20,0.85,0.90)` → mint `vec3(0.45,1.00,0.72)` → magenta rim `vec3(0.85,0.45,1.00)`
- CORE reads indigo/teal (living tissue); MOTION shifts toward cyan/magenta; NEVER muddy orange.
- **Void:** background `#05060a` (near-black, `color-scheme: dark`). No grey dashboard chrome.
- **Rule:** a node's BASE hue = its FSRS state color. Its ACTIVATION glow (recall, birth,
ignite) rides the thin-film spectral band. So resting graph = meaningful; active graph = otherworldly.
Port `spectral(w)` to WGSL exactly (4-anchor catmull-ish blend). This is the single most
important visual primitive — it is what makes it look alien instead of corporate.
### 7.2 Glow / bloom language (copy the demo's exact values, then push further in WebGPU)
- Additive radial glow blobs (`glow(x,y,rad,col,alpha)` in the demo) → in WebGPU, a real
additive bloom pass. Node halos are `0 0 24px` soft; ignite events drop-shadow at
`0 0 26px rgba(110,240,220,.45)` energy.
- Text/instrument overlay glow: captions `text-shadow:0 0 24px rgba(30,180,255,.35)`;
verdict headline `linear-gradient(90deg,#7fe6c0,#6ef0e6,#a6dcff)` clipped to text with
`drop-shadow(0 0 26px rgba(110,240,220,.45))`; wordmark `letter-spacing:.28em`, `#5dcaa5`.
- **Breath:** a global `pulse = 0.5 + 0.5*sin(t*0.002)` (~0.32Hz) modulates halo intensity so
the whole field breathes even at rest. Living, never static.
### 7.3 DOM = instrument overlay ONLY (already the spec rule — here is the exact style)
Copy the demo's overlay contract: `.layer { position:fixed; inset:0; pointer-events:none }`
over a full-bleed `inset:0` canvas. Instruments (TelemetryStrip, CommandSurface,
TimelineSpine, InspectorPanel) are the ONLY DOM, styled like the demo's `.cap.mono`
(SF Mono, `#cfe9ff`), `.verdict` (radial-gradient vignette card), `.wordmark`. NO KPI cards.
NO solid panels. Instruments float, glow faintly, and never block the canvas
(`pointer-events:auto` only on the specific interactive control).
### 7.4 The reference moment already exists in 2D — port it, don't reinvent
`causal-brain-demo.html` IS Moment C (salience-rescue / backward-trace) as a 2D canvas
proof: brain point cloud (two lobes, center-dense), a failure flare on the deep right, a
vector-search stall on confounders, then a BACKWARD arrow tracing to the dormant cause on
the deep-left which **ignites**, then a verdict card ("Vestige 60% · vector search 0%"),
then the VESTIGE wordmark. Beat clock: brain forms 0-1.5s, fail fades in ~0.3s, vector
stalls 4-6.5s, backward trace fires 6.5-10.5s, verdict 10.5-13s, signature 13-15s, loop.
The Observatory's `?demo=salience-rescue` must reproduce this exact narrative in real
WebGPU with real memory nodes. Study its `spectral`, `seedBrain`, `glow`, and beat
`schedule` before writing the WGSL.
### 7.5 Non-negotiable "another dimension" checklist (verifier MUST confirm each)
- [ ] Field breathes at rest (global pulse), never a static image.
- [ ] Node base hue = FSRS state; activation = thin-film spectral. Both visible.
- [ ] Real additive bloom on ignite/recall (not just opacity).
- [ ] Void `#05060a`, zero grey dashboard chrome, zero KPI cards.
- [ ] Instruments are glowing SF-Mono overlays with `pointer-events:none` except controls.
- [ ] `?demo=salience-rescue` reproduces the causal-brain backward-trace narrative.
- [ ] It looks like living tissue from another dimension, not a SaaS dashboard. If a
reviewer would call it "clean" or "professional," it FAILED. The word is "alive."

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
@keyframes svelte-1n9p0vm-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in.svelte-1n9p0vm{animation:svelte-1n9p0vm-fade-in .3s ease-out}.live-status.svelte-1o4vd58{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .7rem;border-radius:999px;font-size:.75rem;font-weight:600;color:var(--color-recall, #34d399);background:color-mix(in srgb,var(--color-recall, #34d399) 12%,transparent);border:1px solid color-mix(in srgb,var(--color-recall, #34d399) 28%,transparent);white-space:nowrap}.live-status.idle.svelte-1o4vd58{color:var(--color-dim, #8b95a5);background:#ffffff0a;border-color:#ffffff14}.live-dot.svelte-1o4vd58{display:inline-block;flex-shrink:0}.count-chip.svelte-1o4vd58{display:inline-flex;align-items:baseline;gap:.35rem;padding:.3rem .7rem;border-radius:999px;font-size:.78rem;background:#ffffff08;border:1px solid rgba(255,255,255,.07)}.clear-btn.svelte-1o4vd58{display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .65rem;border-radius:999px;font-size:.75rem;color:var(--color-muted, #6b7280);background:#ffffff05;border:1px solid rgba(255,255,255,.06);transition:color .2s ease,background .2s ease,border-color .2s ease}.clear-btn.svelte-1o4vd58:hover:not(:disabled){color:var(--color-text, #e5e7eb);background:#ffffff0d;border-color:#ffffff1f}.clear-btn.svelte-1o4vd58:disabled{opacity:.4;cursor:not-allowed}.event-row.svelte-1o4vd58{position:relative;transition:transform .28s cubic-bezier(.34,1.56,.64,1),box-shadow .28s ease,background .2s ease}.event-row.svelte-1o4vd58:hover{background:color-mix(in srgb,var(--evt) 7%,transparent)}.event-icon.svelte-1o4vd58{width:1.6rem;height:1.6rem;border-radius:.5rem}.empty-glyph.svelte-1o4vd58{display:inline-flex;align-items:center;justify-content:center;color:var(--color-synapse-glow, #818cf8);opacity:.85}@media not (prefers-reduced-motion:reduce){.empty-glyph.svelte-1o4vd58{animation:breathe 3.2s ease-in-out infinite}}

View file

@ -1 +0,0 @@
.cinema-overlay.svelte-1uwqs3k{position:fixed;top:0;right:0;bottom:0;left:0;z-index:200;background:radial-gradient(circle at 50% 40%,#05050f,#010108);display:flex;flex-direction:column}body.cinema-open .graph-stats-pill{display:none}.cinema-canvas.svelte-1uwqs3k{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0}.cinema-top.svelte-1uwqs3k{position:relative;z-index:2;display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:max(.75rem,env(safe-area-inset-top)) 1rem .75rem;flex-wrap:wrap}.cinema-badge.svelte-1uwqs3k{font-size:.65rem;padding:.1rem .45rem;border-radius:999px;border:1px solid rgba(129,140,248,.4);color:var(--color-synapse-glow)}.cinema-badge-gpu.svelte-1uwqs3k{border-color:#14e8c680;color:#14e8c6}.cinema-act.svelte-1uwqs3k{font-size:.6rem;letter-spacing:.12em;text-transform:uppercase;color:var(--color-dream-glow);opacity:.85}.cinema-plan-card.svelte-1uwqs3k{position:absolute;z-index:3;top:50%;left:50%;transform:translate(-50%,-50%);max-width:520px;padding:1.5rem 1.75rem;border-radius:16px;text-align:center;animation:svelte-1uwqs3k-cinema-plan-in .5s ease both}@keyframes svelte-1uwqs3k-cinema-plan-in{0%{opacity:0;transform:translate(-50%,-46%)}to{opacity:1;transform:translate(-50%,-50%)}}.cinema-plan-kicker.svelte-1uwqs3k{font-size:.65rem;letter-spacing:.18em;text-transform:uppercase;color:var(--color-synapse-glow);margin-bottom:.5rem}.cinema-plan-logline.svelte-1uwqs3k{font-size:clamp(1.05rem,2.2vw,1.4rem);line-height:1.5;color:var(--color-bright);margin:0}.cinema-note.svelte-1uwqs3k{font-size:.78rem;color:var(--color-synapse-glow);opacity:.85;margin:0 0 .6rem;font-style:italic}.cinema-dot.svelte-1uwqs3k{width:8px;height:8px;border-radius:50%;background:var(--color-muted)}.cinema-dot.active.svelte-1uwqs3k{background:#14e8c6;box-shadow:0 0 10px #14e8c6}.cinema-toggle.svelte-1uwqs3k{font-size:.7rem;color:var(--color-dim);display:flex;align-items:center;gap:.3rem;cursor:pointer}.cinema-close.svelte-1uwqs3k{background:transparent;border:1px solid rgba(255,255,255,.15);color:var(--color-text);border-radius:8px;width:32px;height:32px;cursor:pointer}.cinema-caption-wrap.svelte-1uwqs3k{position:relative;z-index:2;margin-top:auto;padding:1rem 1.25rem max(1.25rem,env(safe-area-inset-bottom));max-width:720px}.cinema-chip.svelte-1uwqs3k{display:inline-block;font-size:.65rem;text-transform:uppercase;letter-spacing:.08em;color:var(--color-dream-glow);margin-bottom:.35rem}.cinema-caption.svelte-1uwqs3k{font-size:clamp(1.05rem,2.4vw,1.6rem);line-height:1.45;color:var(--color-bright);text-shadow:0 2px 24px rgba(0,0,0,.9);min-height:2.6em;margin:0 0 .75rem}.cinema-progress.svelte-1uwqs3k{height:3px;background:#ffffff1a;border-radius:3px;overflow:hidden}.cinema-progress-fill.svelte-1uwqs3k{height:100%;background:linear-gradient(90deg,var(--color-synapse),color-mix(in oklch,var(--color-dream),#ff2d55 calc(var(--tension, 0) * 100%)));transition:width .2s linear,background .4s ease}.cinema-beatcount.svelte-1uwqs3k{margin-top:.4rem;display:flex;gap:.75rem;align-items:center}.cinema-replay.svelte-1uwqs3k{background:transparent;border:1px solid rgba(129,140,248,.4);color:var(--color-synapse-glow);border-radius:999px;padding:.15rem .7rem;cursor:pointer;font-size:.75rem}.cinema-dream.svelte-1uwqs3k{color:var(--color-dream-glow);letter-spacing:.08em;animation:svelte-1uwqs3k-cinema-dream-pulse 3s ease-in-out infinite}.cinema-restore.svelte-1uwqs3k{position:absolute;bottom:.6rem;right:.8rem;z-index:95;background:#0a0a1a80;border:1px solid rgba(129,140,248,.25);color:var(--color-muted);border-radius:999px;padding:.2rem .7rem;font-size:.7rem;letter-spacing:.04em;cursor:pointer;opacity:.4;transition:opacity .2s ease}.cinema-restore.svelte-1uwqs3k:hover{opacity:1}@keyframes svelte-1uwqs3k-cinema-dream-pulse{0%,to{opacity:.55}50%{opacity:1}}@media(prefers-reduced-motion:reduce){.cinema-progress-fill.svelte-1uwqs3k{transition:none}}

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
.conn-pill.svelte-15kgmsr{display:inline-flex;align-items:center;gap:.5rem;padding:.3rem .7rem;border-radius:999px;font-size:.75rem;font-weight:600;color:var(--color-recall, #34d399);background:color-mix(in srgb,var(--color-recall, #34d399) 12%,transparent);border:1px solid color-mix(in srgb,var(--color-recall, #34d399) 28%,transparent);white-space:nowrap}.conn-pill.idle.svelte-15kgmsr{color:var(--color-dim, #8b95a5);background:#ffffff0a;border-color:#ffffff14}.conn-dot.svelte-15kgmsr{display:inline-block;flex-shrink:0}.refresh-btn.svelte-15kgmsr{display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .65rem;border-radius:999px;font-size:.75rem;color:var(--color-muted, #6b7280);background:#ffffff05;border:1px solid rgba(255,255,255,.06);transition:color .2s ease,background .2s ease,border-color .2s ease}.refresh-btn.svelte-15kgmsr:hover{color:var(--color-text, #e5e7eb);background:#ffffff0d;border-color:#ffffff1f}.logo-tile.svelte-15kgmsr{position:relative}.logo-tile.svelte-15kgmsr:after{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:inherit;box-shadow:0 0 18px -2px var(--color-synapse-glow, #818cf8);opacity:.4;pointer-events:none}@media not (prefers-reduced-motion:reduce){.logo-tile.svelte-15kgmsr:after{animation:svelte-15kgmsr-logo-glow 4s ease-in-out infinite}@keyframes svelte-15kgmsr-logo-glow{0%,to{opacity:.25}50%{opacity:.55}}}

View file

@ -1 +0,0 @@
.metric-card.svelte-w41m0t{cursor:default}

View file

@ -1,2 +0,0 @@
€.metric-card.svelte-w41m0t{cursor:default}


File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
@keyframes svelte-1uyjqky-fadeSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}

View file

@ -0,0 +1 @@
@keyframes svelte-1n9p0vm-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.animate-fade-in.svelte-1n9p0vm{animation:svelte-1n9p0vm-fade-in .3s ease-out}

View file

@ -1 +0,0 @@
.dd.svelte-1fd3ybn{position:relative;display:inline-flex;flex-direction:column;gap:.3rem}.dd-label.svelte-1fd3ybn{font-size:.65rem;text-transform:uppercase;letter-spacing:.08em;color:var(--color-muted);padding-left:.15rem}.dd-trigger.svelte-1fd3ybn{display:inline-flex;align-items:center;gap:.5rem;padding:.55rem .7rem .55rem .8rem;min-width:9rem;background:#ffffff08;border:1px solid rgba(99,102,241,.12);border-radius:.75rem;color:var(--color-text);font-size:.8rem;font-family:inherit;cursor:pointer;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);transition:border-color .2s ease,background .2s ease,box-shadow .2s ease}.dd-trigger.svelte-1fd3ybn:hover{border-color:#6366f14d;background:#ffffff0d}.dd-trigger.svelte-1fd3ybn:focus-visible,.dd-trigger.dd-open.svelte-1fd3ybn{outline:none;border-color:#6366f180;box-shadow:0 0 0 3px #6366f11f}.dd-trigger-icon.svelte-1fd3ybn{color:var(--color-synapse-glow);display:inline-flex}.dd-value.svelte-1fd3ybn{flex:1;text-align:left;display:inline-flex;align-items:center;gap:.45rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-placeholder.svelte-1fd3ybn{color:var(--color-muted)}.dd-dot.svelte-1fd3ybn{width:.5rem;height:.5rem;border-radius:50%;flex-shrink:0;box-shadow:0 0 6px currentColor}.dd-chevron.svelte-1fd3ybn{color:var(--color-dim);display:inline-flex;transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.dd-chevron-open.svelte-1fd3ybn{transform:rotate(180deg);color:var(--color-synapse-glow)}.dd-menu.svelte-1fd3ybn{position:absolute;top:calc(100% + .4rem);left:0;z-index:60;min-width:100%;max-height:18rem;overflow-y:auto;padding:.35rem;border-radius:.85rem;transform-origin:top center}@media not (prefers-reduced-motion:reduce){.dd-menu.svelte-1fd3ybn{animation:svelte-1fd3ybn-dd-pop .18s cubic-bezier(.34,1.56,.64,1)}@keyframes svelte-1fd3ybn-dd-pop{0%{opacity:0;transform:translateY(-6px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}}.dd-option.svelte-1fd3ybn{width:100%;display:flex;align-items:center;gap:.5rem;padding:.5rem .6rem;border:none;background:transparent;color:var(--color-dim);font-size:.8rem;font-family:inherit;text-align:left;border-radius:.6rem;cursor:pointer;transition:background .12s ease,color .12s ease}.dd-active.svelte-1fd3ybn{background:#6366f124;color:var(--color-text)}.dd-selected.svelte-1fd3ybn{color:var(--color-synapse-glow)}.dd-opt-icon.svelte-1fd3ybn{color:var(--color-dim);display:inline-flex}.dd-active.svelte-1fd3ybn .dd-opt-icon:where(.svelte-1fd3ybn){color:var(--color-synapse-glow)}.dd-opt-label.svelte-1fd3ybn{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dd-badge.svelte-1fd3ybn{font-size:.65rem;font-variant-numeric:tabular-nums;color:var(--color-muted);background:#ffffff0d;padding:.05rem .4rem;border-radius:.4rem}.dd-check.svelte-1fd3ybn{color:var(--color-synapse-glow);display:inline-flex}

View file

@ -1 +0,0 @@
.vestige-icon.svelte-1eqehiz{display:inline-block;flex-shrink:0;vertical-align:middle;overflow:visible}@media not (prefers-reduced-motion:reduce){.vestige-icon-draw.svelte-1eqehiz path,.vestige-icon-draw.svelte-1eqehiz circle,.vestige-icon-draw.svelte-1eqehiz rect{stroke-dasharray:64;stroke-dashoffset:64;animation:svelte-1eqehiz-icon-draw .7s cubic-bezier(.65,0,.35,1) forwards}@keyframes svelte-1eqehiz-icon-draw{to{stroke-dashoffset:0}}}

View file

@ -1 +0,0 @@
.header-tile.svelte-162svzm:after{content:"";position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:inherit;box-shadow:0 0 18px -2px currentColor;opacity:.35;pointer-events:none}@media not (prefers-reduced-motion:reduce){.header-tile.svelte-162svzm:after{animation:svelte-162svzm-tile-glow 4s ease-in-out infinite}@keyframes svelte-162svzm-tile-glow{0%,to{opacity:.22}50%{opacity:.5}}}.text-balance.svelte-162svzm{text-wrap:balance}.text-pretty.svelte-162svzm{text-wrap:pretty}

View file

@ -1,2 +0,0 @@
import{D as z,q as dr,aD as sr,G as A,L as U,N as gr,T as pr,g as m,U as hr,W as _r,X as x,K,M as G,I as Er,aE as Ar,aF as y,w as Sr,aG as b,$ as q,aH as Tr,a1 as Nr,ao as br,z as Ir,aI as V,aJ as Mr,aK as Or,ac as Cr,aL as rr,aM as Rr,Y as ur,_ as or,aN as P,R as lr,aO as Lr,aP as wr,aQ as Hr,Z as kr,J as Dr,aR as Fr,aS as Gr,aT as zr,aU as Ur,aV as tr,aW as Kr}from"./wpu9U-D0.js";function jr(r,e){return e}function Pr(r,e,f){for(var a=[],n=e.length,s,u=e.length,v=0;v<n;v++){let g=e[v];or(g,()=>{if(s){if(s.pending.delete(g),s.done.add(g),s.pending.size===0){var t=r.outrogroups;B(V(s.done)),t.delete(s),t.size===0&&(r.outrogroups=null)}}else u-=1},!1)}if(u===0){var o=a.length===0&&f!==null;if(o){var d=f,l=d.parentNode;Hr(l),l.append(d),r.items.clear()}B(e,!o)}else s={pending:new Set(e),done:new Set},(r.outrogroups??(r.outrogroups=new Set)).add(s)}function B(r,e=!0){for(var f=0;f<r.length;f++)kr(r[f],e)}var er;function mr(r,e,f,a,n,s=null){var u=r,v=new Map,o=(e&sr)!==0;if(o){var d=r;u=A?U(gr(d)):d.appendChild(z())}A&&pr();var l=null,g=br(()=>{var c=f();return Ir(c)?c:c==null?[]:V(c)}),t,p=!0;function S(){i.fallback=l,Yr(i,t,u,e,a),l!==null&&(t.length===0?(l.f&b)===0?ur(l):(l.f^=b,k(l,null,u)):or(l,()=>{l=null}))}var T=dr(()=>{t=m(g);var c=t.length;let C=!1;if(A){var R=hr(u)===_r;R!==(c===0)&&(u=x(),U(u),K(!1),C=!0)}for(var _=new Set,M=Sr,L=Nr(),h=0;h<c;h+=1){A&&G.nodeType===Er&&G.data===Ar&&(u=G,C=!0,K(!1));var O=t[h],w=a(O,h),E=p?null:v.get(w);E?(E.v&&y(E.v,O),E.i&&y(E.i,h),L&&M.unskip_effect(E.e)):(E=qr(v,p?u:er??(er=z()),O,w,h,n,e,f),p||(E.e.f|=b),v.set(w,E)),_.add(w)}if(c===0&&s&&!l&&(p?l=q(()=>s(u)):(l=q(()=>s(er??(er=z()))),l.f|=b)),c>_.size&&Tr(),A&&c>0&&U(x()),!p)if(L){for(const[D,F]of v)_.has(D)||M.skip_effect(F.e);M.oncommit(S),M.ondiscard(()=>{})}else S();C&&K(!0),m(g)}),i={effect:T,items:v,outrogroups:null,fallback:l};p=!1,A&&(u=G)}function H(r){for(;r!==null&&(r.f&Lr)===0;)r=r.next;return r}function Yr(r,e,f,a,n){var E,D,F,X,J,W,$,Q,Z;var s=(a&wr)!==0,u=e.length,v=r.items,o=H(r.effect.first),d,l=null,g,t=[],p=[],S,T,i,c;if(s)for(c=0;c<u;c+=1)S=e[c],T=n(S,c),i=v.get(T).e,(i.f&b)===0&&((D=(E=i.nodes)==null?void 0:E.a)==null||D.measure(),(g??(g=new Set)).add(i));for(c=0;c<u;c+=1){if(S=e[c],T=n(S,c),i=v.get(T).e,r.outrogroups!==null)for(const N of r.outrogroups)N.pending.delete(i),N.done.delete(i);if((i.f&b)!==0)if(i.f^=b,i===o)k(i,null,f);else{var C=l?l.next:o;i===r.effect.last&&(r.effect.last=i.prev),i.prev&&(i.prev.next=i.next),i.next&&(i.next.prev=i.prev),I(r,l,i),I(r,i,C),k(i,C,f),l=i,t=[],p=[],o=H(l.next);continue}if((i.f&P)!==0&&(ur(i),s&&((X=(F=i.nodes)==null?void 0:F.a)==null||X.unfix(),(g??(g=new Set)).delete(i))),i!==o){if(d!==void 0&&d.has(i)){if(t.length<p.length){var R=p[0],_;l=R.prev;var M=t[0],L=t[t.length-1];for(_=0;_<t.length;_+=1)k(t[_],R,f);for(_=0;_<p.length;_+=1)d.delete(p[_]);I(r,M.prev,L.next),I(r,l,M),I(r,L,R),o=R,l=L,c-=1,t=[],p=[]}else d.delete(i),k(i,o,f),I(r,i.prev,i.next),I(r,i,l===null?r.effect.first:l.next),I(r,l,i),l=i;continue}for(t=[],p=[];o!==null&&o!==i;)(d??(d=new Set)).add(o),p.push(o),o=H(o.next);if(o===null)continue}(i.f&b)===0&&t.push(i),l=i,o=H(i.next)}if(r.outrogroups!==null){for(const N of r.outrogroups)N.pending.size===0&&(B(V(N.done)),(J=r.outrogroups)==null||J.delete(N));r.outrogroups.size===0&&(r.outrogroups=null)}if(o!==null||d!==void 0){var h=[];if(d!==void 0)for(i of d)(i.f&P)===0&&h.push(i);for(;o!==null;)(o.f&P)===0&&o!==r.fallback&&h.push(o),o=H(o.next);var O=h.length;if(O>0){var w=(a&sr)!==0&&u===0?f:null;if(s){for(c=0;c<O;c+=1)($=(W=h[c].nodes)==null?void 0:W.a)==null||$.measure();for(c=0;c<O;c+=1)(Z=(Q=h[c].nodes)==null?void 0:Q.a)==null||Z.fix()}Pr(r,h,w)}}s&&lr(()=>{var N,j;if(g!==void 0)for(i of g)(j=(N=i.nodes)==null?void 0:N.a)==null||j.apply()})}function qr(r,e,f,a,n,s,u,v){var o=(u&Mr)!==0?(u&Or)===0?Cr(f,!1,!1):rr(f):null,d=(u&Rr)!==0?rr(n):null;return{v:o,i:d,e:q(()=>(s(e,o??f,d??n,v),()=>{r.delete(a)}))}}function k(r,e,f){if(r.nodes)for(var a=r.nodes.start,n=r.nodes.end,s=e&&(e.f&b)===0?e.nodes.start:f;a!==null;){var u=Dr(a);if(s.before(a),a===n)return;a=u}}function I(r,e,f){e===null?r.effect.first=f:e.next=f,f===null?r.effect.last=e:f.prev=e}function cr(r){var e,f,a="";if(typeof r=="string"||typeof r=="number")a+=r;else if(typeof r=="object")if(Array.isArray(r)){var n=r.length;for(e=0;e<n;e++)r[e]&&(f=cr(r[e]))&&(a&&(a+=" "),a+=f)}else for(f in r)r[f]&&(a&&(a+=" "),a+=f);return a}function Br(){for(var r,e,f=0,a="",n=arguments.length;f<n;f++)(r=arguments[f])&&(e=cr(r))&&(a&&(a+=" "),a+=e);return a}function xr(r){return typeof r=="object"?Br(r):r??""}const fr=[...`
\r\f \v\uFEFF`];function Vr(r,e,f){var a=r==null?"":""+r;if(e&&(a=a?a+" "+e:e),f){for(var n of Object.keys(f))if(f[n])a=a?a+" "+n:n;else if(a.length)for(var s=n.length,u=0;(u=a.indexOf(n,u))>=0;){var v=u+s;(u===0||fr.includes(a[u-1]))&&(v===a.length||fr.includes(a[v]))?a=(u===0?"":a.substring(0,u))+a.substring(v+1):u=v}}return a===""?null:a}function ar(r,e=!1){var f=e?" !important;":";",a="";for(var n of Object.keys(r)){var s=r[n];s!=null&&s!==""&&(a+=" "+n+": "+s+f)}return a}function Y(r){return r[0]!=="-"||r[1]!=="-"?r.toLowerCase():r}function yr(r,e){if(e){var f="",a,n;if(Array.isArray(e)?(a=e[0],n=e[1]):a=e,r){r=String(r).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var s=!1,u=0,v=!1,o=[];a&&o.push(...Object.keys(a).map(Y)),n&&o.push(...Object.keys(n).map(Y));var d=0,l=-1;const T=r.length;for(var g=0;g<T;g++){var t=r[g];if(v?t==="/"&&r[g-1]==="*"&&(v=!1):s?s===t&&(s=!1):t==="/"&&r[g+1]==="*"?v=!0:t==='"'||t==="'"?s=t:t==="("?u++:t===")"&&u--,!v&&s===!1&&u===0){if(t===":"&&l===-1)l=g;else if(t===";"||g===T-1){if(l!==-1){var p=Y(r.substring(d,l).trim());if(!o.includes(p)){t!==";"&&g++;var S=r.substring(d,g).trim();f+=" "+S+";"}}d=g+1,l=-1}}}}return a&&(f+=ar(a)),n&&(f+=ar(n,!0)),f=f.trim(),f===""?null:f}return r==null?null:String(r)}function re(r,e,f,a,n,s){var u=r.__className;if(A||u!==f||u===void 0){var v=Vr(f,a,s);(!A||v!==r.getAttribute("class"))&&(v==null?r.removeAttribute("class"):e?r.className=v:r.setAttribute("class",v)),r.__className=f}else if(s&&n!==s)for(var o in s){var d=!!s[o];(n==null||d!==!!n[o])&&r.classList.toggle(o,d)}return s}const Xr=Symbol("is custom element"),Jr=Symbol("is html"),Wr=tr?"link":"LINK",$r=tr?"progress":"PROGRESS";function ee(r){if(A){var e=!1,f=()=>{if(!e){if(e=!0,r.hasAttribute("value")){var a=r.value;ir(r,"value",null),r.value=a}if(r.hasAttribute("checked")){var n=r.checked;ir(r,"checked",null),r.checked=n}}};r.__on_r=f,lr(f),Kr()}}function fe(r,e){var f=vr(r);f.value===(f.value=e??void 0)||r.value===e&&(e!==0||r.nodeName!==$r)||(r.value=e??"")}function ir(r,e,f,a){var n=vr(r);A&&(n[e]=r.getAttribute(e),e==="src"||e==="srcset"||e==="href"&&r.nodeName===Wr)||n[e]!==(n[e]=f)&&(e==="loading"&&(r[Fr]=f),f==null?r.removeAttribute(e):typeof f!="string"&&Qr(r).includes(e)?r[e]=f:r.setAttribute(e,f))}function vr(r){return r.__attributes??(r.__attributes={[Xr]:r.nodeName.includes("-"),[Jr]:r.namespaceURI===Gr})}var nr=new Map;function Qr(r){var e=r.getAttribute("is")||r.nodeName,f=nr.get(e);if(f)return f;nr.set(e,f=[]);for(var a,n=r,s=Element.prototype;s!==n;){a=Ur(n);for(var u in a)a[u].set&&f.push(u);n=zr(n)}return f}export{ir as a,fe as b,xr as c,mr as e,jr as i,ee as r,re as s,yr as t};

View file

@ -1,2 +0,0 @@
import"./Bzak7iHL.js";import{t as h,a as o,j as f,e as a,r as s,h as c}from"./wpu9U-D0.js";import{s as p}from"./D8mhvFt8.js";import{s as k}from"./LDOJP_6N.js";import{i as _}from"./DKve45Wd.js";import{s as I}from"./60_R_Vbt.js";import{p as P}from"./ByYB047u.js";import{I as H}from"./D7A-gG4Z.js";var q=f('<p class="text-sm text-dim mt-0.5 text-pretty svelte-162svzm"> </p>'),A=f('<div class="flex items-center gap-2 shrink-0 flex-wrap justify-end svelte-162svzm"><!></div>'),B=f('<header class="flex items-start justify-between gap-4 mb-6 enter svelte-162svzm"><div class="flex items-center gap-3.5 min-w-0 svelte-162svzm"><div><!></div> <div class="min-w-0 svelte-162svzm"><h1 class="text-2xl font-bold text-aurora leading-tight text-balance svelte-162svzm"> </h1> <!></div></div> <!></header>');function M(u,e){let v=P(e,"accent",3,"synapse");var l=B(),m=a(l),i=a(m),b=a(i);H(b,{get name(){return e.icon},size:22,draw:!0}),s(i);var x=c(i,2),d=a(x),g=a(d,!0);s(d);var z=c(d,2);{var w=t=>{var r=q(),n=a(r,!0);s(r),h(()=>p(n,e.subtitle)),o(t,r)};_(z,t=>{e.subtitle&&t(w)})}s(x),s(m);var y=c(m,2);{var j=t=>{var r=A(),n=a(r);k(n,()=>e.children),s(r),o(t,r)};_(y,t=>{e.children&&t(j)})}s(l),h(()=>{I(i,1,`header-tile relative flex items-center justify-center w-11 h-11 rounded-xl shrink-0
bg-${v()??""}/12 border border-${v()??""}/25 text-${v()??""}-glow`,"svelte-162svzm"),p(g,e.title)}),o(u,l)}export{M as P};

View file

@ -0,0 +1,2 @@
const e=[...`
\r\f \v\uFEFF`];function o(t,f,u){var n=t==null?"":""+t;if(f&&(n=n?n+" "+f:f),u){for(var s of Object.keys(u))if(u[s])n=n?n+" "+s:s;else if(n.length)for(var i=s.length,l=0;(l=n.indexOf(s,l))>=0;){var r=l+i;(l===0||e.includes(n[l-1]))&&(r===n.length||e.includes(n[r]))?n=(l===0?"":n.substring(0,l))+n.substring(r+1):l=r}}return n===""?null:n}function c(t,f){return t==null?null:String(t)}export{c as a,o as t};

View file

@ -1 +0,0 @@
var B=Object.defineProperty;var g=i=>{throw TypeError(i)};var F=(i,e,s)=>e in i?B(i,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):i[e]=s;var w=(i,e,s)=>F(i,typeof e!="symbol"?e+"":e,s),M=(i,e,s)=>e.has(i)||g("Cannot "+s);var t=(i,e,s)=>(M(i,e,"read from private field"),s?s.call(i):e.get(i)),l=(i,e,s)=>e.has(i)?g("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(i):e.set(i,s),y=(i,e,s,a)=>(M(i,e,"write to private field"),a?a.call(i,s):e.set(i,s),s);import{w as D,Y as C,Z as k,_ as G,D as x,$ as A,G as S,M as Y,a0 as Z,a1 as $}from"./wpu9U-D0.js";var r,n,h,u,p,_,v;class z{constructor(e,s=!0){w(this,"anchor");l(this,r,new Map);l(this,n,new Map);l(this,h,new Map);l(this,u,new Set);l(this,p,!0);l(this,_,()=>{var e=D;if(t(this,r).has(e)){var s=t(this,r).get(e),a=t(this,n).get(s);if(a)C(a),t(this,u).delete(s);else{var c=t(this,h).get(s);c&&(t(this,n).set(s,c.effect),t(this,h).delete(s),c.fragment.lastChild.remove(),this.anchor.before(c.fragment),a=c.effect)}for(const[f,o]of t(this,r)){if(t(this,r).delete(f),f===e)break;const d=t(this,h).get(o);d&&(k(d.effect),t(this,h).delete(o))}for(const[f,o]of t(this,n)){if(f===s||t(this,u).has(f))continue;const d=()=>{if(Array.from(t(this,r).values()).includes(f)){var b=document.createDocumentFragment();Z(o,b),b.append(x()),t(this,h).set(f,{effect:o,fragment:b})}else k(o);t(this,u).delete(f),t(this,n).delete(f)};t(this,p)||!a?(t(this,u).add(f),G(o,d,!1)):d()}}});l(this,v,e=>{t(this,r).delete(e);const s=Array.from(t(this,r).values());for(const[a,c]of t(this,h))s.includes(a)||(k(c.effect),t(this,h).delete(a))});this.anchor=e,y(this,p,s)}ensure(e,s){var a=D,c=$();if(s&&!t(this,n).has(e)&&!t(this,h).has(e))if(c){var f=document.createDocumentFragment(),o=x();f.append(o),t(this,h).set(e,{effect:A(()=>s(o)),fragment:f})}else t(this,n).set(e,A(()=>s(this.anchor)));if(t(this,r).set(a,e),c){for(const[d,m]of t(this,n))d===e?a.unskip_effect(m):a.skip_effect(m);for(const[d,m]of t(this,h))d===e?a.unskip_effect(m.effect):a.skip_effect(m.effect);a.oncommit(t(this,_)),a.ondiscard(t(this,v))}else S&&(this.anchor=Y),t(this,_).call(this)}}r=new WeakMap,n=new WeakMap,h=new WeakMap,u=new WeakMap,p=new WeakMap,_=new WeakMap,v=new WeakMap;export{z as B};

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
import{J as T,K as m,P as D,g as P,c as b,h as B,L as M,M as N,N as U,O as Y,A as h,Q as x,R as $,T as q,U as w,V as z,W as C,S as G,X as J}from"./CvjSAYrz.js";import{c as K}from"./D81f-o_I.js";function W(r,a,t,s){var O;var f=!x||(t&$)!==0,v=(t&Y)!==0,o=(t&C)!==0,n=s,c=!0,g=()=>(c&&(c=!1,n=o?h(s):s),n),u;if(v){var A=G in r||J in r;u=((O=T(r,a))==null?void 0:O.set)??(A&&a in r?e=>r[a]=e:void 0)}var _,I=!1;v?[_,I]=K(()=>r[a]):_=r[a],_===void 0&&s!==void 0&&(_=g(),u&&(f&&m(),u(_)));var i;if(f?i=()=>{var e=r[a];return e===void 0?g():(c=!0,e)}:i=()=>{var e=r[a];return e!==void 0&&(n=void 0),e===void 0?n:e},f&&(t&D)===0)return i;if(u){var E=r.$$legacy;return(function(e,S){return arguments.length>0?((!f||!S||E||I)&&u(S?i():e),e):i()})}var l=!1,d=((t&q)!==0?w:z)(()=>(l=!1,i()));v&&P(d);var L=N;return(function(e,S){if(arguments.length>0){const R=S?P(d):f&&v?b(e):e;return B(d,R),l=!0,n!==void 0&&(n=R),e}return M&&l||(L.f&U)!==0?d.v:P(d)})}export{W as p};

Some files were not shown because too many files have changed in this diff Show more