Wire Step 8.5 into run_consolidation (bounded + idempotent via durable
Causal edge), share looks_like_failure/extract_entities across MCP tool +
CLI + auto-fire, add whole-word failure markers (API_TIMEOUT no longer
false-fires), --json CLI for CauseBench, and refactor the Redmine SSRF
guard to std::net (drops the dangling url-crate dep).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All verified against real code:
- github fetch_updated: propagate fetch_comments errors instead of
unwrap_or_default() (silent failure stored a comment-less record with a
corrupted hash AND advanced the cursor past it = permanent gap)
- relationships load_relationships: advance next_id past loaded rel-N ids so
new_id() can't collide with persisted ids
- speculative store_pending_predictions: merge instead of clear (two predict()
calls without an intervening record_usage() wiped the first batch's accounting)
- embeddings/code is_comment_only: stop treating leading '#'/'*' as comments
(was deleting #include, #define, *ptr, CSS '*', multiplication)
- importance_signals: reconcile total_count to the surviving pattern sum after
decay/prune (was inflated, driving novelty toward zero over time)
- importance on_retrieved_with_context: set context in the SAME critical section
as the event push via record_retrieval() (two-lock approach raced; last_mut()
could land on a concurrently-pushed event)
core 535/0, clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All verified against real code:
- connectors: empty list_live_ids() no longer mass-tombstones the entire source
(treat empty as "cannot enumerate", like None) — was catastrophic data loss
- redmine: SSRF guard — require http(s), reject loopback/private/link-local hosts
+ localhost (escape hatch VESTIGE_ALLOW_PRIVATE_CONNECTOR_HOSTS for local tests)
- KnowledgeEdge::is_valid now honors valid_from via was_valid_at(now) (was
ignoring it; a future-dated edge read as valid). No production callers.
- emotional_memory: flashbulb counter reconciles to the FINAL is_flashbulb
decision after the importance override (was undercounting via ==0 guard)
- chains path_to_chain: fixed off-by-one — step i now uses the INCOMING edge
connections[i-1], not the outgoing connections[i]; removed the synthetic
connection hack that masked the misalignment
- merge_supersede compose_merged_content: exact normalized dedup instead of
substring containment (was silently dropping "cat" inside "cathedral")
core 535/0, clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Multi-model audit (deepseek-v4/minimax/kimi/qwen) surfaced these; verified
against the real code and fixed the confirmed ones:
- github connector: host-pin failed OPEN when api_root was unparseable/hostless
— the bearer token would ride a Link `next` url to an attacker host. Now
fail-closed: no pinned host => drop the url. (CRITICAL: SSRF / token exfil)
- GithubConfig/RedmineConfig derived Debug leaked the token/api_key into any
{:?} log line or panic message. Replaced with manual redacting Debug impls.
- cross_project priority calc used `as u32 - i` which underflows/panics (debug)
or wraps + corrupts the sort (release). Use saturating_sub.
Verified false-positive (no change): path-traversal in get_file_context — it
only inspects the path string, never reads the file.
core: 535 passed / 0 failed, clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam's locked stage pitch for a multi-pitch event + separate networking hour.
Keeps the World Cup soccer analogy (the weapon that makes anyone get "causally
upstream"), the detonation line + silence, "realizes", and "next layer of the
dev stack". Closer fits reality: "come find me after, I'll show you in 30s" (demo
is 1:1 at networking on a pre-loaded laptop, never installing on their system).
Two seams closed: "hours"->"days", "agents that touch" (plural). Added networking
playbook: pre-warm the seeded demo, demo from strength, the pilot-hook answer for
"does it work on mine?".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam's canonical 60s spoken pitch, every sentence workshopped. Two seams closed
from the assembled draft: opener "hours"->"days" (matches "three days ago"), and
the reveal names "Vestige" (was a naked "memory"). Includes the win-the-room
line list, stage rules (never throat-clear the open; dare don't demo; end on
"come prove me wrong"), and a delivery map of where to slow/pause/drop voice.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam chose to keep Vestige over Postdict: it sounds natural, it's the live + loved
product (561 stars, repo, npm packages), costs zero to keep, and "vestige = a
trace of what remains from the past" is backward-looking = on-thesis. The
category lives in the tagline ("the first memory that finds the cause, not the
lookalike"), not the name. Reverted the pitch to "I created Vestige from my tiny
Chicago apartment", kept the "our"->"my" solo-founder fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam's own tightened version, with 2 corrections: "our memory" -> "my memory"
(solo-founder first-person rule) and the name is Postdict (not Vestige). Adds
the "tiny Chicago apartment" line (underdog-relatable, investor-loved). The 2024
Nature reference verified true. This is the canonical stage script.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam is a solo founder and built Postdict alone — the pitch must say "I built,"
"I ported," "I already did it," never "we." For an early-stage solo founder that
is a strength (ships without a team = what investors bet on). Only remaining
"we" is "now we're handing debugging to AI agents" — there it means all of us as
an industry, not the company. Added delivery rule #4: "I built this" is a flex.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sam delivers this on a stage to 50-100 people — voice only, no screen. Rewrote
from a terminal-demo script into a pure ~60s spoken pitch built for the ear:
short sentences, hard stops, [pause] cues (silence = the loudest move on stage),
and one memorizable detonation line ("a root cause never looks like the bug it
creates"). Opens about the audience's pain, not the product; promises the
laptop demo afterward rather than fumbling a terminal live. Includes a 30s
hallway version and a 5s one-liner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The detonation version of the funding pitch. Deliberately REJECTS a "X companies
got hacked" hook (commodity security-FUD, needs an uncitable stat that torches
the honest+reproducible moat, and shrinks the market from "every agent in
production" to "security"). Instead opens with the WOUND every engineer has
lived — "production breaks, the cause is a change you made days ago and forgot"
— then detonates the industry's flawed axiom ("relevance ≠ resemblance; a root
cause never looks like the bug it creates"), proves it live, defends the moat
(Nature port + their architecture IS the axiom), and ends on TAM.
Uses `postdict` on screen (the new name); includes the interactive-shell alias
so it can be recorded with the new name today. Contrast re-verified end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrote the 60s funding script around Sam's stronger framing: lead with the
flawed AXIOM the whole industry shares ("relevance equals resemblance"), then
detonate it with the fact ("a root cause never looks like the bug it creates").
This names the MECHANISM of the error, not just that one exists — a diagnosis,
not a claim. Keeps the live contrast as the proof, the Nature-port + "their
architecture IS the axiom" as the moat, and "every agent that touches
production" as the market. On-screen commands re-verified end-to-end:
similarity→billing lookalike (wrong), Postdict→API_TIMEOUT cause (right).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A separate, investor-grade script (vs the viral clip): leads with the thesis
"the entire AI-memory industry is trapped in a category error — a root cause
never looks like the bug it creates", proves it live with the similarity-vs-
Postdict contrast, then closes on the moat (faithful Nature port + incumbents'
architecture IS the category error) and the market (every agent that touches
production).
Verified the exact on-screen commands end-to-end: the 3-memory scenario (cause
+ billing-500 lookalike + crash) makes the contrast devastating — similarity
search returns the billing lookalike as its #1, Postdict reaches back 3 days to
the real env-var cause. (Without the lookalike distractor the contrast collapses
— similarity would also surface the cause — so the script plants all three.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
demo/README.md: the complete self-serve demo artifact — one-command run, the
seeded scenario explained, a "build your own scenario" section, the honest
boundary (won't invent a cause; can't reach a cause that was never recorded),
the Nature citation + the "field admits this is unsolved" sources, and the
recording playbook + paste-ready caption.
Writing/testing the README surfaced a real inconsistency, now fixed:
- The CLI's failure-finder used a hardcoded content-only marker subset and
ignored tags, so a "Checkout latency spiked" memory (regression tag, no crash
word in content) was never picked as the failure. The CLI now calls the SAME
public `looks_like_failure` (content + tags, full list) the backfill tool uses
— one definition, no drift.
- Extended FAILURE_MARKERS with performance/degradation failures (spiked,
latency, degraded, slow, hang, throttled, oom, 502/503/504, flaky, ...) so the
feature backfills from perf regressions, not just hard crashes.
clippy clean; 527 core + 453 mcp tests; both the main demo and the README's
custom scenario verified end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3-model rotation audit (DeepSeek V4-Pro / Kimi K2.7 / MiniMax M3, max thinking,
each model × each of 3 sections). Claude verified every finding against code.
CONFIRMED + FIXED:
- [FTS, consensus DeepSeek+MiniMax] sanitize_fts5_or_query split on
!is_alphanumeric()+'_', but the index uses tokenize='porter ascii' which
splits on '_' and non-ASCII. So "API_TIMEOUT"/"café" became single phrases that
could NEVER match. Now splits on !is_ascii_alphanumeric() + lowercases to mirror
the tokenizer; caps token count (64) and length (64) for DoS hardening. Also
fixes the pre-existing storage.search bug (multi-word queries silently returned
nothing). 5 new tests pin it.
- [Demo honesty, consensus Kimi+DeepSeek] the contrast labeled keyword search as
"SIMILARITY SEARCH" and asserted "NONE of these is the cause" universally. Now
prints the REAL engine ("keyword (BM25)" vs "semantic (vector + BM25 hybrid)")
and claims only what's true ("ranked by RESEMBLANCE; its top hit is a lookalike").
De-hardcoded the "Service crashed:" munging to a generic label-strip.
VERIFIED FALSE POSITIVE (not changed): MiniMax "fts.id non-existent column" —
the FTS5 table is declared `fts5(id, content, tags, ...)`, the JOIN is valid.
No injection found by any model (quote-doubling + operator-stripping confirmed safe).
clippy clean; 527 core + 453 mcp tests pass; demo verified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CLI surface for Retroactive Salience Backfill so the seeded demo (and anyone who
clones it) can reproduce "memory with hindsight" from a terminal:
- `ingest --ago-days N`: backdate a memory N days (plant a dated cause/history).
- `backfill [--failure-id ID] [--manual] [--lookback-days N] [--no-promote]`:
reach backward from a failure and surface+promote the causal earlier memory,
with demo-grade colored output (↩ reached back N days, 🔗 causal join: <entity>,
similarity rank, ✅ promoted).
Verified live end-to-end on a real DB: plant a 3-day-old API_TIMEOUT env-var note
+ a semantically-similar 500 distractor + a crash, run `vestige backfill`, and it
surfaces the env-var note by the shared api_timeout entity (ignoring the similar
distractor) and promotes it. clippy clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The MCP surface for memory-with-hindsight. When a failure memory exists, the
`backfill` tool reaches backward across the real store and promotes the quiet
earlier cause that a vector search structurally cannot surface (not similar to
the failure, only causally upstream via a shared entity).
- tools/backfill.rs: builds BackfillCandidates from real KnowledgeNodes (entities
from tags + heuristic env-var/path/identifier extraction), computes real cosine
similarity from stored embeddings (to PROVE the cause ranks low on similarity),
runs the core RetroactiveBackfill, and promotes surfaced causes via
storage.promote_memory. Auto-finds the latest failure, or takes failure_id;
manual=true forces; promote=false for a dry run.
- registered + dispatched in server.rs (35 tools now); tool-list test updated.
- storage: added pub set_created_at (backdate created_at) so the demo/test can
plant a dated cause.
LIVE RECEIPT: live_backfill_surfaces_root_cause_through_storage ingests a
3-day-old API_TIMEOUT env-var note + a semantically-similar 500-error distractor
+ a crash into a REAL SQLite store, runs the backfill tool, and asserts it
surfaces + promotes the env-var note by the shared API_TIMEOUT entity (the root
cause RAG misses). clippy clean; 522 core + 453 mcp tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The headliner neuro-mechanism: when a salient FAILURE lands (bug/crash/
regression — the "aversive event"), reach BACKWARD in time and promote the
quiet earlier memory that caused it — the one a vector search structurally
cannot surface because it isn't *similar* to the failure, only causally upstream.
Faithful port of Zaki/Cai et al. 2024, Nature 637:145-155 ("Offline ensemble
co-reactivation links memories across days"), causally proven (hippocampal
silencing abolishes the linking). Ported faithfully:
- backward-only asymmetry (fear links retrospectively, never prospectively) —
also exactly correct for software: a root cause is always upstream in time.
- linking flows along the shared-entity overlap (same file/env-var/service),
NOT semantic similarity — that's the whole point (RAG already covers similarity).
- scoped to failure->backward-causal-backfill, not "all salience flows backward"
(mirrors the Cai aversive->neutral paradigm; honest about scope).
Trigger: auto-detect (high prediction-error + failure markers) OR manual override.
Promotion: boosts FSRS stability so the cause stops decaying and surfaces next time.
Receipt (4/4 tests): backfill_surfaces_the_cause_rag_misses proves it promotes a
sim=0.11 env-var note over a sim=0.82 distractor by the shared API_TIMEOUT entity;
backward-only (future memory never promoted); no shared entity => no fabricated
cause; non-salient doesn't fire; manual override works. clippy clean; 522 core
tests pass (no regressions).
Wires into existing primitives: prediction_error gate (salience), dreams/
consolidation (offline window), memory/strength (promotion). MCP tool + live
demo next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two deeper review findings (both blockers) + doc de-staling.
C2-deep: my earlier C2 made purge/delete TRACE as memory.write, but gate_writes
did `get_node(id) -> skip on None`, and purge had already DELETEd the row — so a
destructive removal still never opened a Memory PR (it was silently skipped).
The most security-critical write type couldn't be reviewed. Fix: a missing node
is now gateable for destructive decisions — gate_writes builds the WriteContext
from the decision itself (marks `forgets`, which classify_write gates), and the
PR records the removal with node.deleted=true. Proven live: purging a node opens
a PR (kind node_decayed, deleted true); test
gate_opens_pr_for_destructive_write_after_node_deleted_c2.
PRIV: gate_writes copied the FULL node.content into the PR diff + title, so a
real secret in a gated memory would leak into the memory_prs table, the
dashboard, and any exported proof bundle — defeating the point of gating
sensitive writes. Fix: the PR now stores a truncated content PREVIEW + an FNV
content HASH, and sensitive-topic/sensitive-node-type writes are fully REDACTED
("[redacted — sensitive content; review via risk signals]"). The reviewer still
sees the risk signals (why it opened) and a hash (to correlate), never the
secret. Tests gate_redacts_sensitive_content_in_pr_priv,
content_preview_redacts_sensitive_and_truncates, content_hash_is_stable. The
committed memory_pr.json + the whole proof bundle were re-captured and contain
no secret (verified by scan); the re-shot memory-prs.png shows the redaction.
DOC: REVIEW.md commit list is now git-log-based (no stale hashes); C2-deep + PRIV
added to the findings table; PROOF.md write/PR rows updated; test count -> 1007.
Gates: 1007 lib tests pass (+7 new regressions), clippy -D warnings clean,
dashboard check + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two more review findings — both real, both blockers — plus stale-doc cleanup.
C1: the B1 release used reverse_suppression(subject_id, labile_hours), which
REFUSES once the 24h active-forgetting labile window has passed. So a Memory PR
reviewed late could be marked "promoted" while its memory stayed suppressed.
Approving a quarantined write is an explicit reviewer decision and must release
the memory regardless of elapsed time. New SqliteMemoryStore::release_quarantine
fully clears the suppression (count→0, suppressed_at→NULL) with NO time-window
limit; the PR handler now uses it. Proven: a test backdates suppressed_at to
+100h, shows reverse_suppression refuses, and release_quarantine still releases.
C2: memory(action="purge"|"delete") returns `action` + nodeId but those labels
weren't in is_write_decision, so destructive removal bypassed the memory.write
trace and the PR gate. Added purge/purged/delete/deleted/forget/forgotten.
Proven live: purging a node now records a second memory.write event
({"decision":"purge"}) under the run.
Docs: REVIEW.md de-staled — removed the fixed 140b15f diff-stat / "3 commits"
prose (it moved with each fix), listed all commits, added C1/C2 to the findings
table, updated the test count.
Gates: 1002 lib tests pass (+3 new regressions), clippy -D warnings clean,
dashboard check + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A full multi-agent review found 7 real issues (4 blockers). All fixed + tested.
B1 (blocker): Promoting a Memory PR did not release the quarantined memory —
the UI said "promoted" while the memory stayed suppressed/out of retrieval.
act_on_memory_pr now calls reverse_suppression(subject_id) on accept actions;
MemoryPrAction::releases_memory() encodes the rule (promote/merge/supersede
release; forget/quarantine keep it held). Proven live: PR response
subjectReleased:true, SQLite suppression_count 0.
B2 (blocker): memory promote/demote (returns `action`, not `decision`) and
codebase remember_* writes bypassed the write-trace + PR gate. extract_writes
now reads `action` too, filtered by is_write_decision (reads like get/state
excluded); is_write_tool includes `codebase`.
B3 (blocker): receipt ids collided within a run (r_<date>_<runId> +
INSERT OR REPLACE overwrote earlier receipts). IDs are now
r_<date>_<runId8>_<unique6>; build() mints the suffix, build_with_unique()
keeps tests deterministic.
B4 (blocker): proof bundle was assembled from two runs (trace.json=run_proof,
websocket-events.jsonl=run_proof2). Re-captured the whole bundle from a single
run — trace, websocket, receipt, and memory_pr all carry run_proof now.
B5: Black Box receipts panel showed global latest, not the selected run.
Added list_receipts_for_run + /api/receipts?run= ; the page uses listForRun.
B6: SENSITIVE_TOPICS substring matching false-fired (tokenizer->token,
author->auth, secretary->secret). Switched to word-boundary matching; real
phrasings (auth token, security vulnerability, api key) still gate.
B7: set_review_mode now writes atomically (temp+rename via write_atomic);
export_trace sanitizes run_id in the Content-Disposition filename; memory-prs
static routes declared before the dynamic /{id} route.
Withdrawn: the /mode-vs-/{id} route order is NOT a functional bug (axum 0.8 /
matchit prioritizes static segments) — reordered for clarity only.
Gates: 999 lib tests pass (+9 new regressions), clippy -D warnings clean,
dashboard check + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Freeze feature work and package the Black Box proof bundle for full review
before anything else lands (no quarantine-constellation work has started).
REVIEW.md is the single entry point: frozen public claim, the per-producer
caveat ledger (real / caveat — sanhedrin.veto stays an honest off-by-default
caveat; dream.patch is REAL but not live-by-default), and the exact command
receipts (run live at 2026-06-22T22:57:59Z, toolchain pinned) for every
validation status credited — nothing claimed from memory.
The bundle (blackbox-proof-2026-06-22/) already contains: status/trace/receipt/
memory_pr JSON, two live WebSocket captures (one with the dream.patch
TraceEvent), six screenshots (Black Box, Receipts/ReceiptCard, Memory PRs,
Graph, dream Black Box, dream producers panel), and PROOF.md. Every credited
artifact was verified to contain real content.
Review surface: 27 source files, +5830/-18 across commits 80c823a, b89beee,
140b15f (build artifacts + proof bundle excluded).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bounded follow-up (tight acceptance criteria, no scope expansion): flip the
dream.patch producer from "quiet because no dream ran" to a recorded live event.
The dream tool's `insights` array carries no per-item id, so the recorder
extracted zero proposals and dream.patch never fired even on a real dream.
Fix: derive a stable proposal id from each insight's REAL content (its
insight_type + the source memories it consolidated). The dream genuinely ran;
this just gives each real proposal a deterministic handle. Unit-tested against
the exact dream output shape.
Proven end to end (run_dream_proof, 6 memories consolidated):
- one dream.patch event: dream:RecurringPattern:5d941c7f+a41aca72+...
- SQLite + /api/traces/:runId: dream-trace.json (14 events, last is dream.patch)
- WebSocket: dream-websocket-events.jsonl (the dream.patch TraceEvent)
- dashboard: screenshots/dream-producers.png — the row flips to "fired this run"
PROOF.md updated: dream.patch moves from CAVEAT to REAL (still not live by
default — it fires only when a dream actually runs, and the UI says so).
sanhedrin.veto remains an honest CAVEAT (optional hook, off by default).
Gates: 957 lib tests pass, clippy -D warnings clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the receipt chain impossible to doubt. Freeze the claim surface, prove
every hop, and turn the two off-by-default producers into explicit UI states.
Frozen public claim: "Vestige records real MCP memory activity into a
replayable local trace, with receipts and reviewable risky writes." We do NOT
claim Sanhedrin vetoes or dream patches are live by default.
Regression — full-spine test (server.rs): one runId must cross, byte-identical,
MCP output -> SQLite trace -> WebSocket event -> API response shape ->
MCP resource. Fails if any hop drops or rewrites the id.
Honest UI states (Black Box "Event producers" panel):
- sanhedrin.veto -> "No veto producer connected (optional Sanhedrin hook, off
by default)" instead of empty mystery.
- dream.patch -> "No dream run in this trace" unless a dream actually ran.
- contradiction.detected -> "no contradiction in this run" when none fired.
Quarantine review (not pre-write blocking): risky writes are committed then
suppressed — audit history preserved, retrieval influence suspended until
reviewed. Reworded the server notice + UI copy to say exactly that.
Receipts UI gap closed: ReceiptCard is now mounted on the Black Box page
(retrieved/suppressed/trust-floor, activation path, "Open receipt in Cinema").
Proof pack (blackbox-proof-2026-06-22/): status.json, trace.json (the
.vestige-trace.json export), receipt.json, memory_pr.json (promoted via
UI->API->SQLite), websocket-events.jsonl (live TraceEvent x6 + PR opened/
decided), screenshots (Black Box, Receipts, Memory PRs, Graph), and PROOF.md
with real/caveat/stub per feature.
Gates: 988 lib tests pass, clippy -D warnings clean, dashboard check + build
clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The loop fired requestAnimationFrame immediately and never awaited render(); with
150k particles + compute + bloom a frame can exceed 16ms, so rAF callbacks queued
faster than the GPU could drain them and the camera/parallax visibly lagged behind
the cursor. Now AWAIT sb.render(dt) before scheduling the next frame → the loop is
capped to real GPU throughput, every frame reflects the latest pointer position,
no backlog. Also snappier active-steer damping (lam 3.5→9, ~110ms converge) so
input feels immediate; idle glide-home unchanged. renderFailures resets on success.
Plus docs/MEMORY_CINEMA.md — complete feature reference for the cinema engine.
Gate: svelte-check 0/0, 937 tests, verified live (parallax tracks cursor, no lag).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
near-fade × fog × DOF × seam-fade each multiply a <1 factor; together they were
crushing the figure dark. Raised the fog floor (0.18→0.45) and the color/emissive
glow bases so the stacked attenuation lands in a vivid range. Verified live.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step 5 — VELOCITY-STRETCH FLYTHROUGH: sandbox derives camera velocity per frame
(one Vector3, zero compute) and pushes view-space apparent velocity to the storm;
flythrough relaxes the camera clamp floor (lerp 30→6) so the camera plunges
inside the shell. Storm stretches each sprite along screen-space velocity via
rotationNode + scaleNode (clamped streak), separate output graph (no extra
positionView read). Defaults 0 → no-op until wired.
Step 6 — INTERACTIVE PARALLAX: pointer orbits / scroll + pinch zoom the camera
with frame-rate-independent damping, composed onto the director's base pose in
loop() (after director.update, before render); idle >2.5s eases back to 0 so it's
a toy when touched and a film when left alone. sandbox.render re-clamps so the
user can't break framing. Per-beat flythrough strength wired from shot.tension;
dream mode flies through at 0.6. Fully gated off under reduced-motion (no
listeners, flythrough 0).
The 4-feature immersion stack (infinite zoom + flythrough + parallax + DOF/fog)
now composes. Gate: svelte-check 0/0, 937 tests, build green, verified live (all
4 compose, no white-out, no recursion, parallax responds without breaking framing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cloud now dives inward FOREVER, seamlessly. Two layers ride offset phases of
fract(uTime/T): the outer grows pow(λ, phase) toward the camera then snaps back
(invisible — inner@1/λ == outer@1); the inner (half-period offset) grows promoted
by λ to become the next outer shell, while a fresh inner spawns inside. λ=1.923
(=1/0.52 inner scale) makes the snap mathematically exact. A sin(phase·π) seam
cross-fade in rimFactor makes each layer fully transparent at its snap → ZERO pop.
Particle-space (not a camera dolly) so it can't clip or fight the camera clamp.
Rack-focus tracks the descent. uZoomOn gates it: Act II+ and dream mode dive;
beats 0/1 + reduced-motion stay still. Verified: seamless loop, no white-out, no
recursion, 937 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Off-focus particles dim (read as bokeh defocus under the bloom) with a breathing
rack-focus. Folded into the single depthFade depth read — NOT sprite scaleNode,
which collapsed the sprites to invisible and collides with the upcoming streak.
Subtle (0.3) so it adds cinematic depth without darkening the figure.
Gate: svelte-check 0/0, 937 tests, verified live (depth grading reads, no recursion).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Distant particles dim toward the void with view depth (exp falloff) → real 3D
atmospheric recession, not a flat sprite cloud. Combined with the near-fade into
a SINGLE depthFade Fn — critical three@0.172 TSL constraint discovered: reading
positionView from a second Fn feeding the same material output triggers a cyclic
stack-overflow in the node type-resolver (getNodeType). One depth read, one Fn.
Gate: svelte-check 0/0, 937 tests, verified live (fog depth reads, no recursion).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Particles dissolve as they approach the camera (view-space -positionView.z,
smoothstep over [near, near+band]) so the upcoming flythrough never additive-pops
a sprite in your face. Folded into color + emissive so the bloom fades too.
Invisible at the default far camera. positionView confirmed working in the
SpriteNodeMaterial color node.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SpriteNodeMaterial.setupPositionView already rebuilds the billboard quad from
positionGeometry — the prior .add(positionLocal) double-counted it (harmless at
0.1 size). Bare center is required for the upcoming velocity-stretch streak
(scaleNode/rotationNode will drive the quad). Verified renders identically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The nested 3D-within-3D figure now collides with its shell in OPPOSING color
universes, not a shared rainbow. Each layer is painted from a hard duotone:
the outer shell from one world (ice / acid / gold / mint / electric-blue), the
inner figure from its enemy (fire / blood / violet / crimson / gold). A new
uClash uniform cycles the pair every beat (and randomizes per dream figure), so
it's a fresh ice-vs-fire / acid-vs-blood collision each time — the kind of
contrast that stops a scroll.
To make the clash READ instead of washing white: inner glow floor dropped hard
(dense small-radius overlap was blowing to white and killing the color), inner
figure scaled up to 0.52 (spread → less overlap), the color blast capped at 0.6
mix so the duotone shows through even during a detonation, and Act II/III
ignition lowered 8.0→4.5 so beats no longer flash the clash to white.
Gate: svelte-check 0/0, 937/937 tests pass, build green, verified live (gold
shell + violet core clash reads clearly, no white-out, beats 0/1 calm).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three upgrades to Memory Cinema:
1. IMPOSSIBLE-GEOMETRY FORM PACK — replaced the stringy ribbon dream worlds with
brand-new signature skins nobody ships as a living particle figure:
- world 8 → Calabi–Yau quintic cross-section (6D string-theory manifold,
Hanson 4D→3D projection; α rotates it through the 4th dimension)
- world 9 → Boy's surface (Bryant–Kusner minimal immersion of RP²)
- world 10 → Aizawa attractor shell (breathing strange-attractor skin)
- world 11 → Gyroid↔Schwarz-D Bonnet morph (triply-periodic minimal surface)
The (u,v) MANIFOLD GRID basis is the key fix: particles map over a tensor grid
so neighbors share edges → reads as a sculpted SKIN, not spaghetti. Plus a
facing-ratio Fresnel rim so forms read as lit solids. Inline complex-math +
hyperbolics (sinh/cosh not in three@0.172). atan2→atan (deprecation).
2. 3D-WITHIN-3D NESTING — ~34% of particles form a SECOND, smaller, counter-
rotating figure (a different world, ~45% scale, complementary hue) at the core
of the outer shell. A figure inside a figure — fills the formerly-blank-bright
center with intentional structure and depth.
3. DEMO-CAPTURE MODE — press H in the cinema overlay to hide all UI chrome
(top bar + captions) for clean recording; a faint restore hint remains.
Overlay z-index raised + body.cinema-open hides the graph page's stats pill so
nothing bleeds through.
Gate: svelte-check 0/0, 937/937 tests pass, build green, verified live (Calabi–Yau
+ nested core render, forms cycle, no errors, beats 0/1 calm).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 7-beat tour no longer freezes on the last figure. When it ends, Memory
Cinema drops into an infinite generative loop: every ~5.5s it morphs into a
fresh RANDOM procedural figure and detonates a color blast — each crazier than
the last.
Five new procedural worlds (7..11), parameterized by a per-figure uMorphSeed +
a uChaos ramp so the same index never looks the same twice:
7 supershape (3D superformula) 8 torus knot (random p,q winding)
9 warped lissajous lattice 10 helix storm
11 quantum foam (curl-warped chaos — max wild)
storm.dreamBeat() picks a random world, reseeds it, ramps chaos, and fires a
moderate-ignition blast (kept below the tour's 8.0 so dense random figures don't
wash white). Surfaced via sandbox.dreamBeat(); MemoryCinema starts a dream timer
on director onComplete, shows "∞ Dreaming", and tears it down on close/replay.
Honors reduced-motion (no dream loop) and the render-fail fallback.
Gate: svelte-check 0/0, 937/937 tests pass, build green, verified live (reaches
dream mode, generates distinct figures — supershapes, torus knots — cycling
forever, no white-out, no errors).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Standing default for all Vestige work, at the absolute top of CLAUDE.md so it
loads first every session: assume maximum ambition, scour before settling, no
hedging, show proof, protect what's flawless and detonate what isn't.
Origin: the overnight session that turned the dashboard + Memory Cinema into a
category-of-one particle journey. Make that depth the default, not the exception.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Memory Cinema is now a choreographed 7-act journey. One 150k-particle pool,
one compute kernel; a uWorld state machine select()s which home-target + forces
are live, and uBlend crossfades world→world over ~1s. The particles never swap
— only the forces on them — which IS the journey.
The seven worlds (beats map 1:1, beatIndex % 7):
0 nebula mist (curl-noise flow) 1 orbital anchor (cross-product spin)
2 strange attractor (Thomas) 3 detonation void (staggered shockwave)
4 crystal lattice (voxel snap) 5 fluid galaxy (curl + tangential swirl)
6 phyllotaxis bloom (Vogel sunflower, golden angle)
The signature COLOR BLAST: a long-lived uBlast envelope (~2.8s, decoupled from
the fast physics burst so the color OUTLIVES the shockwave) drives an outward
SPECTRAL DISPERSION wave — concentric rainbow rings expanding through the radius
over uBlastTime (real prism order, red lags / blue leads), with a warm blackbody
ember core underneath. Spectrum dominates so the detonation reads as RAINBOW,
not a white plasma flash.
Plus per-world cosine palettes (IQ) so each world is a distinct PLACE. All the
white-out guardrails preserved + extended: rim-gated blast, capped kelvin/gain,
emissive blast held below the color path. Beats 0/1 stay calm (low uBlast),
Acts II/III blaze.
Frontier techniques sourced from a parallel web-research + design workflow;
verified against the installed three@0.172 TSL build. Retires the old uShape
5-form gallery.
Gate: svelte-check 0/0, 937/937 tests pass, build green, verified live (7
distinct worlds, spectral blast, no white-out, calm opening).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Memory Cinema storm color/shape overhaul (the crown-jewel pillar):
- Fix the white-out root cause: emissiveNode was NEVER set, so the selective
MRT bloom had no color to bloom and washed the frame white. Route the shared
iridescent rainbow to BOTH colorNode and emissiveNode.
- Rim glow (fresnel-style): bright glowing edges, dim readable center — the
shareable luminous-shell / hollow-torus look.
- Morphing geometry: the home target cycles sphere → torus → galaxy spiral →
cube lattice → wave sheet, drifting continuously and snapping per beat.
- Hyper-saturated full-spectrum palette (per-particle phase + radial shells +
spatial bands + time) so the whole rainbow is present at once.
- Spread the initial spawn across a wide hollow shell (was a tiny dense ball
that boot-flashed white).
- Act/beat-aware brightness: beats 0/1 fade in soft, Act I held calm, Acts
II/III blaze at full. No white-out regressions.
Gate: svelte-check 0/0, 937/937 tests pass (cinema auteur/pathfinder green),
verified live in browser.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the dashboard feel alive every second, with clear controls, for the
July 14 HN relaunch. Memory Cinema is left fully untouched (zero changes to
MemoryCinema.svelte / graph/cinema/*; its tests still pass).
Foundation (lifts every page):
- Icon.svelte: inline-SVG icon system, zero runtime dep. A UNIQUE semantic
silhouette per nav item — kills the old duplicated Unicode glyphs (◎◈◉◷
were each reused across multiple items). Wired into sidebar, mobile nav,
command palette, logo.
- Dropdown.svelte: accessible, keyboard-nav, type-ahead, animated select
replacement with color dots / badges. Replaces dead native <select>s.
- AnimatedNumber.svelte: rAF count-up/tween, reduced-motion safe.
- PageHeader.svelte: shared masthead (drawn route icon + aurora title).
- actions/reveal.ts + actions/interactions.ts: scroll-reveal, magnetic,
tilt(+glare), spotlight — all no-op under reduced-motion.
- app.css "alive layer": @property animatable gradients, conic live-border,
breathe/ping, shimmer skeletons, @starting-style entry, aurora text, lift.
Per-page: every route (graph non-cinema controls, reasoning, memories,
timeline, feed, explore, activation, dreams, schedule, importance,
duplicates, contradictions, patterns, intentions, stats, settings) now uses
PageHeader, real Icons, count-ups, staggered reveals, shimmer loaders,
spotlight cards, and warm empty states. Native selects and button-row
filters became clear Dropdowns where it improves clarity.
Gates: svelte-check 0 errors/0 warnings, 937/937 tests pass, build green,
verified live in the browser preview.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per direction: keep the mind-blowing explosion + pixelation moments, ditch the
thin ribbon swirls. Complete physics rewrite:
- removed orbital/stream/Rössler modes (the swirls + the off-center drift source)
- each particle has a deterministic HOME on a volumetric shell around ORIGIN
(centroid anchored — can never drift off-frame again)
- uBurst detonation cycle: every beat blows particles radially out (explosion),
then a home-spring crystallizes them back (reform); contradictions detonate hardest
- PIXELATION: positions snap to a 3D grid that's fine when reformed, dissolved
during the burst — the crystalline voxel look
- hard velocity + radius clamps so it can never fly off or blow up
937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Storm was a small ring leaving the canvas empty, and the core blew to white.
- FILL: sandbox fitRadius margin 0.40 -> 0.82 so the storm fills most of the
frame; particles now target their OWN radius across 0.12r..0.92r (filled
volumetric ORB, not a thin ring).
- COLOR: brightness was x(ignition*2.4+0.6) = up to x19.8, which + additive
blending across 150k sprites clipped every channel to white. Clamp the glow
low (0.45 floor, ~1.15 ceil) so the RAINBOW shows as pure spectral color;
smaller quads (0.18 -> 0.1) keep particles crisp instead of overlapping to
mush; gentler bloom (strength 1.1->0.6, threshold 0->0.35) accents cores
rather than washing the cloud. 937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The rainbow storm looked next-dimensional but still clipped the edges — the
additive bloom halo extends each particle's glow well past its geometric radius,
so the visible cloud was bigger than the contain sphere.
- spawn radius 15 -> 8 (particles start inside the shell, no asymmetric inward yank)
- sandbox fitRadius margin 0.55 -> 0.40 (leaves room for the bloom halo)
- camera band tightened + pushed farther (30-44) so the contained cloud sits
small + centered; director standoff clamped into that band in centerOnOrigin
mode so the camera never fights the per-frame clamp (the off-center jump).
937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes the runaway ring (screenshot showed an expanding ellipse clipping the
frame): orbital mode added tangential velocity with nothing pulling particles to
a target radius, so they spiraled outward. Now a two-sided RADIAL SPRING pulls
every particle toward an in-frame shell (containRadius*0.62) with a per-particle
band so the cloud is a contained breathing sphere, not an ever-growing ring.
Tighter velocity clamp + boundary snap as belt-and-suspenders.
Color: replaced the flat 3-color tint with a living iridescent RAINBOW — hue
drifts by per-particle phase + radius + time + a global rotating hue shift
(fract/abs hexagon palette). Dramatic beats blend their mode color over the
rainbow (crimson at contradictions, gold at surprises) via uModeTintAmt; calm
beats stay mostly rainbow. 937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause: layoutPositions grew per beat (radius 22 + i*6), so each beat sat
farther out; the camera + storm marched off into space as the tour progressed.
Fix (centered-by-construction):
- layoutPositions: tight BOUNDED golden-angle shell (SHELL_RADIUS 14), no growth.
- sandbox: storm pinned to the WORLD ORIGIN permanently; camera hard-clamped to
an 18-46 unit distance band and always lookAt(origin); containment sphere
sized to the FOV at origin. A runaway move is corrected every frame.
- director: new centerOnOrigin mode (enabled when WebGPU active) — frames/orbits
the origin instead of flying to scattered nodes; variety from angle/standoff.
No path remains for the subject to leave frame. 937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
director.ts: optional shots:ResolvedShot[] in DirectorOptions; per-beat
flight/dwell timing; framePosition now reads move (push_in/pull_back/crane
scale standoff) + angle (low=look-up, high=look-down) + standoff; orbit shots
revolve the camera during dwell; Dutch roll via camera.up; hard/match cuts
snap (editorial cut). With NO shots the camera is byte-identical to before
(all values fall back to the existing constants + easeInOutCubic lerp).
MemoryCinema.svelte: build computeSignals + planShotsDeterministic + resolveShots
on launch, pass shots to the director; onBeat drives storm mode + director's
note + Act + tension from the shot. New UI: pre-roll DIRECTOR'S PLAN card
(logline naming real memories), per-beat 'why this shot' note, Act I/II/III
badge, tension-tinted progress bar, Auteur source badge.
The deterministic auteur ships the hero film with zero LLM. 937 tests + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Particles (esp. the unbounded Rössler chaos mode) could fly off-screen. Add a
camera-frame-sized spherical containment field: spring pull-back past the
radius, hard velocity clamp, and a snap-to-shell safety net so no particle can
escape. The sandbox sizes the radius from camera distance + vfov each frame so
the storm reframes as the camera flies. Verified: check + build green.