Commit graph

190 commits

Author SHA1 Message Date
Sam Valladares
16903f3ab4 Merge pull request #62 from delandtj/design-adr-0002-phase-2-execution 2026-06-15 16:25:38 -05:00
Sam Valladares
31890278d3 Merge pull request #65 from samvallad33/release/v2.1.24-data-dir-permissions 2026-06-15 15:59:12 -05:00
Sam Valladares
47de61f2d2 feat(config): Phase 2 Configurable Output — vestige.toml + output profiles (v2.1.26)
Rebased on v2.1.25 merge/supersede and bumped the post-release metadata to v2.1.26 so this branch does not roll versions backward.

Adds local vestige.toml defaults, output profiles, and MCP response precedence for search, timeline, codebase context, and session context.

Verified:
- cargo metadata --format-version 1 --locked --no-deps
- cargo test -p vestige-core config --no-fail-fast
- cargo test -p vestige-mcp config --no-fail-fast
2026-06-15 13:51:50 -05:00
brendon
51f08264f7
fix(storage): tolerate SQLite-native datetime format in parse_timestamp
Tolerate SQLite-native timestamps from external writers while preserving RFC3339 as the canonical write format.

Verified locally on the merge result:
- cargo test -p vestige-core test_parse_timestamp_accepts_rfc3339_and_sqlite_native --no-fail-fast

CI/Test Suite on the updated PR branch are green.
2026-06-15 13:50:55 -05:00
Sam Valladares
c23d7a309c
feat(merge-supersede): Phase 3 — diff-previewed, reversible merge/supersede controls (v2.1.25) (#75)
Adds opt-in, preview-first combine/dedupe/supersede on a never-delete
(bitemporal) store. The default is review, never silent mutation. Every applied
operation is recorded as a reversible, auditable event with provenance — a git
reflog for your agent's memory.

Core (vestige-core):
- advanced::merge_supersede — pure Fellegi-Sunter two-threshold scoring
  (embedding + tag + token Jaccard), match/possible/non_match classification,
  plan/diff and operation-log types, merge-composition helpers. Unit-tested.
- storage: merge_candidates, plan_merge, plan_supersede, apply_plan, merge_undo,
  protect/pin, and per-project merge_policy (persisted in fsrs_config, env
  overridable). Supersede invalidates bitemporally (valid_until + superseded_by,
  Graphiti-style "invalidate, don't delete") and keeps the old node queryable.
- Migration V14: merge_plans + merge_operations tables, knowledge_nodes.protected
  and .superseded_by columns + indexes. Idempotent on replay (duplicate-column
  guarded ADD COLUMNs).

MCP (vestige-mcp):
- Seven new tools registered + dispatched: merge_candidates, plan_merge,
  plan_supersede, apply_plan, merge_undo, protect, merge_policy.
- apply_plan requires confirm=true for possible/non_match plans; match plans
  auto-apply only when policy.auto_apply is set (default off).

Tests: candidate-threshold classification, plan-preview makes no mutation,
apply+undo reversibility, supersede bitemporal invalidation preserves old-node
queryability, protect blocks merge-away, low-confidence requires confirm, policy
roundtrip, migration V14 + idempotent replay. All 796 scoped tests pass; clippy
-D warnings clean on touched crates.

Docs: docs/MERGE_SUPERSEDE.md + CHANGELOG entry. Version bump 2.1.23 -> 2.1.25.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 12:55:31 -05:00
Luc Lauzon
b01269db22
feat(mcp/system_status): add optional schema_introspection flag (#69)
* feat(mcp/system_status): add optional schema_introspection flag

Adds an optional `schema_introspection: bool` parameter to the
`system_status` MCP tool. When set to true, the response gains a
`schema` block carrying:

- `schemaVersion` (u32) — highest applied migration, mirrors
  `Storage::current_schema_version` (now exposed via a typed public
  method).
- `schemaVersionAppliedAt` (RFC3339, optional) — timestamp the
  current schema_version row was applied.
- `tables` ([{name, rows, columns}]) — per-table row count + column
  list, walked over the canonical PORTABLE_USER_DATA_TABLES set so
  the surface stays stable across migrations rather than enumerating
  arbitrary sqlite_master rows.
- `embeddingNullCount` (i64) — count of knowledge_nodes with NO row
  in node_embeddings. Distinct from MemoryStats.nodes_with_embeddings
  (which keys off the `has_embedding` flag column), so audit scripts
  can detect drift between the flag and the join-based truth.
- `activeEmbeddingModel` (string, optional) + `activeEmbeddingDimensions`
  (u32, optional) — mirrors the existing MemoryStats active-model
  fields, included here so audits get schema_version + active model
  in a single round-trip.

Motivation: external consumers (audit scripts, migration guards,
downstream binary upgrade scripts) currently must read SQLite
directly to learn the schema shape, which couples them to internals
Vestige owns and breaks on every migration. This PR closes that gap
with a first-class MCP surface.

Implementation:

- New `pub fn schema_introspection() -> Result<SchemaIntrospection>`
  inherent method on `Storage` (sqlite.rs). Inherent, not on a
  trait — schema-walk is SQLite-specific by nature, so this stays
  out of any future MemoryStore trait extraction.
- New typed structs `SchemaIntrospection` + `TableIntrospection` in
  memory/mod.rs (canonical home alongside MemoryStats), re-exported
  from the crate root.
- MCP layer (maintenance.rs) parses `SystemStatusArgs`, conditionally
  extends the existing response object with a `schema` key — additive,
  default off, response shape unchanged when omitted.

Coupling assessment vs PR #61 (storage-trait-phase1):

This PR adds ONE new public inherent method on `Storage` plus uses
three already-existing private helpers (`current_schema_version`,
`table_exists`, `table_row_count`, `table_columns`). It does NOT
touch the existing inherent method signatures, does NOT add anything
to the prospective `MemoryStore` trait surface, and does NOT modify
any of the ~25 methods #61 lifts into the trait. PR #61 is purely
additive on the trait surface (per its description, `pub type
Storage = SqliteMemoryStore;` preserves all existing call sites);
this PR is additive on the inherent surface. Two purely-additive
changes to disjoint surfaces should rebase cleanly.

Tests:
- system_status_schema_has_schema_introspection_flag (schema
  introspection: property present, type=boolean, default=false,
  not required)
- system_status_without_schema_flag_omits_schema_block
  (backwards-compat: unset/false → no `schema` key)
- system_status_with_schema_flag_emits_schema_block (positive case:
  schema block present, schemaVersion >= 13, tables non-empty,
  knowledge_nodes row count + columns sane, convenience fields
  present)
- system_status_camelcase_alias (#[serde(rename_all="camelCase")] +
  alias works for both snake and camel input)
- storage_schema_introspection_method (Storage-layer method tested
  directly, independent of MCP)

Closes the second of two gaps surfaced in the knowledge-mgmt-sota-uplift
initiative. Companion to PR #68 (search.tag_prefix). The two PRs are
deliberately decoupled — this one carries the storage-layer surface
extension; the other is MCP-layer-only.

* fix(memory): derive Default on SchemaIntrospection to satisfy clippy

The manual `impl Default for SchemaIntrospection` tripped
`clippy::derivable_impls` under the workspace's `-D warnings` CI gate.
All fields are types with `Default` impls (`u32`, `Option<T>`, `Vec<T>`,
`i64`), so deriving is equivalent and clippy-clean. Matches the existing
style of `ConsolidationResult` further down in the same file.
2026-06-11 14:24:42 -05:00
Luc Lauzon
5aa261398d
feat(mcp/search): add optional tag_prefix post-filter (#68)
Adds an optional `tag_prefix` string parameter to the `search` MCP tool.
When set, only results that carry at least one tag whose value starts
with the prefix are returned (case-sensitive, matching the existing
exact-tag semantics in memory_timeline / export / gc).

Motivation: external consumers that need "all memories tagged
`<class>:*`" (e.g. `meeting:standup`, `meeting:1-on-1`) currently have
three paths, all bad: (i) export everything and filter client-side
(heavy), (ii) enumerate the prefix space and pass exact tags as a list
(impractical for open-set tag classes), or (iii) read SQLite directly
(an anti-pattern that couples consumers to internal schema). This PR
closes that gap with a minimal, additive surface.

Implementation note: filter runs at the MCP layer, NOT in the storage
predicate. Rationale: (a) leaves crates/vestige-core/src/storage/
untouched, avoiding collision with PR #61's storage-trait extraction;
(b) `SearchResult.node.tags` is already loaded from the same JSON-array
column the brief's proposed SQL would scan, so the post-filter is
functionally equivalent; (c) post-filter applies BEFORE the reranker
so the cross-encoder does not waste cycles on memories the caller will
not receive, and BEFORE strengthen-on-access so dropped results do not
get a testing-effect boost they did not earn.

Headroom: when tag_prefix is set, the hybrid path doubles its overfetch
multiplier (capped at the existing 100 ceiling) and the concrete path
fetches 3x its normal limit, both to leave the post-filter enough pool
to still return ~limit results after thinning. The Stage 0
keyword-priority merge also re-applies the prefix filter so it cannot
re-introduce filtered-out memories.

Backwards-compat: parameter is optional, defaults to None; every existing
call shape and response shape is unchanged.

Tests:
- tags_match_prefix unit (prefix-vs-substring, case-sensitivity,
  tagless-memory semantics, empty-prefix corner case)
- schema introspection (property present, type=string, not required)
- hybrid-path filter excludes non-matching tag-classes
- hybrid-path filter excludes tagless memories
- backwards-compat: no tag_prefix → behavior unchanged
- concrete-path filter (literal-query branch) honors tag_prefix

Closes a gap surfaced in the knowledge-mgmt-sota-uplift initiative
(KMSU Session 89 audit; ~3,300-memory production Vestige).
2026-06-11 14:24:33 -05:00
Sam Valladares
241dfdd6cb
Merge pull request #67 from ShalokShalom/patch-1
Update CONFIGURATION.md with Opencode TUI details
2026-06-08 12:14:26 -05:00
Sam Valladares
603efdcb2c
Merge branch 'main' into patch-1 2026-06-08 12:13:09 -05:00
Sam Valladares
a355da99a6 Revert "Add developer launch kit for Vestige v2.1.23"
This reverts commit 00511948ff.
2026-06-02 12:40:48 -05:00
Sam Valladares
6a37586c5f Revert "Fix Pages workflow enablement and add Lobsters launch copy"
This reverts commit 67f9550e6c.
2026-06-02 12:40:48 -05:00
Sam Valladares
67f9550e6c Fix Pages workflow enablement and add Lobsters launch copy
Use configure-pages enablement so the landing deploy can succeed without
manual repo setup first.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:39:26 -05:00
Sam Valladares
00511948ff Add developer launch kit for Vestige v2.1.23
Dual-wave marketing assets (Receipt Lock + cognitive memory), GitHub Pages
landing, comparison doc, ready-to-post copy, growth-engine scripts, and a
dedicated marketing Vestige data-dir setup path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 12:38:18 -05:00
ShalokShalom
d8de7daf11
Update CONFIGURATION.md
Add link to specific options to place the config file
2026-06-02 16:08:39 +02:00
ShalokShalom
c9f457dd94
Update CONFIGURATION.md with Opencode TUI details
Added configuration instructions for Opencode TUI/Desktop.
2026-06-02 16:06:08 +02:00
Sam Valladares
cd496e562c docs: add Glama ownership metadata 2026-05-28 16:37:33 -05:00
Sam Valladares
8c18231a20 docs: keep README focused on v2.1.23 2026-05-28 13:34:09 -05:00
Sam Valladares
3df930ca7e Fix data-dir permission preservation 2026-05-27 20:00:46 -05:00
Sam Valladares
14b061f124
Release v2.1.23 Receipt Lock hardening
Hardens Sanhedrin Receipt Lock for model-agnostic use, adds fail-open telemetry and receipt docs, fixes smart_ingest batch safety, wires opt-in CUDA Qwen3 device selection, and refreshes dashboard/release assets.\n\nFixes #54\nFixes #58\nFixes #60\nRefs #59
2026-05-27 19:03:16 -05:00
Jan De Landtsheer
21f0b29bae
docs: rewrite local-dev-postgres-setup for container approach; bump pg16 -> pg18
Land the Postgres dev cluster recipe Jan provisioned on delandtj-home
(rootless podman + pgvector/pgvector:pg18, PG 18.4, pgvector 0.8.2) and
align all live ADR 0002 / Phase 2 sub-plan references from pg16 to pg18.

- docs/plans/local-dev-postgres-setup.md -- rewritten end-to-end:
  podman container vestige-pg with --restart=always, named volume
  vestige-pgdata, PGDATA=/var/lib/postgresql/data/pgdata, port mapping
  127.0.0.1:5432:5432, two-password split (superuser + app role),
  pgvector preinstalled, CREATE EXTENSION vector handled at setup,
  day-to-day commands, password rotation, dev-grade backup/restore,
  teardown, boot-persistence notes for rootless podman. Old native
  Arch install recipe moved to Out-of-scope (covered by image now).

- docs/adr/0002-phase-2-execution.md -- the open-thread mention of
  pgvector/pgvector:pg16 in the Follow-ups section now reads pg18.

- docs/plans/0002c-migrations.md -- container example in the local
  dev section updated to pg18.

- docs/plans/0002d-store-impl-bodies.md -- testcontainers GenericImage
  tag pg16 -> pg18; prose reference updated.

- docs/plans/0002h-testing-and-benches.md -- harness pg18 across
  testcontainers Postgres builder, image-caching prose, CI workflow
  example.

The archival master plan (docs/plans/0002-phase-2-postgres-backend.md)
keeps its original pg16 references intentionally; the supersession
notice already points readers to the live sub-plans.
2026-05-27 15:09:23 +02:00
Jan De Landtsheer
fc0681ed0f
Merge branch 'main' into design-adr-0002-phase-2-execution 2026-05-27 14:50:59 +02:00
Jan De Landtsheer
9ef8afdb20
docs(plans): add Phase 2 sub-plans 0002a-0002i + supersession notice
Nine Phase 2 sub-plans operationalising ADR 0002 against the Phase 2
master plan, each sized to fit a focused implementation session and
handed to Claude Code as a /goal brief without requiring the agent to
load the master plan.

Order of execution (each depends on the previous unless noted):
- 0002a-skeleton-and-feature-gate.md -- postgres-backend Cargo feature
  + PgMemoryStore skeleton with todo!() bodies. D1+D2.
- 0002b-pool-and-config.md -- PgPool builder, VestigeConfig/
  PostgresConfig, vestige.toml loader wired into vestige-mcp. D3+D7
  (master plan numbering).
- 0002c-migrations.md -- sqlx migrations 0001_init/0002_hnsw including
  D7 (users/groups/memberships, owner/visibility/shared_with_groups)
  and D8 (codebase column). SQLite V15 parity migration. D4.
- 0002d-store-impl-bodies.md -- real CRUD + registry bodies; trivial
  fts_search/vector_search bodies. D2+D6.
- 0002e-hybrid-search.md -- one-statement RRF query. D5.
- 0002f-migrate-cli.md -- vestige migrate copy (SQLite -> Postgres),
  --dry-run, idempotent re-runs, --allow-source-upgrade for pre-V15
  sources. D8+D10.
- 0002g-reembed.md -- vestige migrate reembed (offline rebuild).
  D9 + D10 reembed arm. Ships resolve_embedder helper as a workaround
  for the missing Embedder::from_name(&str) constructor.
- 0002h-testing-and-benches.md -- testcontainers harness, six
  integration test files, Criterion bench at 1k/100k. D14+D15.
- 0002i-runbook.md -- operator-facing deployment + day-2 runbook. D16.

Supersession notice added to the master plan (0002-phase-2-postgres-
backend.md) pointing at ADR 0002; body retained as archival reference.

PR B carries this commit plus the previous two (ADR 0002 + Phase 1
amendment sub-plans); no code change.
2026-05-27 09:35:58 +02:00
Jan De Landtsheer
697ade5b02
docs(plans): add Phase 1 amendment sub-plans 0001a/b/c
Three sub-plans operationalising ADR 0002 D1 + D3 on the existing
feat/storage-trait-phase1 branch (790c0c8, not yet pushed upstream):

- 0001a-trait-rewrite.md -- rewrite MemoryStore with
  #[trait_variant::make(MemoryStore: Send)] generating a non-Send
  LocalMemoryStore companion. Production callers use Arc<Storage> and are
  unaffected; only the trait declaration and SQLite impl block change.
- 0001b-sqlite-split.md -- pure code motion. Split sqlite.rs (8713 lines)
  into a sqlite/ directory (mod, crud, search, scheduling, graph, domain,
  registry, portable_sync, trait_impl). Public re-exports unchanged; tests
  green commit-by-commit. Depends on 0001a so trait_impl.rs picks up the
  trait_variant attribute once.
- 0001c-async-trait-sunset.md -- rewrite Embedder the same way, then
  remove async-trait = "0.1" from crates/vestige-core/Cargo.toml. End
  state: zero async_trait references in the workspace.

Together these three lands as PR A.
2026-05-27 09:35:37 +02:00
Jan De Landtsheer
c584ec8afe
docs(adr): add ADR 0002 -- Phase 2 execution
Binding ADR for Phase 2 Postgres backend integration plus the Phase 1
amendment that removes async_trait from the storage and embedder traits.

Decisions D1-D8:
- D1: sunset async_trait across MemoryStore + Embedder via trait_variant
- D2: PgMemoryStore::connect(url, max_connections) mirrors SqliteMemoryStore;
  no Embedder in constructor; register_model handles pgvector typmod
- D3: split sqlite.rs into a sqlite/ directory as Phase 1 amendment
- D4: postgres/ as a directory from day one
- D5: sub-plan layout -- 3 Phase 1 amendment + 9 Phase 2 sub-plans
- D6: no separate ADR for the SQLite split (pure code motion)
- D7: reserve multi-tenancy schema (users/groups/group_memberships +
  owner_user_id/visibility/shared_with_groups) in Phase 2 so Phase 3 auth
  is additive, not an online migration over an HNSW-indexed table
- D8: codebase promoted to a first-class indexed column on knowledge_nodes;
  mcp_client_id and session_id stay in metadata JSONB

PR cadence: PR A = Phase 1 amendment (code on feat/storage-trait-phase1);
PR B = this ADR + Phase 2 sub-plans (docs only); PR C = Phase 2
implementation. Phase 4 sharing_rules table sketched in Follow-ups.
2026-05-27 09:35:23 +02:00
Luc Lauzon
a8550410b0
feat(mcp): add per-tool _meta["anthropic/maxResultSizeChars"] annotation (#56)
Claude Code v2.1.91+ honors the per-tool annotation
`_meta["anthropic/maxResultSizeChars"]` (up to 500_000) to override
its 50K default truncation of `CallToolResult`. Without it, large
Vestige payloads are silently truncated and spilled to disk, forcing
the parent agent to chunk-read them.

Empirically observed truncation under realistic default parameters
(measured on v1.3.0 against ~3,300 memories; v2.x tool surface
preserves the same names + payload shapes):

  search(detail_level="full", limit=20)  -> 134,824 chars  -> truncated
  search(detail_level="summary", limit=10) ->  71,318 chars -> truncated
  memory_timeline(limit=30)              ->  83,626 chars  -> truncated

This patch:

1. Adds `meta: Option<serde_json::Value>` to `ToolDescription` with
   `#[serde(rename = "_meta")]` so the wire shape matches the MCP
   spec. Backwards-compatible (the field is optional +
   `skip_serializing_if`; older MCP clients ignore unknown JSON keys
   per the spec).

2. Derives `Default` on `ToolDescription` so existing call sites can
   adopt the new field via struct-update syntax
   (`..Default::default()`) without restating it.

3. Annotates the four high-payload tools per measurement-driven
   discipline; the other 21 tools deliberately do NOT carry the
   annotation (cargo-cult prevention — a generous cap on every tool
   dilutes the signal and trains future maintainers that the value
   is arbitrary):

   - search           -> 300_000 (2.2x headroom over observed peak)
   - memory_timeline  -> 200_000 (2.4x headroom over observed peak)
   - memory           -> 100_000 (single-record bounded)
   - codebase         -> 100_000 (future-growth bounded)

   Tools that COULD plausibly grow into the annotated set with future
   workload (`deep_reference`, `cross_reference`, `memory_graph`,
   `explore_connections`, `session_context`) are left unannotated
   until empirical measurement shows truncation under realistic use.

4. Adds three regression tests in `server::tests`:
   - test_high_payload_tools_have_max_result_size_annotation:
     pins each cap value + asserts <= 500K Anthropic ceiling
   - test_other_tools_do_not_carry_max_result_size_annotation:
     cargo-cult prevention; dynamically iterates `tools/list` and
     asserts every tool NOT in the discipline-prescribed set lacks
     the annotation (robust to new tools being added by future PRs)
   - test_meta_wire_shape_uses_underscore_meta_field:
     pins the serde rename to `_meta` (the spec'd wire name) so a
     refactor of `ToolDescription` cannot silently drop the rename

All 22 `server::tests` pass on v2.1.22 base (19 pre-existing + 3 new).
Full lib test suite: 379/380 pass; the 1 unrelated failure
(`tools::maintenance::tests::test_portable_export_writes_archive_to_storage_exports_dir`)
is a pre-existing Windows path-separator assertion bug in
`tools/maintenance.rs:823` (`path.ends_with("exports/portable-test.json")`
fails on Windows where the path uses `\`) — unaffected by this PR.

References:
- Anthropic CC v2.1.91 release notes (April 2026): "Added MCP tool
  result persistence override via _meta['anthropic/maxResultSizeChars']
  annotation (up to 500K), allowing larger results like DB schemas
  to pass through without truncation"
- claude-agent-sdk-python v0.1.55 #756: forward bookkeeping
  establishing the on-Tool-definition (not on-CallToolResult)
  semantics for this annotation

Co-authored-by: Peter Lauzon <inbijiburu@protonmail.com>
2026-05-25 13:49:51 -05:00
Sam Valladares
1399329810
Release v2.1.22 Sanhedrin receipts (#55) 2026-05-25 01:44:52 -05:00
Sam Valladares
c4e90f7f4a Fix clippy release event match 2026-05-24 16:27:48 -05:00
Sam Valladares
3c913f9942 Fix release dashboard validation 2026-05-24 16:13:06 -05:00
Sam Valladares
7eba0b1e97 Prepare agent-neutral hardening release 2026-05-24 16:09:44 -05:00
Sam Valladares
9936928be9 v2.1.2 Honest Memory
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / Release Build (x86_64-apple-darwin) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
Concrete search, irreversible purge, first-class contradictions tool, vestige update CLI, dense dream persistence fix, embedding-model upgrade repair, and a /dashboard/waitlist Pro early-access preview.

25 MCP tools. SQLite migration v13. Backwards compatible: 'delete' remains as a 'purge' alias.

Closes #50, #51.
2026-05-06 02:22:24 -05:00
Sam Valladares
c3c54d4e97 Prepare v2.1.2 simple update release
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / Release Build (x86_64-apple-darwin) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
2026-05-01 13:44:31 -05:00
Sam Valladares
c77b05078c Add simple Vestige update flow 2026-05-01 13:33:54 -05:00
Sam Valladares
fb250207a3 Fix embedding model upgrade consolidation
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
CI / Release Build (x86_64-apple-darwin) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
Fixes #51
2026-05-01 05:48:28 -05:00
Sam Valladares
4e9e11ac0b Fix dense dream connection persistence
Fixes #50
2026-05-01 05:37:27 -05:00
Sam Valladares
f3d63af12e Release v2.1.1 portable sync 2026-05-01 05:24:03 -05:00
Sam Valladares
738e4f7dce Document Windows Claude Desktop update flow
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / Release Build (x86_64-apple-darwin) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
2026-04-28 02:00:53 -05:00
Sam Valladares
c9e96b06fd Add vestige self-update command 2026-04-28 01:23:40 -05:00
Sam Valladares
d4313df759 Release v2.1.0
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
CI / Release Build (x86_64-apple-darwin) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
2026-04-27 13:20:51 -05:00
Sam Valladares
694e837898 Build dashboard assets for AhaGraph color mode
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
CI / Release Build (x86_64-apple-darwin) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
2026-04-26 14:37:22 -05:00
Sam Valladares
850ead6390 Add AhaGraph dashboard color mode 2026-04-26 14:36:38 -05:00
Sam Valladares
7a80f5e20f docs(readme): add v2.0.8 "Pulse" and v2.0.7 "Visible" to What's New
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / Release Build (x86_64-apple-darwin) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
The README jumped from v2.0.9 → v2.0.6 → v2.0.5, skipping two shipped
releases that sit between. Readers landing on the repo missed the
Reasoning Theater / Pulse InsightToast / Terrarium / Intel Mac work
(v2.0.8) and the Suppress HTTP endpoints / uptime / export panic fix
/ Migration V11 work (v2.0.7).

Summaries pulled from CHANGELOG.md for each release — condensed to
3-6 bullets per section, consistent framing across v2.0.5 through
v2.0.9. Older releases (v2.0 → v2.0.4) stay in the <details> block.

Tool / module / test counts already in sync with v2.0.9 (24 · 30 · 1,223).
2026-04-24 13:24:50 -05:00
Sam Valladares
da8c40935e
v2.0.9 "Autopilot" — backend event-subscriber + 3,091 LOC orphan cleanup (#46)
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
CI / Release Build (x86_64-apple-darwin) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
* feat(v2.0.9): Autopilot — backend event-subscriber routes 6 live events into cognitive hooks

The single architectural change that flips 14 dormant cognitive primitives
into active ones. Before this commit, Vestige's 20-event WebSocket bus had
zero backend subscribers — every emitted event flowed to the dashboard
animation layer and terminated. Cognitive modules with fully-built trigger
methods (synaptic_tagging.trigger_prp, predictive_memory.record_*,
activation_network.activate, prospective_memory.check_triggers, the 6h
auto-consolidation dreamer path) were never actually called from the bus.

New module `crates/vestige-mcp/src/autopilot.rs` spawns two tokio tasks at
startup:

1. Event subscriber — consumes the broadcast::Receiver, routes:
   - MemoryCreated  → synaptic_tagging.trigger_prp(CrossReference)
                    + predictive_memory.record_memory_access(id, preview, tags)
   - SearchPerformed → predictive_memory.record_query(q, [])
                    + record_memory_access on top 10 result_ids
   - MemoryPromoted → activation_network.activate(id, 0.3) spread
   - MemorySuppressed → emit Rac1CascadeSwept (was declared-never-emitted)
   - ImportanceScored (composite > 0.85 AND memory_id present)
                    → storage.promote_memory + re-emit MemoryPromoted
   - Heartbeat (memory_count > 700, 6h cooldown)
                    → spawned find_duplicates sweep (rate-limited)
   The loop holds the CognitiveEngine mutex only per-handler, never across
   an await, so MCP tool dispatch is never starved.

2. Prospective poller — 60s tokio::interval calls
   prospective_memory.check_triggers(Context { timestamp: now, .. }).
   Matched intentions are logged at info! level today; v2.5 "Autonomic"
   upgrades this to MCP sampling/createMessage for agent-side notifications.

ImportanceScored event gained optional `memory_id: Option<String>` field
(#[serde(default)], backward-compatible) so auto-promote has the id to
target. Both existing emit sites (server.rs tool dispatch, dashboard
handlers::score_importance) pass None because they score arbitrary content,
not stored memories — matches current semantics.

docs/VESTIGE_STATE_AND_PLAN.md §15 POST-v2.0.8 ADDENDUM records the full
three-agent audit that produced this architecture (2026-SOTA research,
active-vs-passive module audit, competitor landscape), the v2.0.9/v2.5/v2.6
ship order, and the one-line thesis: "the bottleneck was one missing
event-subscriber task; wiring it flips Vestige from memory library to
cognitive agent that acts on the host LLM."

Verified:
  - cargo check --workspace        clean
  - cargo clippy --workspace -- -D warnings  clean (let-chain on Rust 1.91+)
  - cargo test -p vestige-mcp --lib  356/356 passing, 0 failed

* fix(autopilot): supervisor + dedup race + opt-out env var

Three blockers from the 5-agent v2.0.9 audit, all in autopilot.rs.

1. Supervisor loops around both tokio tasks (event subscriber + prospective
   poller). Previously, if a cognitive hook panicked on a single bad memory,
   the spawned task died permanently and silently — every future event lost.
   Now the outer supervisor catches JoinError::is_panic(), logs the panic
   with full error detail, sleeps 5s, and respawns the inner task. Turns
   a permanent silent failure into a transient hiccup.

2. DedupSweepState struct replaces the bare Option<Instant> timestamp. It
   tracks the in-flight JoinHandle so the next Heartbeat skips spawning a
   second sweep while the first is still running. Previously, the cooldown
   timestamp was set BEFORE spawning the async sweep, which allowed two
   concurrent find_duplicates scans on 100k+ memory DBs where the sweep
   could exceed the 6h cooldown window. is_running() drops finished handles
   so a long-dead sweep doesn't block the next legitimate tick.

3. VESTIGE_AUTOPILOT_ENABLED=0 opt-out. v2.0.8 users updating in place
   can preserve the passive-library contract by setting the env var to
   any of {0, false, no, off}. Any other value (unset, 1, true, etc.)
   enables the default v2.0.9 Autopilot behavior. spawn() early-returns
   with an info! log before any task is spawned.

Audit breakdown:
- Agent 1 (internals): NO-GO → fixed (1, 2)
- Agent 2 (backward compat): NO-GO → fixed (3)
- Agent 3 (orphan cleanup): GO clean
- Agent 4 (runtime safety): GO clean
- Agent 5 (release prep): GO, procedural note logged

Verification:
- cargo check -p vestige-mcp: clean
- cargo test -p vestige-mcp --lib: 373 passed, 0 failed
- cargo clippy -p vestige-mcp --lib --bins -- -D warnings: clean

* chore(release): v2.0.9 "Autopilot"

Bump workspace + vestige-core + vestige-mcp + apps/dashboard to 2.0.9.
CHANGELOG [2.0.9] entry + README hero banner rewrite to "Autopilot".

Scope (two commits on top of v2.0.8):
- 0e9b260: 3,091 LOC orphan-code cleanup
- fe7a68c: Autopilot backend event-subscriber
- HEAD (this branch): supervisor + dedup race + opt-out env var hardening

Pure backend release — tool count unchanged (24), schema unchanged,
JSON-RPC shape unchanged, CLI flags unchanged. Only visible behavior
change is the Autopilot task running in the background, which is
VESTIGE_AUTOPILOT_ENABLED=0-gated.

Test gate: 1,223 passing / 0 failed (workspace, no-fail-fast).
Clippy: clean on vestige-mcp lib + bins with -D warnings.
Audit: 5 parallel agents (internals, backward compat, orphan cleanup,
runtime safety, release prep) — all GO after hardening commit.
2026-04-24 02:00:00 -05:00
Sam Valladares
0e9b260518 chore: remove 3,091 LOC of orphan code + fix ghost env-var docs
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
CI / Release Build (x86_64-apple-darwin) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
Nine tool modules in crates/vestige-mcp/src/tools/ had zero callers after
the v2.0.x unification work shipped *_unified + maintenance::* replacements.
They'd been #[allow(dead_code)]-papered over and forgotten. Verified each
module independently: grep for tools::<name>::, string dispatch in server.rs,
cross-crate usage — all nine returned zero external callers.

Removed modules (all superseded):
  checkpoint (364 LOC) — no callers anywhere
  codebase (298) — superseded by codebase_unified
  consolidate (36) — superseded by maintenance::execute_consolidate
  ingest (456) — superseded by smart_ingest
  intentions (1,093) — superseded by intention_unified
  knowledge (106) — no callers anywhere
  recall (403) — superseded by search_unified
  search (184) — superseded by search_unified
  stats (132) — superseded by maintenance::execute_system_status

Also removed:
  - EmotionCategory::base_arousal (10 LOC, zero callers)

Kept (still string-dispatched from server.rs):
  - context, feedback, memory_states, review, tagging

Doc fixes (ghost env vars that were documented but zero Rust source reads):
  - docs/CONFIGURATION.md — dropped VESTIGE_DATA_DIR, VESTIGE_LOG_LEVEL rows
    (neither is read anywhere; --data-dir CLI flag + RUST_LOG are the real
    mechanisms). Added the full real env-var table.
  - packages/vestige-mcp-npm/README.md — same two ghost rows dropped
  - docs/VESTIGE_STATE_AND_PLAN.md:399 — dropped VESTIGE_DATA_DIR row
  - docs/VESTIGE_STATE_AND_PLAN.md:709 — typo VESTIGE_API_KEY
    -> VESTIGE_AUTH_TOKEN (matches shipping convention), "open if unset"
    -> "auto-generated if unset" to match actual behavior

Verified post-cleanup:
  - cargo check --workspace       clean
  - cargo clippy --workspace -D warnings  clean
  - cargo test --workspace        1,223 passing / 0 failed
  - cargo build --release -p vestige-mcp  clean

Net: -3,091 LOC (14 files), zero behavior change, zero regressions.
2026-04-23 03:18:53 -05:00
Sam Valladares
6a807698ef
v2.0.8 Pulse — Reasoning Theater + Pulse Toast + Terrarium + Intel Mac (#44)
* docs: anti-hedging banner + master state-and-plan + UI roadmap

Project-level CLAUDE.md line 1 is now the anti-hedging banner referencing
the global rule at /Users/entity002/CLAUDE.md. When Sam commits to an
execution path (content cadence, shipping velocity, outreach, launch),
forecasts compute as if the commitment is already met — no discounting by
execution probability.

VESTIGE_STATE_AND_PLAN.md (1135 lines, 14 parts): single-page reference
any AI agent can read to plan the next phase without re-reading the repo.
Covers current state of every crate, the 24 MCP tools, the 19 WebSocket
event types, the 8 IDE integrations, and v2.1 through v3.0 roadmap.

UI_ROADMAP_v2.1_v2.2.md: compiled UI-gap analysis from 4 parallel research
agents (April 19). 10 critical UI gaps + 2026 bleeding-edge patterns +
unclaimed territory (menu bar widget, voice-first, AR/VR, time-scrubber).

* feat(v2.2-pulse): InsightToast + multi-process STORAGE docs

Two independent ship items landing together on the v2.2 branch ahead of
the Tuesday launch — a new UI surface that makes Vestige's cognitive
events visible in real time, and honest documentation of the multi-process
safety story that underpins the Stigmergic Swarm narrative.

**InsightToast** (apps/dashboard/src/lib/components/InsightToast.svelte,
apps/dashboard/src/lib/stores/toast.ts):
  The dashboard already had a working WebSocket event stream on
  ws://localhost:3927/ws that broadcast every cognitive event (dream
  completions, consolidation sweeps, memory promotions/demotions, active-
  forgetting suppression and Rac1 cascades, bridge discoveries). None of
  that was surfaced to a user looking at anything other than the raw feed
  view. InsightToast subscribes to the existing eventFeed derived store,
  filters the spammy lifecycle events (Heartbeat, SearchPerformed,
  RetentionDecayed, ActivationSpread, ImportanceScored, MemoryCreated),
  and translates the narrative events into ephemeral toasts with a
  bioluminescent colored accent matching EVENT_TYPE_COLORS.

  Design notes:
  - Rate-limited ConnectionDiscovered at 1.5s intervals (dreams emit many).
  - Max 4 visible toasts, auto-dismiss at 4.5-7s depending on event weight.
  - Click or Enter/Space to dismiss early.
  - Bottom-right on desktop, top-banner on mobile.
  - Reduced-motion honored via prefers-reduced-motion.
  - Zero new websocket subscriptions — everything piggybacks on the
    existing derived store.

  Also added a "Preview Pulse" button to Settings -> Cognitive Operations
  that fires a synthetic sequence of four toasts (DreamCompleted,
  ConnectionDiscovered, MemorySuppressed, ConsolidationCompleted) so
  the animation is demoable without waiting for real cognitive activity.

**Multi-Process Safety section in docs/STORAGE.md**:
  Grounds the Stigmergic Swarm story with concrete tables of what the
  current WAL + 5s busy_timeout configuration actually supports vs what
  remains experimental. Key honest points:
  - Shared --data-dir + ONE vestige-mcp + N clients is the shipping
    pattern for multi-agent coordination.
  - Two vestige-mcp processes writing the same file is experimental —
    documented with the lsof + pkill recovery path.
  - Roadmap lists the three items that would promote it to "supported":
    advisory file lock, retry-with-jitter on SQLITE_BUSY, and a
    concurrent-writer load test.

Build + typecheck:
- npm run check: 0 errors, 0 warnings across 583 files
- npm run build: clean static build, adapter-static succeeds

* feat(v2.3-terrarium): Memory Birth Ritual + event pipeline fix

v2.3 "Terrarium" headline feature. When a MemoryCreated event arrives, a
glowing orb materialises in the cosmic center (camera-relative z=-40),
gestates for ~800ms growing from a tiny spark into a full orb, then arcs
along a dynamic quadratic Bezier curve to the live position of the real
node, and on arrival hands off to the existing RainbowBurst + Shockwave +
RippleWave cascade. The target position is re-resolved every frame so
the force simulation can move the destination during flight without the
orb losing its mark.

**New primitive — EffectManager.createBirthOrb()** (effects.ts):
  Accepts a camera, a color, a live target-position getter, and an
  arrival callback. Owns a sprite pair (outer halo + inner bright core),
  both depthTest:false with renderOrder 999/1000 so the orb is always
  visible through the starfield and the graph.
  - Gestation phase: easeOutCubic growth + sinusoidal pulse, halo tints
    from neutral to event color as the ritual charges.
  - Flight phase: QuadraticBezierCurve3 with control point at midpoint
    raised on Y by 30 + 15% of orb-to-target distance (shooting-star
    arc). Sampled with easeInOutQuad. Orb shrinks ~35% approaching target.
  - Arrival: fires onArrive callback once, then fades out over 8 frames
    while expanding slightly (energy dispersal).
  - Caller's onArrive triggers the burst cascade at arrivePos (NOT the
    original spawnPos — the force sim may have moved the target during
    the ritual, so we re-read nodeManager.positions on arrival).
  - Dispose path integrated with existing EffectManager.dispose().

**Event pipeline fix — Graph3D.processEvents()**:
  Previously tracked `processedEventCount` assuming APPEND order, but
  websocket.ts PREPENDS new events (index 0) and caps the array at
  MAX_EVENTS. Result: only the first MemoryCreated event after page
  load fired correctly; subsequent ones reprocessed the oldest entry.
  Fixed to walk from index 0 until hitting the last-processed event
  by reference identity — correct regardless of array direction or
  eviction pressure. Events are then processed oldest-first so causes
  precede effects. Found while wiring the v2.3 demo button; would have
  manifested as "first orb only" in production.

**Demo trigger** (Settings -> Birth Ritual Preview):
  Button that calls websocket.injectEvent() with a synthetic
  MemoryCreated event, cycling through node types (fact / concept /
  pattern / decision / person / place) to showcase the type-color
  mapping. Downstream consumers can't distinguish synthetic from real,
  so this drives the full ritual end-to-end. Intended for demo clip
  recording for the Wednesday launch.

**Test coverage:**
  - events.test.ts now tests the v2.3 birth ritual path: spawns 2+
    sprites in the scene immediately, and fires the full arrival
    cascade after driving the effects.update() loop past the ritual
    duration.
  - three-mock.ts extended with Vector3.addVectors, Vector3.applyQuaternion,
    Color.multiplyScalar, Quaternion, QuadraticBezierCurve3, Texture,
    and Object3D.quaternion/renderOrder so production code runs unaltered
    in tests.

Build + typecheck:
- npm run check: 0 errors, 0 warnings across 583 files
- npm test: 251/251 pass (net +0 from v2.2)
- npm run build: clean adapter-static output

The Sanhedrin Shatter (anti-birth ritual for hallucination veto) needs
server-side event plumbing and is deferred. Ship this as the Wednesday
visual mic-drop.

* fix(v2.3): 5 FATAL bugs + 4 god-tier upgrades from post-ship audit

Post-ship audit surfaced 6 FATALs and 4 upgrades. Shipping 5 of the 6 +
all 4 upgrades. FATAL 4 (VRAM hemorrhage from un-pooled label canvases
in createTextSprite) is pre-existing, not from this session, and scoped
separately for a proper texture-pool refactor.

**FATAL 1 — Toast Silent Lobotomy** (stores/toast.ts)
Subscriber tracked events[0] only. When Svelte batched multiple events
in one update tick (swarm firing DreamCompleted + ConnectionDiscovered
within the same millisecond), every event but the newest got silently
dropped. Fixed to walk from index 0 until hitting lastSeen — same
pattern as Graph3D.processEvents. Processes oldest-first to preserve
narrative order.

**FATAL 2 — Premature Birth** (graph/nodes.ts + graph/events.ts)
Orb flight is 138 frames; materialization was 30 frames. Node popped
fully grown ~100 frames before orb arrived — cheap UI glitch instead
of a biological birth. Added `addNode(..., { isBirthRitual: true })`
option that reserves the physics slot but hides mesh/glow/label and
skips the materializing queue. New `igniteNode(id)` flips visibility
and enqueues materialization. events.ts onArrive now calls igniteNode
at the exact docking moment, so the elastic spring-up peaks on impact.

**FATAL 3 — 120Hz ProMotion Time-Bomb** (components/Graph3D.svelte)
All physics + effect counters are frame-based. On a 120Hz display every
ritual ran at 2x speed. Added a `lastTime`-based governor in animate()
that early-returns if dt < 16ms, clamping effective rate to ~60fps.
`- (dt % 16)` carry avoids long-term drift. Zero API changes; tonight's
fast fix until physics is rewritten to use dt.

**FATAL 5 — Bezier GC Panic** (graph/effects.ts birth-orb update)
Flight phase allocated a new Vector3 (control point) and a new
QuadraticBezierCurve3 every frame per orb. With 3 orbs in flight that's
360 objects/sec for the GC to collect. Rewrote as inline algebraic
evaluation — zero allocations per frame, identical curve.

**FATAL 6 — Phantom Shockwave** (graph/events.ts)
A 166ms setTimeout fired the 2nd shockwave. If the user navigated
away during that window the scene was disposed, the timer still
fired, and .add() on a dead scene threw unhandled rejection. Dropped
the setTimeout entirely; both shockwaves fire immediately in onArrive
with different scales/colors for the same layered-crash feel.

**UPGRADE 1 — Sanhedrin Shatter** (graph/effects.ts birth-orb update)
If getTargetPos() returns undefined AFTER gestation (target node was
deleted mid-ritual — Stop hook sniping a hallucination), the orb
turns blood-red, triggers a violent implosion in place, and skips
the arrival cascade. Cognitive immune system made visible.

**UPGRADE 2 — Newton's Cradle** (graph/events.ts onArrive)
On docking the target mesh's scale gets bumped 1.8×, so the elastic
materialization + force-sim springs physically recoil instead of the
orb landing silently. The graph flinches when an idea is born into it.

**UPGRADE 3 — Hover Panic** (stores/toast.ts + InsightToast.svelte)
Paused dwell timer on mouseenter/focus, resume on mouseleave/blur.
Stored remaining ms at pause so resume schedules a correctly-sized
timer. CSS pairs via `animation-play-state: paused` on the progress
bar. A toast the user is reading no longer dismisses mid-sentence.

**UPGRADE 4 — Event Horizon Guard** (components/Graph3D.svelte)
If >MAX_EVENTS (200) events arrive in one tick, lastProcessedEvent
falls off the end of the array and the walk consumes ALL 200 entries
as "fresh" — GPU meltdown from 200 simultaneous births. Detect the
overflow and drop the batch with a console.warn, advancing the
high-water mark so next frame is normal.

Build + test:
- npm run check: 0 errors, 0 warnings
- npm test: 251/251 pass
- npm run build: clean static build

* test(v2.3): full e2e + integration coverage for Pulse + Birth Ritual

Post-ship verification pass — five parallel write-agents produced 229 new
tests across vitest units, vitest integration, and Playwright browser e2e.
Net suite: 361 vitest pass (up from 251, +110) and 9/9 Playwright pass on
back-to-back runs.

**toast.test.ts (NEW, 661 lines, 42 tests)**
  Silent-lobotomy batch walk proven (multi-event tick processes ALL, not
  just newest, oldest-first ordering preserved). Hover-panic pause/resume
  with remaining-ms math. All 9 event type translations asserted, all 11
  noise types asserted silent. ConnectionDiscovered 1500ms throttle.
  MAX_VISIBLE=4 eviction. clear() tears down all timers. fireDemoSequence
  staggers 4 toasts at 800ms intervals. vi.useFakeTimers + vi.mock of
  eventFeed; vi.resetModules in beforeEach for module-singleton isolation.

**websocket.test.ts (NEW, 247 lines, 30 tests)**
  injectEvent adds to front, respects MAX_EVENTS=200 with FIFO eviction,
  triggers eventFeed emissions. All 6 derived stores (isConnected,
  heartbeat, memoryCount, avgRetention, suppressedCount, uptimeSeconds)
  verified — defaults, post-heartbeat values, clearEvents preserves
  lastHeartbeat. 13 formatUptime boundary cases (0/59/60/3599/3600/
  86399/86400 seconds + negative / NaN / ±Infinity).

**effects.test.ts (EXTENDED, +501 lines, +21 tests, 51 total)**
  createBirthOrb full lifecycle — sprite count (halo + core), cosmic
  center via camera.quaternion, gestation phase (position lock, opacity
  rise, scale easing, color tint), flight Bezier arc above linear
  midpoint at t=0.5, dynamic mid-flight target redirect. onArrive fires
  exactly once at frame 139. Post-arrival fade + disposal cleans scene
  children. Sanhedrin Shatter: target goes undefined mid-flight →
  onArrive NEVER called, implosion spawned, halo blood-red, eventual
  cleanup. dispose() cleans active orbs. Multiple simultaneous orbs.
  Custom gestation/flight frame opts honored. Zero-alloc invariant
  smoke test (6 orbs × 150 frames, no leaks).

**nodes.test.ts (EXTENDED, +197 lines, +10 tests, 42 total)**
  addNode({isBirthRitual:true}) hides mesh/glow/label immediately,
  stamps birthRitualPending sentinel with correct totalFrames +
  targetScale, does NOT enqueue materialization. igniteNode flips
  visibility + enqueues materialization. Idempotent — second call
  no-op. Non-ritual nodes unaffected. Unknown id is safe no-op.
  Position stored in positions map while invisible (force sim still
  sees it). removeNode + late igniteNode is safe.

**events.test.ts (EXTENDED, +268 lines, +7 tests, 55 total)**
  MemoryCreated → mesh hidden immediately, 2 birth-orb sprites added,
  ZERO RingGeometry meshes and ZERO Points particles at spawn. Full
  ritual drive → onArrive fires, node visible + materializing, sentinel
  cleared. Newton's Cradle: target mesh scale exactly 0.001 * 1.8 right
  after arrival. Dual shockwave: exactly 2 Ring meshes added. Re-read
  live position on arrival — force-sim motion during ritual → burst
  lands at the NEW position. Sanhedrin abort path → rainbow burst,
  shockwave, ripple wave are NEVER called (vi.spyOn).

**three-mock.ts (EXTENDED)**
  Added Color.setRGB — production Three.js has it, the Sanhedrin-
  Shatter path in effects.ts uses it. Two write-agents independently
  monkey-patched the mock inline; consolidated as a 5-line mock
  addition so tests stay clean.

**e2e/pulse-toast.spec.ts (NEW, 235 lines, 6 Playwright tests)**
  Navigate /dashboard/settings → click Preview Pulse → assert first
  toast appears within 500ms → assert >= 2 toasts visible at peak.
  Click-to-dismiss removes clicked toast (matched by aria-label).
  Hover survives >8s past the 5.5s dwell. Keyboard Enter dismisses
  focused toast. CSS animation-play-state:paused on .toast-progress-
  fill while hovered, running on mouseleave. Screenshots attached to
  HTML report. Zero backend dependency (fireDemoSequence is purely
  client-side).

**e2e/birth-ritual.spec.ts (NEW, 199 lines, 3 Playwright tests)**
  Canvas mounts on /dashboard/graph (gracefully test.fixme if MCP
  backend absent). Settings button injection + SPA route to /graph
  → screenshot timeline at t=0/500/1200/2000/2400/3000ms attached
  to HTML report. pageerror + console-error listeners catch any
  crash (would re-surface FATAL 6 if reintroduced). Three back-to-
  back births — no errors, canvas still dispatches clicks.

Run commands:
  cd apps/dashboard && npm test           # 361/361 pass, ~600ms
  cd apps/dashboard && npx playwright test # 9/9 pass, ~25s

Typecheck: 0 errors, 0 warnings. Build: clean adapter-static.

* fix(graph): default /api/graph to newest-memory center, add sort param

memory_timeline PR #37 exposed the same class of bug in the graph
endpoint: the dashboard Graph page (and the /api/graph endpoint it
hits) defaulted to centering on the most-connected memory, ran BFS at
depth 3, and capped the subgraph at 150 nodes. On a mature corpus this
clustered the visualization around a historical hotspot and hid freshly
ingested memories that hadn't accumulated edges yet. User-visible
symptom: TimeSlider on /graph showing "Feb 21 → Mar 1 2026" when the
database actually contains memories through today (Apr 20).

**Backend (`crates/vestige-mcp/src/dashboard/handlers.rs`):**
- `GraphParams` gains `sort: Option<String>` (accepted: "recent" |
  "connected", unknown falls back to "recent").
- New internal `GraphSort` enum + case-insensitive `parse()`.
- Extracted `default_center_id(storage, sort)` so handler logic and
  tests share the same branching. Recent path picks `get_all_nodes(1,
  0)` (ORDER BY created_at DESC). Connected path picks
  `get_most_connected_memory`, degrading gracefully to recent if the
  DB has no edges yet.
- Default behaviour flipped from "connected" to "recent" — matches
  user expectation of "show me my recent stuff".

**Dashboard (`apps/dashboard`):**
- `api.graph()` accepts `sort?: 'recent' | 'connected'` with JSDoc
  explaining the rationale.
- `/graph/+page.svelte` passes `sort: 'recent'` when no query or
  center_id is active. Query / center_id paths unchanged — they
  already carry their own centering intent.

**Tests:** 6 new unit tests in `handlers::tests`:
- `graph_sort_parse_defaults_to_recent` (None, empty, garbage,
  "recent", "Recent", "RECENT")
- `graph_sort_parse_accepts_connected_case_insensitive`
- `default_center_id_recent_returns_newest_node` — ingest 3 nodes,
  assert newest is picked
- `default_center_id_connected_prefers_hub_over_newest` — wire a hub
  node with 3 spokes, then ingest a newer "lonely" node; assert the
  hub wins in Connected mode even though it's older
- `default_center_id_connected_falls_back_to_recent_when_no_edges`
  — fresh DB with no connections still returns newest, not 404
- `default_center_id_returns_not_found_on_empty_db` — both modes
  return 404 cleanly on empty storage

Build + test:
- cargo test -p vestige-mcp --lib handlers:: → 6/6 pass
- cargo test --workspace --lib → 830/830 pass, 0 failed
- cargo clippy -p vestige-core -p vestige-mcp --lib -- -D warnings →
  clean
- npm run check → 0 errors, 0 warnings
- npm test → 361/361 pass

Binary already installed at ~/.local/bin/vestige-mcp (copied from
cargo build --release -p vestige-mcp). New Claude Desktop / Code
sessions will pick it up automatically when they respawn their MCP
subprocess. The dashboard HTTP server on port 3927 needs a manual
relaunch from a terminal with the usual pattern:

    nohup bash -c 'tail -f /dev/null | \
        VESTIGE_DASHBOARD_ENABLED=true ~/.local/bin/vestige-mcp' \
        > /tmp/vestige-mcp.log 2>&1 & disown

* feat(v2.4): UI expansion — 8 new surfaces exposing the cognitive engine

Sam asked: "Build EVERY SINGLE MISSING UI PIECE." 10 parallel agents shipped
10 new viewports over the existing Rust backend, then 11 audit agents
line-by-line reviewed each one, extracted pure-logic helpers, fixed ~30
bugs, and shipped 549 new unit tests. Everything wired through the layout
with single-key shortcuts and a live theme toggle.

**Eight new routes**
- `/reasoning`  — Reasoning Theater: Cmd+K ask palette → animated 8-stage
  deep_reference pipeline + FSRS-trust-scored evidence cards +
  contradiction arcs rendered as live SVG between evidence nodes
- `/duplicates` — threshold-driven cluster detector with winner selection,
  Merge/Review/Dismiss actions, debounced slider
- `/dreams`     — Dream Cinema: trigger dream + scrubbable 5-stage replay
  (Replay → Cross-reference → Strengthen → Prune → Transfer) + insight
  cards with novelty glow
- `/schedule`   — FSRS Review Calendar: 6×7 grid with urgency color
  bands (overdue/today/week/future), retention sparkline, expand-day list
- `/importance` — 4-channel radar (Novelty/Arousal/Reward/Attention) with
  composite score + top-important list
- `/activation` — live spreading-activation view: search → SVG concentric
  rings with decay animation + live-mode event feed
- `/contradictions` — 2D cosmic constellation of conflicting memories,
  arcs colored by severity, tooltips with previews
- `/patterns`   — cross-project pattern transfer heatmap with category
  filters, top-transferred sidebar

**Three layout additions**
- `AmbientAwarenessStrip` — slim top band with retention vitals, at-risk
  count, active intentions, recent dream, activity sparkline, dreaming
  indicator, Sanhedrin-watch flash. Pure `$derived` over existing stores.
- `ThemeToggle` — dark/light/auto cycle with matchMedia listener,
  localStorage persistence, SSR-safe, reduced-motion-aware. Rendered in
  sidebar footer next to the connection dot.
- `MemoryAuditTrail` — per-memory Sources panel integrated as a
  Content/Audit tab into the existing /memories expansion.

**Pure-logic helper modules extracted (for testability + reuse)**
  reasoning-helpers, duplicates-helpers, dream-helpers, schedule-helpers,
  audit-trail-helpers, awareness-helpers, contradiction-helpers,
  activation-helpers, patterns-helpers, importance-helpers.

**Bugs fixed during audit (not exhaustive)**
- Trust-color inconsistency between EvidenceCard and the page confidence
  ring (0.75 boundary split emerald vs amber)
- `new Date('garbage').toLocaleDateString()` returned literal "Invalid Date"
  in 3 components — all now return em-dash or raw string
- NaN propagation in `Math.max(0, Math.min(1, NaN))` across clamps
- Off-by-one PRNG in audit-trail seeded mock (seed === UINT32_MAX yielded
  rand() === 1.0 → index out of bounds)
- Duplicates dismissals keyed by array index broke on re-fetch; now keyed
  by sorted cluster member IDs with stale-dismissal pruning
- Empty-cluster crash in DuplicateCluster.pickWinner
- Undefined tags crash in DuplicateCluster.safeTags
- Debounce timer leak in threshold slider (missing onDestroy cleanup)
- Schedule day-vs-hour granularity mismatch between calendar cell and
  sidebar list ("today" in one, "in 1d" in the other)
- Schedule 500-memory hard cap silently truncated; bumped to 2000 + banner
- Schedule DST boundary bug in daysBetween (wall-clock math vs
  startOfDay-normalized)
- Dream stage clamp now handles NaN/Infinity/floats
- Dream double-click debounce via `if (dreaming) return`
- Theme setTheme runtime validation; initTheme idempotence (listener +
  style-element dedup on repeat calls)
- ContradictionArcs node radius unclamped (trust < 0 or > 1 rendered
  invalid sizes); tooltip position clamp (could push off-canvas)
- ContradictionArcs $state closure capture (width/height weren't reactive
  in the derived layout block)
- Activation route was MISSING from the repo — audit agent created it
  with identity-based event filtering and proper RAF cleanup
- Layout: ThemeToggle was imported but never rendered — now in sidebar
  footer; sidebar overflow-y-auto added for the 16-entry nav

**Tests — 549 new, 910 total passing (0 failures)**
  ReasoningChain     42 | EvidenceCard       50
  DuplicateCluster   64 | DreamStageReplay   19
  DreamInsightCard   43 | FSRSCalendar       32
  MemoryAuditTrail   45 | AmbientAwareness   60
  theme (store)      31 | ContradictionArcs  43
  ActivationNetwork  54 | PatternTransfer    31
  ImportanceRadar    35 | + existing 361 tests still green

**Gates passed**
- `npm run check`:  0 errors, 0 warnings across 623 files
- `npm test`:       910/910 passing, 22 test files
- `npm run build`:  clean adapter-static output

**Layout wiring**
- Nav array expanded 8 → 16 entries (existing 8 + 8 new routes)
- Single-key shortcuts added: R/A/D/C/P/U/X/N (no conflicts with
  existing G/M/T/F/E/I/S/,)
- Cmd+K palette search works across all 16
- Mobile nav = top 5 (Graph, Reasoning, Memories, Timeline, Feed)
- AmbientAwarenessStrip mounted as first child of <main>
- ThemeToggle rendered in sidebar footer (was imported-but-unmounted)
- Theme initTheme() + teardown wired into onMount cleanup chain

Net branch delta: 47 files changed, +13,756 insertions, -6 deletions

* chore(release): v2.0.8 "Pulse"

Bundled release: Reasoning Theater wired to the 8-stage deep_reference
cognitive pipeline, Pulse InsightToast, Memory Birth Ritual (v2.3
Terrarium), 7 new dashboard surfaces (/duplicates, /dreams, /schedule,
/importance, /activation, /contradictions, /patterns), 3D graph
brightness system with auto distance-compensation + user slider, and
contradiction-detection + primary-selection hardening in the
cross_reference tool. Intel Mac (x86_64-apple-darwin) also flows through
to the release matrix from PR #43.

Added:
- POST /api/deep_reference — HTTP surface for the 8-stage pipeline
- DeepReferenceCompleted WebSocket event (primary + supporting +
  contradicting memory IDs for downstream graph animation)
- /reasoning route, full UI + Cmd+K Ask palette
- 7 new dashboard surfaces exposing the cognitive engine
- Graph brightness slider + localStorage persistence + distance-based
  emissive compensation so nodes don't disappear into fog at zoom-out

Fixed:
- Contradiction-detection false positives: adjacent-domain memories no
  longer flagged as conflicts (NEGATION_PAIRS wildcards removed,
  shared-words floor 2 → 4, topic-sim floor 0.15 → 0.55, STAGE 5
  overlap floor 0.15 → 0.4)
- Primary-memory selection: unified composite 0.5 × relevance + 0.2 ×
  trust + 0.3 × term_presence with hard topic-term filter, closing the
  class of bug where off-topic high-trust memories won queries about
  specific subjects
- Graph default-load fallback from sort=recent to sort=connected when
  the newest memory is isolated, both backend and client

Changed:
- Reasoning page information hierarchy: chain renders first as hero,
  confidence meter + Primary Source citation footer below
- Cargo feature split: embeddings code-only + ort-download | ort-dynamic
  backends; defaults preserve identical behavior for existing consumers
- CI release-build now gates PRs too so multi-platform regressions
  surface pre-merge
2026-04-23 02:21:11 -05:00
Sam Valladares
5b993e841f
fix(#41): restore Intel Mac build via ort-dynamic + Homebrew ONNX Runtime (#43)
* fix: restore Intel Mac build via ort-dynamic + system libonnxruntime

Microsoft is discontinuing x86_64 macOS ONNX Runtime prebuilts after
v1.23.0, so ort-sys 2.0.0-rc.11 can't ship an Intel Mac binary and never
will. Previous Intel Mac attempts kept dying in the ort-sys build script
with "does not provide prebuilt binaries for the target x86_64-apple-darwin
with feature set (no features)." Issue #41 was the latest casualty.

Fix: route Intel Mac through the ort-dynamic feature path (runtime dlopen
against a system libonnxruntime installed via Homebrew). This sidesteps
ort-sys prebuilts entirely and works today.

Changes:

- crates/vestige-core/Cargo.toml: split `embeddings` into code-only vs
  backend-choice. The embeddings feature now just pulls fastembed + hf-hub
  + image-models and activates the 27 #[cfg(feature = "embeddings")] gates
  throughout the crate. New `ort-download` feature carries the
  download-binaries-native-tls backend (the historical default). Existing
  `ort-dynamic` feature now transitively enables `embeddings`, so the
  cfg gates stay active when users swap backends.

  Default feature set expands `["embeddings", ...]` -> `["embeddings",
  "ort-download", ...]` so existing consumers see identical behavior.

- crates/vestige-mcp/Cargo.toml: mirrors the split. Adds `ort-download`
  feature that chains to vestige-core/ort-download, keeps `ort-dynamic`
  that chains to vestige-core/ort-dynamic. Both transitively pull
  `embeddings`. Default adds `ort-download` so `cargo install vestige-mcp`
  still picks the prebuilt-ort backend like before.

- .github/workflows/ci.yml: re-adds x86_64-apple-darwin to the
  release-build matrix with `--no-default-features --features
  ort-dynamic,vector-search`. Adds a `brew install onnxruntime` step that
  sets ORT_DYLIB_PATH from `brew --prefix onnxruntime`.

- .github/workflows/release.yml: re-adds x86_64-apple-darwin to the
  release matrix with the same flags + brew install step. The Intel Mac
  tarball now also bundles docs/INSTALL-INTEL-MAC.md so binary consumers
  get the `brew install onnxruntime` + ORT_DYLIB_PATH prereq out of the
  box.

- docs/INSTALL-INTEL-MAC.md: new install guide covering the Homebrew
  prereq, binary install, source build, troubleshooting, and the v2.1
  ort-candle migration plan.

- README.md: replaces the "Intel Mac and Windows build from source only"
  paragraph with the prebuilt Intel Mac install (brew + curl + env var)
  and a link to the full guide. Platform table updated: Intel Mac back
  on the "prebuilt" list.

Verified locally on aarch64-apple-darwin:
- `cargo check --release -p vestige-mcp` -> clean (default features)
- `cargo check --release -p vestige-mcp --no-default-features
   --features ort-dynamic,vector-search` -> clean

Runtime path on Intel Mac (verified on CI):
  brew install onnxruntime
  export ORT_DYLIB_PATH=$(brew --prefix onnxruntime)/lib/libonnxruntime.dylib
  vestige-mcp --version

Fixes #41. Long-term plan (v2.1): migrate to ort-candle pure-Rust backend
so no system ONNX Runtime dep is needed on any platform.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(ci): drop unused brew install + ORT_DYLIB_PATH from CI steps

Build is a cross-compile (macos-latest runner is Apple Silicon targeting
x86_64-apple-darwin) and ort-load-dynamic doesn't link libonnxruntime at
build time — only at runtime via dlopen. So the brew install step and
ORT_DYLIB_PATH export were ceremony without payload. Removed to cut CI
time. Runtime setup remains documented in docs/INSTALL-INTEL-MAC.md for
end users installing the tarball on their own Intel Mac.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: run release-build on PRs too — catch Intel Mac regressions pre-merge

Previously release-build was gated behind `github.ref == 'refs/heads/main'`,
so the Intel Mac, aarch64-apple-darwin, and Linux release targets were only
validated AFTER merge to main. If someone broke the Intel Mac cross-compile
by touching feature flags or Cargo dependencies, we'd only find out when
the release tag was cut and the job exploded on main. Extending the guard
to also fire on pull_request means regressions surface in the PR status
check instead of on a release branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 02:03:45 -05:00
Sam Valladares
d3a2274778
Merge pull request #39 from delandtj/fts-implicit-and-fix
Some checks are pending
CI / Test (macos-latest) (push) Waiting to run
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Release Build (aarch64-apple-darwin) (push) Blocked by required conditions
CI / Release Build (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Test Suite / Unit Tests (push) Waiting to run
Test Suite / MCP E2E Tests (push) Waiting to run
Test Suite / User Journey Tests (push) Blocked by required conditions
Test Suite / Dashboard Build (push) Waiting to run
Test Suite / Code Coverage (push) Waiting to run
fix(fts): match multi-word queries as implicit-AND, not adjacent phrase
2026-04-22 13:27:44 -05:00
Jan De Landtsheer
9c633c172b
Added postgres admin
added amends to the postgres backend/phase2
2026-04-22 12:10:39 +02:00
Jan De Landtsheer
0d273c5641
docs: ADR 0001 + Phase 1-4 implementation plans
Pluggable storage backend, network access, and emergent domain
classification. Introduces MemoryStore + Embedder traits, PgMemoryStore
alongside SqliteMemoryStore, HTTP MCP + API key auth, and HDBSCAN-based
domain clustering. Phase 5 federation deferred to a follow-up ADR.

- docs/adr/0001-pluggable-storage-and-network-access.md -- Accepted
- docs/plans/0001-phase-1-storage-trait-extraction.md
- docs/plans/0002-phase-2-postgres-backend.md
- docs/plans/0003-phase-3-network-access.md
- docs/plans/0004-phase-4-emergent-domain-classification.md
- docs/prd/001-getting-centralized-vestige.md -- source RFC
2026-04-22 12:10:24 +02:00
Jan De Landtsheer
5e411833f5
fix(fts): match multi-word queries as implicit-AND, not adjacent phrase
sanitize_fts5_query wraps queries in quotes, producing FTS5 phrase search
where the words must be adjacent. So "quantum physics" against a doc
containing "quantum entanglement superposition physics" returned no FTS
hit; semantic search hid the issue whenever embeddings were enabled.

Add sanitize_fts5_terms that splits into space-separated terms (FTS5
implicit AND, any order, any position), and use it in:

- keyword_search_with_scores (hybrid-search FTS leg) so multi-word
  queries return docs containing all words regardless of adjacency
- a new SqliteMemoryStore::search_terms inherent method for callers
  that want individual-term matching without the full hybrid pipeline

sanitize_fts5_query stays in place; KeywordSearcher still uses it
(phrase semantics preserved where they were wanted).
2026-04-22 10:52:07 +02:00
Sam Valladares
2391acf480
Merge pull request #37 from Unforgettable-93/unforgettable-timeline-tags-filter
Some checks failed
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
Test Suite / Unit Tests (push) Has been cancelled
Test Suite / MCP E2E Tests (push) Has been cancelled
Test Suite / Dashboard Build (push) Has been cancelled
Test Suite / Code Coverage (push) Has been cancelled
CI / Release Build (aarch64-apple-darwin) (push) Has been cancelled
CI / Release Build (x86_64-unknown-linux-gnu) (push) Has been cancelled
Test Suite / User Journey Tests (push) Has been cancelled
fix(timeline): push node_type and tags filters into SQL WHERE
2026-04-20 18:21:58 -05:00