Skip to content

Add user wiki feature#4

Merged
sysread merged 1 commit intomainfrom
claude/add-user-wiki-feature-Aw8vC
May 9, 2026
Merged

Add user wiki feature#4
sysread merged 1 commit intomainfrom
claude/add-user-wiki-feature-Aw8vC

Conversation

@sysread
Copy link
Copy Markdown
Owner

@sysread sysread commented May 9, 2026

=head1 SYNOPSIS

A new flat encyclopedia about the user, peer to memory and journal.
Articles are titled, single-level (no nesting), and never auto-injected
into the chat - the assistant reaches them only through the always-on
wiki_search tool.

=head1 PURPOSE

Currently the app maintains three forms of long-running user knowledge:

  • Memories - atomic labelled facts surfaced inline by opening recall.
  • Journal - dated reflective prose; today's automatic entry is primed
    in the system prompt on the first turn.
  • Chats - the raw conversations themselves.

There is no surface for topical, encyclopedic knowledge that spans
many conversations - "what is the recipe project", "who is Maya", "where
does the Lisbon trip stand". This is the gap the wiki fills.

=head1 DESCRIPTION

=head2 Layer 1: how the existing subsystems behave

The journal worker acquires a cross-tab Web Lock
(nak:journal-worker), claims a thread via
claim_next_thread_for_journal (gated on a same-day cooldown using
threads.updated_at), runs JournalAgent.run (single Venice call,
response_format=json_object, no tools), and lands the entry plus the
pointer-advance through the atomic upsert_journal_entry_and_mark_thread
RPC. The reflection worker uses the same scaffold but runs
runHeadlessToolLoop against memoryToolbox; tool calls are the side
effects, the model's final text is discarded. The generic embedding
worker
round-robins polymorphic source adapters (memories, threads,
samskara substrate, journal entries), each adapter shaping a
(claim, save) pair against per-table RPCs. The chat-side toolbox
has every read tool registered always-on so the model never has to flip
a toolbox to search; writes still gate.

=head2 Layer 2: what this PR adds, parallel to layer 1

The wiki worker acquires nak:wiki-worker, claims via
claim_next_thread_for_wiki, runs WikiAgent.run (which uses
runHeadlessToolLoop against the new wikiToolbox), and lands the
pointer-advance through mark_thread_wiki_processed_if_claimed -
unconditionally on done. Two deliberate divergences from the
journal claim RPC:

  • Newest-message timestamp, not threads.updated_at. The
    eligibility lateral reads messages.created_at directly via a
    second lateral so the gate stays stable against future bumps to
    threads.updated_at from unrelated writes.
  • Strict-yesterday gate:
    (newest.created_at at time zone tz)::date < (now() at time zone tz)::date.
    Effect: chat Monday -> eligible Tuesday; resume Wednesday -> the
    newest msg lands on Wednesday and the inequality fails again until
    Thursday.

The wiki embedding adapter is a clone of the memories adapter (input
shape ${title}\n\n${content}, 16k char cap, same claim/save pair). The
generic worker picks it up alongside the existing four sources.

wiki_search is registered in the always-on toolbox so every chat
turn can call it without a toolbox toggle. wiki_create/wiki_update/
wiki_delete live only in the agent-only wikiToolbox -
the main LLM has no path to write to the wiki.

The per-article manual flow (WikiAgent.updateOne) runs synchronously
on the main thread: one Venice call, response_format=json_object, no
tool loop. UI mirrors Journal.svelte's regenerate flow with
Accept / Try again / Cancel preview. The prompt explicitly forbids
discarding existing facts unless the user asks for them removed, and
forbids fabrication.

unique(user_id, title) lets wiki_create raise on collision; the tool
rephrases the unique-violation as actionable text the autonomous agent
reads and falls through to wiki_search + wiki_update.

A new WIKI_BLOCK in src/lib/chat-prompt.ts tells the main LLM the
wiki exists and to call wiki_search whenever the user references
something topical or factual about themselves, a project, a person, or
a place. Articles are explicitly never auto-injected.

A new wikiAutomaticEnabled setting (defaulting on, mirroring
journalAutomaticEnabled) gates the autonomous worker. Manual edits and
the per-article "ask agent to update" button keep working when off. The
wiki shares the journal's tz preference - one user-tz setting covers
both background subsystems.

=head2 Layer 3: how this resolves PURPOSE

The user (and a background agent) now have a fourth long-running surface
sized for "what is X" rather than atomic facts or dated reflections. The
assistant can ground "what's the recipe project about" answers without
guessing. Articles accrete coherently because the autonomous agent
search-then-mutates and is told to preserve facts. The user can still
correct, rewrite, or delete by hand, or hand the agent explicit
instructions for a single article.

Notes for AI reviewers:

  • The wiki agent is intentionally allowed wiki_delete (unlike
    memoryToolbox which uses memory_invalidate). The prompt
    constrains it to consolidation only - "an article you just updated
    now strictly subsumes another one". Never delete on the basis of
    "the user said something different today" alone.
  • Pointer-advance is unconditional on done. Even a no-op cycle
    advances the pointer; otherwise every cycle would re-process the
    same "no topic warranted writing" conversation.
  • embedding_claim_expires (no _at) on wiki_articles is intentional -
    matches the existing memories / journal_entries convention. The
    thread-side wiki_claim_expires_at DOES have the suffix, matching
    the journal claim columns.
  • The autonomous prompt's "preserve every existing fact unless the
    conversation explicitly contradicts it" line is load-bearing. A
    reviewer suggesting "let the agent rewrite for clarity" would
    silently churn the wiki.

https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH


Generated by Claude Code

A new flat encyclopedia about the user, peer to memory and journal.
Articles are titled, single-level, and unique per (user_id, title).
The user authors via the new Wiki drawer tab; an autonomous background
agent maintains articles by reading conversations a day after they
settle. The main LLM reaches articles only through the always-on
wiki_search tool - articles are never auto-injected.

Schema: wiki_articles + three threads columns
(last_wiki_processed_msg_id, wiki_claim_holder, wiki_claim_expires_at)
+ five RPCs (claim_next_thread_for_wiki,
mark_thread_wiki_processed_if_claimed, claim_next_pending_wiki_article,
save_wiki_article_embedding_if_claimed, search_wiki_articles_by_embedding).
The eligibility predicate is the deliberate divergence from journal:
newest message must land on a calendar day strictly before today in
the user's tz, and reads the timestamp from messages.created_at via a
second lateral rather than threads.updated_at.

Autonomous agent clones the journal manager/worker/loop scaffolding
but uses reflection's tool-call shape internally - runHeadlessToolLoop
against a new wikiToolbox (wiki_search, wiki_create, wiki_update,
wiki_delete). Pinned to deepseek-v4-flash via AGENT_MODELS.wiki.
Pointer-advance is unconditional on done so a no-op cycle still
consumes the conversation.

Per-article "ask agent to update" runs synchronously on the main
thread via WikiAgent.updateOne - one Venice call,
response_format=json_object, no tool loop. UI mirrors Journal.svelte's
regenerate flow with Accept / Try again / Cancel preview. The prompt
explicitly forbids discarding existing facts unless the user asks
for them to be removed, and forbids fabrication.

Settings adds a wikiAutomaticEnabled toggle (mirroring journal). The
wiki shares the journal's tz preference. WIKI_BLOCK in chat-prompt.ts
tells the main LLM the wiki exists and to use wiki_search whenever
the user references something topical or factual about themselves.

Docs: docs/user/wiki.md and docs/dev/wiki.md, both linked from their
respective README index pages. Tests updated for the new always-on
tool and the new routed key.

https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH
@sysread sysread merged commit 9c6103a into main May 9, 2026
1 check passed
@sysread sysread deleted the claude/add-user-wiki-feature-Aw8vC branch May 9, 2026 18:30
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.

2 participants