Skip to content

feat(tui): read-only Next Memory page over daemon familiar-memory reads (#415) - #436

Open
BunsDev wants to merge 6 commits into
feat/414-research-viewfrom
feat/415-memory-inspector
Open

BunsDev wants to merge 6 commits into
feat/414-research-viewfrom
feat/415-memory-inspector

Conversation

@BunsDev

@BunsDev BunsDev commented Nov 2, 2026

Copy link
Copy Markdown
Member

Context

Implementation

  • Adapter (crates/core/src/coven_daemon.rs): DaemonClient::memory_list and memory_overview.
    • The relative path is never parsed.
    • The title and excerpt are dropped during parsing unless the producer gave a classification and reveal_required: false. A null, empty or unknown classification, or a null or true reveal, gives Withheld, so no later layer can show, log or export the text.
    • A row id must be a UUID. Older daemons derived the id from the file name, which would show the title being withheld. That response shape returns the new DaemonError::UnsupportedShape and reads as update required.
    • In the overview, missing counts stay unknown and missing capabilities read as absent.
  • Snapshot (crates/tui/src/application/memory.rs): Offline, UpdateRequired (a 404 on the list route, or an older row shape), Failed or Loaded. A failure shows only the HTTP status and error code, never a daemon message, parse error or transport text. The overview is skipped when the list read fails.
  • Inspector: a Memory page between Research and Views.
    • Its header always shows "read only: daemon reads, no fallback" and "unavailable: reveal, selection ([TUI v2][P1] T8: add a private, read-only familiar-memory inspector and explicit context selection #415)".
    • Each row shows:
      • [familiar memory] <familiar id> (unverified)
      • the preview, or "withheld: not cleared by the producer"
      • the opaque reference
      • update time and source
      • the producer's check label, marked "not attested"
    • The page also names /memory as Local instructions, a separate engine-local surface.
    • The Views entry for Memory now reads "read only".
  • Poller (crates/cli/src/main.rs): reads the daemon off the UI thread, only while the Next Memory page is open.
    • It re-reads at most every 10 s, and waits at least 2 s between reads even across closing and reopening the page.
    • Closing the page drops the snapshot and any read in flight, so a late answer is discarded. Nothing is cached to disk or sent to a model.
  • User-visible behavior: Next only. Alt+I, then Tab four times. Classic is unchanged: Classic never reads memory.
  • Compatibility notes: one new DaemonError variant and two new client methods. There are no exhaustive matches on DaemonError outside the client. No storage, protocol or command changes.

Verification

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace: 2922 passed, 0 failed, 53 ignored. The extra ignored test is the opt-in live-daemon test below.
  • tmux suite with a disposable HOME and SHELL=/bin/bash: 84/84. That includes the new case 10, which has memory files with a canary on disk in COVEN_HOME and no daemon running. The page says offline, and the canary and file name never appear. Case 10 is 9/9 here and fails 3 checks on the feat(tui): read-only Next Research page over session search and fetch (#414) #434 base binary.
  • Real daemon, synthetic memory: coven v0.4.10 run on a disposable COVEN_HOME holding two synthetic entries, one carrying a canary and one carrying prompt-injection text. The real ~/.coven daemon was not used. The daemon was stopped and its home removed afterwards.
    • Raw GET /api/v1/memory returned the canary in the excerpt with privacy_classification: null and reveal_required: null. This is exactly the case that must be withheld.
    • COVEN_MEMORY_LIVE_TEST=1 COVEN_HOME=<disposable> cargo test -p claurst-core --lib live_daemon -- --ignored reported: rows=2, withheld=2, entries=2, familiars=2, detail=true, mutations=false.
    • In a PTY at 120x40 against the same daemon, the page showed both familiars, previews withheld and the overview counts. The canary and the injection text appeared 0 times, and closing the page removed the rows.
  • n/a python3 scripts/check-secrets.py: this repository has no such script. The diff adds no credentials or private paths; fixtures are synthetic canaries.
  • Additional manual checks: every new test was seen failing first, against a stub or the previous behaviour. Coverage:
    • adapter: 9 tests, including fake-socket GET and an old daemon's 404
    • snapshot: 4 tests
    • render at 100x50, 120x50, 100x30, 40x12, 20x6 and 1x1: 8 tests
    • poller: 4 tests

Independent review

A fresh reviewer read 85112b2. Verdict: changes requested, for one high-severity leak. Resolved in 45aae3a:

Finding Resolution
High: daemons before v1 use {familiar}-{file stem} as the id, so "Reference" showed the withheld title The id must be a UUID; otherwise the read reports update required, with a canary test
Daemon messages and serde text could reach the screen The reason is the HTTP status plus code, or fixed text, with a canary test
A structured classification failed the whole page Untagged enum; an unknown shape is withheld
Reopening the page spawned a read every time 2 s minimum gap across reopen, with a test
Overview read even after the list failed Skipped
The live test could hit ~/.coven and print rows Needs explicit opt-in, refuses ~/.coven, prints counts only
Vacuous no-submit and no-respawn tests A typed draft is kept and not submitted; a third poll proves no respawn
Nits Raw structs moved out of the Tests banner; Views uses Evidence; an empty title reads "untitled"

Accepted and documented:

  • Withheld title and excerpt bytes are still deserialized before they are dropped. They are never kept, shown or logged, and no in-memory wiping is claimed.
  • Keys the inspector does not bind, such as Backspace, still reach the composer by [TUI v2][P0] T3: deliver the responsive transcript/composer shell and unified command navigation #410's design. They edit the draft and never submit it.
  • Text that is deliberately rendered can remain in terminal scrollback or tmux capture. Closing the page does not erase those copies.

Unrelated: one workspace run on #434 after merging #411 showed a single failure that did not reproduce in three reruns.

Risk and Rollback

  • Risk level: low. The change is read only, Next only, and active only while the Memory page is open. The privacy rule fails closed in the adapter.
  • Rollback plan: revert this PR. No data or format migrates.

…ds (#415)

- Core: typed DaemonClient reads for GET /api/v1/memory and /overview. The
  relative path is never parsed; title and excerpt are dropped unless the
  producer classified the entry and said no reveal is required. Missing
  counts stay unknown and missing capabilities read as absent.
- TUI: a Memory inspector page between Research and Views. Rows show the
  familiar id as given and unverified, the opaque reference, source and the
  producer's check label marked not attested. Offline, update-required and
  failed reads say so with no disk fallback. /memory is named as Local
  instructions. No reveal, selection or mutation.
- CLI: the daemon is read off the UI thread at most every 10 s, only while
  the Next Memory page is open; closing it drops the snapshot and any late
  answer.
- Tests: adapter canaries and fake-socket reads, snapshot classification,
  render tests, poller tests, tmux case 10 (offline, canary files on disk
  never shown), and an opt-in live-daemon test.
…ors (#415)

- A memory row id must be a UUID. Older daemons derived it from the file
  name, which would show the title the preview withholds; that shape now
  reads as update required.
- A structured privacy classification is withheld, not a parse failure.
- Failed reads show only the HTTP status and error code, never a daemon
  message, parse error or transport text; the overview is skipped when the
  list read fails.
- Reopening the page waits at least 2 s between daemon reads, so paging
  back and forth cannot pile up threads.
- The live-daemon test needs COVEN_MEMORY_LIVE_TEST=1, refuses ~/.coven and
  prints only counts.
- Stronger tests: a draft is not submitted by Memory-page keys, and a dead
  read is not respawned at once.
@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Nov 3, 2026 5:14am UTC

Request Review

This branch was successfully deployed

1 active deployment
Preview — 368c81f7 Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant