From 341341459757baf97bf538485ebba013a90ef7bb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 12:46:40 +0000 Subject: [PATCH 1/2] CLAUDE.md: drop the delete_ref 403 explanation, just leave remote branches alone The note told future sessions not to try --delete because the GitHub app's installation token returns 403 on delete_ref. The user has a periodic cleanup script that handles stale remote branches, so the right instruction is just "leave the remote feature branch alone" without the failed-attempt explanation. The shorter wording also avoids inviting a future session to test the behaviour. https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH --- CLAUDE.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2f613ec..7642d56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -379,13 +379,10 @@ When the user has explicitly green-lit merging this branch to `main`: message covering the whole change 4. `git push origin main` 5. Delete the feature branch locally (`git branch -d `). - Do NOT try to delete the remote feature branch - Anthropic's GitHub - app doesn't carry the `delete_ref` scope, so `git push origin - --delete ` returns HTTP 403 by design. That's not a - fixable condition from inside the session; the user cleans up stale - remote branches on their own cadence. No need to mention the skipped - remote delete in the end-of-turn summary either - it's the expected - shape of every merge. + Leave the remote feature branch alone - the user runs a periodic + cleanup script over stale remote branches. No need to mention the + skipped remote delete in the end-of-turn summary either; it's the + expected shape of every merge. **Claude Code CLI** (interactive local sessions): commit to whatever branch is currently checked out and stop. Do not rebase, do not merge, do not push, From 01d6a098585d9eb1975a9db9c16936ddd694c863 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 10 May 2026 12:59:18 +0000 Subject: [PATCH 2/2] Wiki: lock scope to user-centric only; librarian deletes out-of-scope articles Production traffic showed the per-conversation agent writing standalone articles about generic world-knowledge topics that came up in conversation. The concrete case: a brainstorm about app naming mentioned the 1980s "Kermit" file-transfer protocol, and the agent created a "Kermit protocol" article. The wiki is meant to be ABOUT the user (their projects, people in their life, things they're learning, their work) - not a general encyclopedia of topics that came up. Both the per-conversation prompt and the librarian prompt now carry an explicit scope block: IN scope: projects, people, places, learning, work, hobbies, experiments, the user themselves. OUT of scope: generic technical concepts, world history, public figures the user does not know personally, tutorials, news. External topics that come up INSIDE a user-centric article get a Markdown link to a public source (Wikipedia conventionally), not their own article. Concrete example baked into the prompt: instead of writing a standalone "Kermit protocol" article, link to [Kermit](https://en.wikipedia.org/wiki/Kermit_(protocol)) inside the user-centric article about the app being built. The librarian's workflow now starts with scope cleanup (workflow step 1, ahead of duplicate consolidation) so existing out-of-scope articles get removed on the next 12h cycle. Step 1 deliberately runs before consolidation so the librarian doesn't tidy two off-topic articles into one tidier-but-still-off-topic article. wiki_delete now has an explicit second case for out-of- scope removal that doesn't require a merge target. The chat-prompt's WIKI_BLOCK and the user docs now reflect the user-centric scope so the chat assistant's mental model of the wiki matches what the agents actually maintain. Rationale comments at the top of both prompts record the historical failure mode so a future revisit doesn't quietly re-relax the scope rule. Also includes the previously-pushed CLAUDE.md edit dropping the delete_ref 403 explanation in favour of "leave the remote feature branch alone; the user has a cleanup script." https://claude.ai/code/session_015XcR7xzLdij66ZbYERUdLH --- docs/dev/wiki.md | 16 +++++ docs/user/wiki.md | 48 ++++++++++++++- src/lib/agents/wiki-librarian/prompt.ts | 78 +++++++++++++++++++------ src/lib/agents/wiki/prompt.ts | 77 +++++++++++++++++++++++- src/lib/chat-prompt.ts | 27 ++++----- 5 files changed, 212 insertions(+), 34 deletions(-) diff --git a/docs/dev/wiki.md b/docs/dev/wiki.md index 3c96f90..4b2df48 100644 --- a/docs/dev/wiki.md +++ b/docs/dev/wiki.md @@ -376,6 +376,22 @@ JSON). ## Gotchas +- **The wiki is user-centric, not a general encyclopedia.** The + per-conversation prompt has historically slipped on this - a + brainstorm about app naming that mentioned the 1980s "Kermit" + protocol produced a standalone "Kermit protocol" article. Both + the per-conversation prompt and the librarian prompt now carry + an explicit scope block (IN: projects, people, places, + learning, work, hobbies, experiments / OUT: generic technical + concepts, world history, public figures the user does not + know, tutorials). The librarian's workflow step 1 is "delete + out-of-scope articles", deliberately ahead of duplicate + consolidation so it doesn't tidy two off-topic articles into + one off-topic article. External topics referenced inside a + user-centric article get a Markdown link (Wikipedia + conventionally), not their own page. If you relax the scope + rule, leave the historical failure mode noted somewhere or + the per-thread shape will silently re-introduce it. - **Use `messages.created_at` for the day-gate, not `threads.updated_at`.** The journal RPC reads `threads.updated_at` because journals fired on a same-day diff --git a/docs/user/wiki.md b/docs/user/wiki.md index 3deff0c..c0ac494 100644 --- a/docs/user/wiki.md +++ b/docs/user/wiki.md @@ -1,8 +1,9 @@ # Wiki -The Wiki is a flat encyclopedia about you. Every entry is a titled -article in encyclopedic third-person prose - about a project, a person -in your life, a place, an interest, a recurring situation. There's no +The Wiki is a flat encyclopedia **about you** - your projects, the +people in your life, places you live or care about, things you're +learning or reading, work, hobbies, experiments. Every entry is a +titled article in encyclopedic third-person prose. There's no nesting; everything sits at the same level and the drawer lists articles alphabetically. @@ -13,6 +14,47 @@ topical pages that cover what something IS - "the recipe project", "Maya", "Lisbon trip planning". An article sits across many conversations. +## Scope: about you, not about the world + +The wiki is deliberately user-centric. Articles describe things in +your life. They do **not** describe generic topics that came up in +conversation. So: + +**In scope** - article-worthy when discussed: + +- Projects you are building, planning, or running. +- People in your life - family, friends, colleagues, contacts. +- Places you live, work, travel to, or care about. +- Things you are learning or reading - books, courses, papers, + skills you are practising. +- Habits and experiments you are tracking - a running streak, + a sourdough starter, an elimination diet. +- Your career, current job, prior roles, ongoing work. +- Hobbies and interests you have invested time in. + +**Out of scope** - even if the conversation discussed them at length: + +- General technical concepts, libraries, protocols, or frameworks + not specific to one of your projects. +- World-knowledge topics: historical events, scientific concepts, + geography. +- Public people you don't know personally - celebrities, authors of + books you're reading, historical figures. +- News, current events. +- Tutorials and one-off help interactions. + +When an external topic gets mentioned inside a user-centric article +(say, you're building an app whose name references a 1980s +file-transfer protocol), the agent will add a Markdown link to a +public source like Wikipedia rather than creating a separate +article. External topics are linked, never given their own pages. + +Both the per-conversation agent and the librarian enforce this +scope. The librarian will delete out-of-scope articles it finds on +its periodic sweep, so any encyclopedic-but-not-about-you articles +that slipped through earlier should disappear over the next 12 to +24 hours. + ## Opening the Wiki Click the **Wiki** tab in the left drawer. The sidebar shows the diff --git a/src/lib/agents/wiki-librarian/prompt.ts b/src/lib/agents/wiki-librarian/prompt.ts index 36a8fa4..7038ca6 100644 --- a/src/lib/agents/wiki-librarian/prompt.ts +++ b/src/lib/agents/wiki-librarian/prompt.ts @@ -5,15 +5,24 @@ * - Input shape. The librarian gets a flat list of every article * (title + short excerpt) instead of a conversation. The opening * paragraph reflects that. - * - Goal. Reorganise, fact-check, consolidate. Not "react to a - * conversation". The librarian's win condition is "the wiki is - * more coherent than it was at the start of this run", which - * usually means fewer articles or sharper boundaries between - * them, not more. + * - Goal. Reorganise, fact-check, consolidate, and enforce scope. + * Not "react to a conversation". The librarian's win condition + * is "the wiki is more coherent than it was at the start of + * this run", which usually means fewer articles or sharper + * boundaries between them, not more. * - Tools. Has wiki_search + wiki_update + wiki_delete + * conversation_search. NO wiki_create - the librarian does not * invent new articles. * + * The scope-cleanup pass is workflow step 1, deliberately ahead of + * the duplicate / staleness / boundary passes. The per-conversation + * agent has historically slipped out-of-scope articles into the wiki + * (a Kermit-protocol article from a brainstorm about app naming, + * for example - see src/lib/agents/wiki/prompt.ts for the matching + * scope rule). Getting those out before the rest of the workflow + * runs avoids "consolidating" two off-topic articles into one + * tidier-but-still-off-topic article. + * * Voice and "preserve facts" discipline are shared with the per- * conversation agent's prompt - same encyclopedic third-person * register, same "do not fabricate / do not discard facts" rules. @@ -28,14 +37,30 @@ export function buildWikiLibrarianPrompt(opts: { 'list below is every article in the wiki right now, by title, with', 'a short excerpt of each. Your job is to make the wiki more', 'coherent than you found it - not by adding articles, but by', - 'consolidating duplicates, fact-checking against conversation', - 'history, and tightening the boundaries between articles that', - 'overlap.', + 'consolidating duplicates, removing out-of-scope articles, fact-', + 'checking against conversation history, and tightening the', + 'boundaries between articles that overlap.', '', 'Articles in the wiki:', '', articleList, '', + '**Scope: this wiki is about the user, not the world.** Every', + "article must be about the user's life, projects, people, work,", + 'learning, or interests. Articles whose subject is a generic world-', + 'knowledge topic (a programming concept, a protocol, a historical', + 'event, a public figure the user does not know personally, a', + 'tutorial or explainer of something external) DO NOT belong in the', + 'wiki and should be deleted - even if they are well-written. The', + 'concrete failure mode the wiki must defend against: a brainstorming', + 'conversation mentioned that an app is named after the 1980s "Kermit"', + 'protocol, and the per-conversation agent created a standalone', + '"Kermit protocol" article. The fix is to delete that article (and,', + 'if the relevant user-centric article exists, e.g. one about the', + 'app the user is building, optionally edit a single Markdown link', + 'into it that references Kermit). External topics get LINKED from', + 'user-centric articles; they do not get their own articles.', + '', '**Tools you can use**:', '', '- `wiki_search` - read the full body of any article (search by', @@ -49,10 +74,15 @@ export function buildWikiLibrarianPrompt(opts: { ' that are still accurate; integrate facts from a duplicate', ' article you intend to delete; correct stale information you', ' verified is contradicted by recent conversations.', - '- `wiki_delete` - hard-delete an article. ONLY use this for', - ' consolidation: when you have just updated another article to', - ' cover everything the deleted article said. Never delete an', - ' article whose content has not been merged elsewhere.', + '- `wiki_delete` - hard-delete an article. Use for two cases:', + ' (a) consolidation - you just updated another article to cover', + ' everything the deleted article said.', + ' (b) out-of-scope cleanup - the article is about a generic world-', + " knowledge topic that does not belong in the user's wiki", + ' (see the scope rule above). For these, no merge is required;', + ' the article should not exist at all.', + ' Never delete a user-centric article whose content has not been', + ' merged into another user-centric article.', '', '**You DO NOT have wiki_create.** New articles flow from the per-', 'conversation wiki agent or directly from the user. Your job is', @@ -63,20 +93,34 @@ export function buildWikiLibrarianPrompt(opts: { '', '**Workflow**:', '', - '1. **Scan the list above for duplicates and near-duplicates.**', - ' Two articles whose titles or excerpts strongly overlap are', - ' the highest-value consolidation targets. Use wiki_search to', + '1. **Scan for out-of-scope articles first.** Look at the list', + ' above for any title or excerpt that reads as a generic', + ' encyclopedia topic rather than something specific to the', + ' user (technical concepts, protocols, world events, public', + ' figures the user does not know personally, generic tutorials,', + " debug-session writeups). For each suspicious article: read", + ' the full body via wiki_search to confirm it is not in fact', + " about a project the user is building or someone in their life,", + ' and only after confirming, wiki_delete it. If a related user-', + ' centric article exists where the topic is referenced (e.g. an', + ' article about the app whose name references the deleted topic),', + ' wiki_update that article first to add a short Markdown link to', + ' a public reference (Wikipedia conventionally) so the connection', + ' is preserved.', + '2. **Scan the list for duplicates and near-duplicates.** Two', + ' articles whose titles or excerpts strongly overlap are the', + ' next-highest-value consolidation targets. Use wiki_search to', ' read full bodies before deciding. If you confirm overlap:', ' wiki_update the article that is the better home (longer,', ' broader, or more accurate) to absorb the unique facts from', ' the duplicate, then wiki_delete the duplicate.', - '2. **Check for stale facts.** When an excerpt makes a specific', + '3. **Check for stale facts.** When an excerpt makes a specific', ' claim that could plausibly have changed (a job title, a', ' relationship status, a project status, a date), use', ' conversation_search to look for recent mentions. If you find', ' a clear contradiction, wiki_update the article. If you find', ' nothing or only ambiguous evidence, leave it alone.', - '3. **Tighten subject boundaries.** When two articles cover', + '4. **Tighten subject boundaries.** When two articles cover', ' adjacent topics that confusingly bleed into each other (a', ' "Maya" article and a "household" article that both cover', ' the same person), decide which article is the right home', diff --git a/src/lib/agents/wiki/prompt.ts b/src/lib/agents/wiki/prompt.ts index 8bd5eea..81467cd 100644 --- a/src/lib/agents/wiki/prompt.ts +++ b/src/lib/agents/wiki/prompt.ts @@ -22,8 +22,24 @@ * Refer to the subject directly (their first name, the project * name) rather than "the user" so articles read as encyclopedia * entries rather than session-scoped notes. + * - **User-centric scope.** Earlier production traffic also + * surfaced the agent writing standalone articles about generic + * world-knowledge topics that came up in conversation - e.g. + * after a brainstorm about app naming that mentioned the 1980s + * "Kermit" file-transfer protocol, the agent created a "Kermit + * protocol" article. The wiki is meant to be ABOUT the user + * (their projects, people in their life, things they're + * learning, their work), not a general encyclopedia of topics + * that came up. The prompt now carries an explicit scope block + * with concrete IN / OUT examples and a rule that OUT-of-scope + * references inside a user-centric article get a Markdown link + * to a public source (Wikipedia conventionally) rather than a + * separate article. Do not relax this without leaving the + * historical failure mode noted somewhere - the per-conversation + * shape pushes the model toward "this came up so it deserves a + * page" by default. * - "Update is the default; create is rare." Earlier production - * traffic showed the agent generating one new article per + * traffic also showed the agent generating one new article per * conversation - the per-thread shape biased it toward * "this conversation is its own topic, write a new article". * The prompt now leads with the bias hard the other way: @@ -57,6 +73,55 @@ export const WIKI_AUTONOMOUS_PROMPT = [ 'situation. Articles are NEVER auto-injected into the chat; the user', 'and assistant only reach them through wiki_search.', '', + '**Scope: this wiki is about the user, not the world.** Every article', + "must be about the user's life, interests, projects, or context.", + 'External topics that came up in conversation but have no specific', + "connection to the user do NOT get their own article, even if the", + 'conversation discussed them at length. They get linked from a user-', + 'centric article instead.', + '', + 'IN scope (article-worthy when discussed):', + "- Projects the user is building, planning, or running.", + "- People in the user's life - family, friends, colleagues, contacts.", + "- Places the user lives, works, travels, or cares about.", + "- Things the user is learning or reading - books, courses, papers,", + ' skills they are practising.', + "- Habits and experiments the user is tracking - a running streak,", + ' a sourdough starter, an elimination diet.', + "- The user's career, current job, prior roles, ongoing work.", + "- Hobbies and interests the user has invested time in.", + '- The user themselves (a single article about them as the subject).', + '', + 'OUT of scope (do NOT create articles for these, even if the', + 'conversation went deep on them):', + "- General technical concepts, libraries, protocols, or frameworks", + " that are not specific to one of the user's projects (e.g.", + ' JavaScript closures, the Kermit protocol, HTTP semantics, regex).', + "- World-knowledge topics: historical events, scientific concepts,", + ' geography, biology, finance fundamentals.', + "- Public people the user does not know personally (celebrities,", + ' authors of books they are reading, historical figures).', + "- News, current events, things in the wider world.", + "- Tutorials, debug sessions, or one-off help interactions where the", + ' user was just looking up information.', + '', + 'When an OUT-of-scope topic comes up INSIDE a user-centric article', + '(e.g. the conversation mentioned that the app being built is named', + "after a 1980s file-transfer protocol called \"Kermit\"), link to a", + 'public reference rather than creating a separate article. The link', + 'goes inside the relevant user-centric article in standard Markdown', + 'form, e.g.', + ' "The name comes from [Kermit](https://en.wikipedia.org/wiki/Kermit_(protocol)),', + ' a 1980s file-transfer protocol."', + 'Wikipedia URLs are the conventional choice; any stable public URL', + 'works. Do NOT fabricate URLs - only use links you can write from', + 'memory of well-known articles, or omit the URL and just bold or', + 'italicize the term.', + '', + 'If a conversation is mostly out-of-scope - tutorials, generic', + 'technical Q&A, news, debugging unrelated libraries - produce zero', + 'edits. That is a correct outcome.', + '', '**The single most important discipline: UPDATE is the default,', 'CREATE is rare.** A new article should be the exception, not the', 'rule. Most conversations should result in zero or one wiki_update', @@ -148,6 +213,16 @@ export const WIKI_MANUAL_PROMPT = [ 'person. Refer to the subject directly (a first name, the project', "name) rather than \"the user\" unless the article's topic IS the user.", '', + '**Scope**: this wiki is about the user, not the world. Articles', + "describe the user's life, projects, people, work, learning, and", + 'interests. References to external topics (a generic library, a', + 'historical event, a public figure the user does not know) belong', + "as Markdown links inside a user-centric article, NOT as their own", + 'articles. If the user instructs you to add information that would', + 'pull the article away from being about them - e.g. asks you to', + 'expand the article into a general explainer of an external topic', + '- prefer a noop with a one-sentence reason over silently drifting.', + '', '**Rules**:', '', "- Do exactly what the user asks. Their instructions are the binding", diff --git a/src/lib/chat-prompt.ts b/src/lib/chat-prompt.ts index 6d94537..5d2e192 100644 --- a/src/lib/chat-prompt.ts +++ b/src/lib/chat-prompt.ts @@ -134,20 +134,21 @@ Explore their boundaries and assumptions. Do NOT hallucinate or invent psychological concepts or insights that have no basis in science or established philosophical traditions. `; -// Wiki framing. The user maintains a flat encyclopedia about themselves -// and the topics they care about - articles by title, written in third -// person, never auto-injected into the chat. wiki_search is the only -// path the assistant has to reach this layer: when the user references -// something topical or factual about themselves (a project name, a -// person, a place, a recurring habit), call wiki_search to pull the -// relevant article so the reply is grounded rather than guessing. -// Distinct from memory (atomic facts) and journal (dated reflections): -// the wiki carries curated topical articles that span many -// conversations. +// Wiki framing. The user maintains a flat encyclopedia ABOUT THEMSELVES +// - their projects, the people in their life, places they live or visit, +// things they're learning or reading, ongoing experiments, their work. +// Articles by title, written in third person, never auto-injected into +// the chat. wiki_search is the only path the assistant has to reach +// this layer. The scope is intentionally NOT a general encyclopedia of +// topics that came up - external topics referenced inside a user-centric +// article are linked (Wikipedia conventionally), not given their own +// pages. Distinct from memory (atomic facts) and journal (dated +// reflections): the wiki carries curated topical articles centered on +// the user, that span many conversations. const WIKI_BLOCK = `\ -The application also maintains a user wiki: a flat collection of titled, encyclopedic articles the user (and a background agent) curate alongside memories and the journal. -Articles are NEVER auto-injected into the chat - call wiki_search whenever the user references something topical or factual about themselves, a project, a person, or a place to retrieve the relevant article. -The wiki is the right surface for "what is X" lookups against the user's own knowledge graph; memories carry atomic facts, the journal carries dated reflections, and the wiki carries the longer-form topical entries. +The application also maintains a user wiki: a flat collection of titled articles ABOUT THE USER - their projects, the people in their life, places they care about, things they are learning or reading, work, hobbies, experiments. Not a general encyclopedia of topics that came up. +Articles are NEVER auto-injected into the chat - call wiki_search whenever the user references one of their own projects, a person they know, a place in their life, or a topic they have personally invested in, to retrieve the relevant article. +The wiki is the right surface for "what is X (in the user's life)" lookups against the user's own knowledge graph; memories carry atomic facts, the journal carries dated reflections, and the wiki carries the longer-form topical entries on the user-centric subjects. `; // Toolbox framing. The model sees the catalog below with (on)/(off) marks