Vibecode Recall
track this build4 phases, 12 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Recall, save user-provided sources, summarize them, and build a linked personal knowledge graph. The hard boundary is hosted extraction, model routing, browser capture, sync, and knowledge-graph polish, plus capture polish, sync, and content partnerships.
You are building a lean indie version of Recall. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Recall · indie build A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Sources saved with their extracted text at capture time | a link rots and a page changes, so the text is what you actually have | | Entities | Extracted with resolution across sources | the same person named three ways is one node or the graph is useless | | Links | Typed, evidenced and thresholded rather than generated freely | an unthresholded graph connects everything and means nothing | | Summaries | Generated with citations into the stored text | a summary you cannot check is a summary you should not trust | | Runtime | TypeScript with a local application and SQLite | reading history is a record of what you were thinking about | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application and the extraction pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A local model runner** · free, needs memory - Why: Reading history and summaries are personal, and entity extraction runs over everything you save. - Get it: Any local runner with a model that returns structured output reliably for extraction. - Verify: Ten extractions return valid structured output with positions - [ ] **Two hundred real sources you have read** · free - Why: Graph density, entity resolution and link quality are all uninteresting below a hundred sources and become the entire problem above two hundred. - Get it: Import from wherever you save things now, including the duplicates and the articles about the same topic from different angles. - Verify: Two hundred sources exist with overlapping subject matter - [ ] **Your link threshold and density limit** · free - Why: An unthresholded graph connects everything, and choosing the threshold after seeing the graph means choosing whichever makes it look best. - Get it: Decide the minimum link strength and the density above which you consider the graph degraded, before generating any links. - Verify: Both numbers are written down and configured - [ ] **Ten entities you can check by hand** · free - Why: Entity resolution quality is only assessable where you know the right answer, including the cases where two similar names are genuinely different people. - Get it: From your sources, list ten entities including two pairs that look similar and are distinct. - Verify: The list exists with the correct merges and non-merges noted ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. - hosted extraction, model routing, browser capture, sync, and knowledge-graph polish - publisher bypasses - cross-device mobile capture - high-quality recommendation graph - licensed full-text feeds If one of those is essential to you, that is the reason to keep paying for Recall, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Recall The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a knowledge graph built from what you read, where a link means something to replace Recall. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Capture: Sources saved with their extracted text at capture time · a link rots and a page changes, so the text is what you actually have - Entities: Extracted with resolution across sources · the same person named three ways is one node or the graph is useless - Links: Typed, evidenced and thresholded rather than generated freely · an unthresholded graph connects everything and means nothing - Summaries: Generated with citations into the stored text · a summary you cannot check is a summary you should not trust - Runtime: TypeScript with a local application and SQLite · reading history is a record of what you were thinking about ### Data model (create this before Phase 1) - sources(id, kind, url, title, text, author, published_at, captured_at) - entities(id, kind, canonical_name, aliases_json, first_seen_source_id) · kind is person, organisation, concept or work - mentions(source_id, entity_id, char_start, char_end, surface_form, confidence) - links(id, from_entity_id, to_entity_id, kind, strength, evidence_source_ids_json, created_at) · kind names the relationship - summaries(source_id, body, cited_spans_json, model, generated_at, validated) - reviews(entity_id, merged_into_id, decided_by, at) · entity merges are decisions and are reversible - A link stores the sources that evidence it, so a connection can be inspected rather than trusted, and a weak link can be found and removed. ### Phase 1 · Sources saved whole Build: Capture sources with their extracted readable text, title, author and date. Store the text at capture rather than fetching later. Handle articles, documents and pages behind a login. Trap: Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. Done when: Sources store their extracted text at capture. A source whose page later changes still shows what you captured. Documents and login-protected pages capture what was on screen. Capture is one action with no prompt for tags or categories. Do not build yet: No entities, links or summaries. ### Phase 2 · The same thing named three ways Build: Extract entity mentions with their positions, and resolve variants to a canonical entity. Propose merges rather than performing them automatically. Make every merge reversible with its decision recorded. Trap: Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. Done when: Mentions of one entity under different surface forms resolve to a single node once merged. A merge is proposed rather than applied automatically. Every merge is reversible and its decision is recorded. Two distinct entities with similar names are proposed rather than merged, tested with a crafted pair. Do not build yet: No links or summaries yet. ### Phase 3 · Links that mean something Build: Create links only above a strength threshold, typed by relationship and carrying the sources that evidence them. Report graph density and refuse to create links that would connect an entity to most of the graph. Trap: Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. Done when: Links are created only above a strength threshold and carry their evidencing sources. Graph density is reported and a hub entity connecting to most of the graph is flagged rather than accepted. Each link has a relationship type rather than being an untyped association. Removing a source removes the links it alone evidenced. Do not build yet: No summaries or navigation yet. ### Phase 4 · Summaries you can check Build: Generate per-source summaries with citations into the stored text, validated before display. Reject a summary containing an uncited claim. Run locally by default. Trap: A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. Done when: Every summary line cites a span that resolves to the stored text, asserted in the suite. A crafted uncited summary is rejected. Clicking a citation shows the source text at that position. Summarisation runs locally by default with hosted use as an explicit per-source choice. Do not build yet: No navigation or export yet. ### Phase 5 · Finding your way back Build: Navigation from an entity to its sources and its strongest links, with search across everything. Export the graph and the sources. Prune weak links and unused entities on a policy. Trap: A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. Done when: Navigation starts from an entity and shows its sources and strongest links legibly. Search covers source text, summaries and entity names. Weak links and orphaned entities are pruned on a stated policy. Export contains sources, entities, links and summaries and re-imports identically. Do not build yet: Nothing after this. ### Out of scope (and why) - Answering questions across your library, which needs retrieval quality well beyond what a personal graph supports and which invites confident wrong answers. - Collaborative or shared graphs, which changes both the synchronisation problem and what people are willing to save. - Automatic topic modelling as a replacement for entities, which produces clusters nobody can name or navigate. - Importing an existing knowledge base with its own link semantics, which rarely maps cleanly onto another model. ### README must contain - That source text is stored at capture, because a link rots and a page changes - That entity merges are proposed and reversible, since combining two people is not recoverable - That links require a strength threshold and rising graph density is treated as a defect - That every summary line cites text that resolves, and an uncited summary is rejected entirely - That navigation starts from an entity, because a whole-graph view answers no question ===== AGENTS.md ===== # Agent instructions · Recall indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Sources saved with their extracted text at capture time, Extracted with resolution across sources, Typed, evidenced and thresholded rather than generated freely, Generated with citations into the stored text, TypeScript with a local application and SQLite. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ===== BUILD_PLAN.md ===== # Build plan · Recall A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Sources saved whole What you read is stored as text, not as a link. ### Steps 1. Store extracted text at capture Fetching later means a page that changed or vanished. Files: `src/capture.ts` 2. Capture author and date where present A claim's provenance depends on both. Files: `src/extract.ts` 3. Ask for nothing at capture Any field at capture time is a decision, and decisions end habits. Files: `src/capture.ts` ### Done when - [ ] Sources store extracted text at capture - [ ] A changed page still shows what was captured - [ ] Documents and logged-in pages capture correctly - [ ] Capture asks for nothing ### Watch out - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. ## Phase 2 · The same thing named three ways Entity mentions across sources resolve to one node. ### Steps 1. Extract mentions with positions So a link can point at the sentence rather than the document. Files: `src/entities.ts` 2. Propose merges, never apply them Combining two people is not recoverable from the mentions. Files: `src/merge.ts` 3. Keep merges reversible With the decision recorded. You will get some wrong. Files: `src/merge.ts`, `tests/merge-reversal.test.ts` ### Done when - [ ] Variant surface forms resolve to one node - [ ] Merges are proposed, not applied - [ ] Merges are reversible with recorded decisions - [ ] Similar-named distinct entities are not merged ### Watch out - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. ## Phase 3 · Links that mean something The graph is sparse enough to be useful. ### Steps 1. Threshold on strength Co-occurrence alone connects everything to everything. Files: `src/links.ts` 2. Type the relationship An untyped association is barely more useful than none. Files: `src/links.ts` 3. Report density and flag hubs Rising density is a defect, not coverage. Files: `src/graph-health.ts` ### Done when - [ ] Links require a strength threshold - [ ] Density is reported and hubs are flagged - [ ] Links carry a relationship type - [ ] Removing a source removes links it alone evidenced ### Watch out - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. ## Phase 4 · Summaries you can check Every summary line cites the text it came from. ### Steps 1. Require citations per line Validated against the stored text, not against the source URL. Files: `src/summarise.ts`, `src/validate.test.ts` 2. Reject rather than trim A summary with one invented line silently removed is still untrustworthy. Files: `src/summarise.ts` 3. Default to local processing Your reading history is a record of what you were thinking about. Files: `src/policy.ts` ### Done when - [ ] Every summary line cites resolvable text - [ ] An uncited summary is rejected - [ ] Citations navigate to the source position - [ ] Local processing is the default ### Watch out - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ## Not in this build - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. ## After v1, if you want it - Surfacing an old source when a new one connects to it, which is where a graph earns its keep - Contradiction detection between sources on one entity, which is genuinely useful and hard - Reading queue ordering by how connected a source is to what you already have ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Sources, entities and links. A record of what you have been reading. DB_PATH=~/.local/share/recall/graph.db # Required. Runs over everything you save, so local is the sensible default. EXTRACTION_MODEL=local # Required. Below this no link is created. Unthresholded graphs connect everything. LINK_STRENGTH_THRESHOLD=0.6 # Required. An entity connecting to more than this is flagged as a hub rather than accepted. MAX_ENTITY_DEGREE=200 # Required. Present as a constant. Combining two people is not recoverable. AUTO_MERGE_ENTITIES=never # Required. Weak links and orphaned entities are pruned on a policy to keep the graph legible. PRUNE_AFTER_DAYS=365
You are building a lean indie version of Recall. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Recall · indie build A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Sources saved with their extracted text at capture time | a link rots and a page changes, so the text is what you actually have | | Entities | Extracted with resolution across sources | the same person named three ways is one node or the graph is useless | | Links | Typed, evidenced and thresholded rather than generated freely | an unthresholded graph connects everything and means nothing | | Summaries | Generated with citations into the stored text | a summary you cannot check is a summary you should not trust | | Runtime | TypeScript with a local application and SQLite | reading history is a record of what you were thinking about | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application and the extraction pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A local model runner** · free, needs memory - Why: Reading history and summaries are personal, and entity extraction runs over everything you save. - Get it: Any local runner with a model that returns structured output reliably for extraction. - Verify: Ten extractions return valid structured output with positions - [ ] **Two hundred real sources you have read** · free - Why: Graph density, entity resolution and link quality are all uninteresting below a hundred sources and become the entire problem above two hundred. - Get it: Import from wherever you save things now, including the duplicates and the articles about the same topic from different angles. - Verify: Two hundred sources exist with overlapping subject matter - [ ] **Your link threshold and density limit** · free - Why: An unthresholded graph connects everything, and choosing the threshold after seeing the graph means choosing whichever makes it look best. - Get it: Decide the minimum link strength and the density above which you consider the graph degraded, before generating any links. - Verify: Both numbers are written down and configured - [ ] **Ten entities you can check by hand** · free - Why: Entity resolution quality is only assessable where you know the right answer, including the cases where two similar names are genuinely different people. - Get it: From your sources, list ten entities including two pairs that look similar and are distinct. - Verify: The list exists with the correct merges and non-merges noted ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. - hosted extraction, model routing, browser capture, sync, and knowledge-graph polish - publisher bypasses - cross-device mobile capture - high-quality recommendation graph - licensed full-text feeds If one of those is essential to you, that is the reason to keep paying for Recall, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Recall The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a knowledge graph built from what you read, where a link means something to replace Recall. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Capture: Sources saved with their extracted text at capture time · a link rots and a page changes, so the text is what you actually have - Entities: Extracted with resolution across sources · the same person named three ways is one node or the graph is useless - Links: Typed, evidenced and thresholded rather than generated freely · an unthresholded graph connects everything and means nothing - Summaries: Generated with citations into the stored text · a summary you cannot check is a summary you should not trust - Runtime: TypeScript with a local application and SQLite · reading history is a record of what you were thinking about ### Data model (create this before Phase 1) - sources(id, kind, url, title, text, author, published_at, captured_at) - entities(id, kind, canonical_name, aliases_json, first_seen_source_id) · kind is person, organisation, concept or work - mentions(source_id, entity_id, char_start, char_end, surface_form, confidence) - links(id, from_entity_id, to_entity_id, kind, strength, evidence_source_ids_json, created_at) · kind names the relationship - summaries(source_id, body, cited_spans_json, model, generated_at, validated) - reviews(entity_id, merged_into_id, decided_by, at) · entity merges are decisions and are reversible - A link stores the sources that evidence it, so a connection can be inspected rather than trusted, and a weak link can be found and removed. ### Phase 1 · Sources saved whole Build: Capture sources with their extracted readable text, title, author and date. Store the text at capture rather than fetching later. Handle articles, documents and pages behind a login. Trap: Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. Done when: Sources store their extracted text at capture. A source whose page later changes still shows what you captured. Documents and login-protected pages capture what was on screen. Capture is one action with no prompt for tags or categories. Do not build yet: No entities, links or summaries. ### Phase 2 · The same thing named three ways Build: Extract entity mentions with their positions, and resolve variants to a canonical entity. Propose merges rather than performing them automatically. Make every merge reversible with its decision recorded. Trap: Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. Done when: Mentions of one entity under different surface forms resolve to a single node once merged. A merge is proposed rather than applied automatically. Every merge is reversible and its decision is recorded. Two distinct entities with similar names are proposed rather than merged, tested with a crafted pair. Do not build yet: No links or summaries yet. ### Phase 3 · Links that mean something Build: Create links only above a strength threshold, typed by relationship and carrying the sources that evidence them. Report graph density and refuse to create links that would connect an entity to most of the graph. Trap: Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. Done when: Links are created only above a strength threshold and carry their evidencing sources. Graph density is reported and a hub entity connecting to most of the graph is flagged rather than accepted. Each link has a relationship type rather than being an untyped association. Removing a source removes the links it alone evidenced. Do not build yet: No summaries or navigation yet. ### Phase 4 · Summaries you can check Build: Generate per-source summaries with citations into the stored text, validated before display. Reject a summary containing an uncited claim. Run locally by default. Trap: A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. Done when: Every summary line cites a span that resolves to the stored text, asserted in the suite. A crafted uncited summary is rejected. Clicking a citation shows the source text at that position. Summarisation runs locally by default with hosted use as an explicit per-source choice. Do not build yet: No navigation or export yet. ### Phase 5 · Finding your way back Build: Navigation from an entity to its sources and its strongest links, with search across everything. Export the graph and the sources. Prune weak links and unused entities on a policy. Trap: A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. Done when: Navigation starts from an entity and shows its sources and strongest links legibly. Search covers source text, summaries and entity names. Weak links and orphaned entities are pruned on a stated policy. Export contains sources, entities, links and summaries and re-imports identically. Do not build yet: Nothing after this. ### Out of scope (and why) - Answering questions across your library, which needs retrieval quality well beyond what a personal graph supports and which invites confident wrong answers. - Collaborative or shared graphs, which changes both the synchronisation problem and what people are willing to save. - Automatic topic modelling as a replacement for entities, which produces clusters nobody can name or navigate. - Importing an existing knowledge base with its own link semantics, which rarely maps cleanly onto another model. ### README must contain - That source text is stored at capture, because a link rots and a page changes - That entity merges are proposed and reversible, since combining two people is not recoverable - That links require a strength threshold and rising graph density is treated as a defect - That every summary line cites text that resolves, and an uncited summary is rejected entirely - That navigation starts from an entity, because a whole-graph view answers no question ===== AGENTS.md ===== # Agent instructions · Recall indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Sources saved with their extracted text at capture time, Extracted with resolution across sources, Typed, evidenced and thresholded rather than generated freely, Generated with citations into the stored text, TypeScript with a local application and SQLite. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ===== BUILD_PLAN.md ===== # Build plan · Recall A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Sources saved whole What you read is stored as text, not as a link. ### Steps 1. Store extracted text at capture Fetching later means a page that changed or vanished. Files: `src/capture.ts` 2. Capture author and date where present A claim's provenance depends on both. Files: `src/extract.ts` 3. Ask for nothing at capture Any field at capture time is a decision, and decisions end habits. Files: `src/capture.ts` ### Done when - [ ] Sources store extracted text at capture - [ ] A changed page still shows what was captured - [ ] Documents and logged-in pages capture correctly - [ ] Capture asks for nothing ### Watch out - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. ## Phase 2 · The same thing named three ways Entity mentions across sources resolve to one node. ### Steps 1. Extract mentions with positions So a link can point at the sentence rather than the document. Files: `src/entities.ts` 2. Propose merges, never apply them Combining two people is not recoverable from the mentions. Files: `src/merge.ts` 3. Keep merges reversible With the decision recorded. You will get some wrong. Files: `src/merge.ts`, `tests/merge-reversal.test.ts` ### Done when - [ ] Variant surface forms resolve to one node - [ ] Merges are proposed, not applied - [ ] Merges are reversible with recorded decisions - [ ] Similar-named distinct entities are not merged ### Watch out - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. ## Phase 3 · Links that mean something The graph is sparse enough to be useful. ### Steps 1. Threshold on strength Co-occurrence alone connects everything to everything. Files: `src/links.ts` 2. Type the relationship An untyped association is barely more useful than none. Files: `src/links.ts` 3. Report density and flag hubs Rising density is a defect, not coverage. Files: `src/graph-health.ts` ### Done when - [ ] Links require a strength threshold - [ ] Density is reported and hubs are flagged - [ ] Links carry a relationship type - [ ] Removing a source removes links it alone evidenced ### Watch out - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. ## Phase 4 · Summaries you can check Every summary line cites the text it came from. ### Steps 1. Require citations per line Validated against the stored text, not against the source URL. Files: `src/summarise.ts`, `src/validate.test.ts` 2. Reject rather than trim A summary with one invented line silently removed is still untrustworthy. Files: `src/summarise.ts` 3. Default to local processing Your reading history is a record of what you were thinking about. Files: `src/policy.ts` ### Done when - [ ] Every summary line cites resolvable text - [ ] An uncited summary is rejected - [ ] Citations navigate to the source position - [ ] Local processing is the default ### Watch out - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ## Not in this build - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. ## After v1, if you want it - Surfacing an old source when a new one connects to it, which is where a graph earns its keep - Contradiction detection between sources on one entity, which is genuinely useful and hard - Reading queue ordering by how connected a source is to what you already have ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Sources, entities and links. A record of what you have been reading. DB_PATH=~/.local/share/recall/graph.db # Required. Runs over everything you save, so local is the sensible default. EXTRACTION_MODEL=local # Required. Below this no link is created. Unthresholded graphs connect everything. LINK_STRENGTH_THRESHOLD=0.6 # Required. An entity connecting to more than this is flagged as a hub rather than accepted. MAX_ENTITY_DEGREE=200 # Required. Present as a constant. Combining two people is not recoverable. AUTO_MERGE_ENTITIES=never # Required. Weak links and orphaned entities are pruned on a policy to keep the graph legible. PRUNE_AFTER_DAYS=365
You are building a production product version of Recall. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Recall · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Recall, save user-provided sources, summarize them, and build a linked personal knowledge graph. The hard boundary is hosted extraction, model routing, browser capture, sync, and knowledge-graph polish, plus capture polish, sync, and content partnerships. ## Product outcome A knowledge graph sparse enough to be useful, where every link can be inspected, every summary can be checked, and nothing was merged without you agreeing to it. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - local disk - optional browser bookmarklet - user-supplied RSS feeds ## Explicit non-goals for v1 - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. - hosted extraction, model routing, browser capture, sync, and knowledge-graph polish - publisher bypasses - cross-device mobile capture - high-quality recommendation graph - licensed full-text feeds ## Success criteria - Source text is confirmed stored at capture - Entity merges are confirmed proposed rather than automatic - Links are confirmed to require a strength threshold - An uncited summary is confirmed rejected - Entity navigation is confirmed legible at two hundred sources ===== BRIEF.md ===== # Build brief · Recall The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a knowledge graph built from what you read, where a link means something to replace Recall. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Capture: Sources saved with their extracted text at capture time · a link rots and a page changes, so the text is what you actually have - Entities: Extracted with resolution across sources · the same person named three ways is one node or the graph is useless - Links: Typed, evidenced and thresholded rather than generated freely · an unthresholded graph connects everything and means nothing - Summaries: Generated with citations into the stored text · a summary you cannot check is a summary you should not trust - Runtime: TypeScript with a local application and SQLite · reading history is a record of what you were thinking about ### Data model (create this before Phase 1) - sources(id, kind, url, title, text, author, published_at, captured_at) - entities(id, kind, canonical_name, aliases_json, first_seen_source_id) · kind is person, organisation, concept or work - mentions(source_id, entity_id, char_start, char_end, surface_form, confidence) - links(id, from_entity_id, to_entity_id, kind, strength, evidence_source_ids_json, created_at) · kind names the relationship - summaries(source_id, body, cited_spans_json, model, generated_at, validated) - reviews(entity_id, merged_into_id, decided_by, at) · entity merges are decisions and are reversible - A link stores the sources that evidence it, so a connection can be inspected rather than trusted, and a weak link can be found and removed. ### Phase 1 · Sources saved whole Build: Capture sources with their extracted readable text, title, author and date. Store the text at capture rather than fetching later. Handle articles, documents and pages behind a login. Trap: Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. Done when: Sources store their extracted text at capture. A source whose page later changes still shows what you captured. Documents and login-protected pages capture what was on screen. Capture is one action with no prompt for tags or categories. Do not build yet: No entities, links or summaries. ### Phase 2 · The same thing named three ways Build: Extract entity mentions with their positions, and resolve variants to a canonical entity. Propose merges rather than performing them automatically. Make every merge reversible with its decision recorded. Trap: Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. Done when: Mentions of one entity under different surface forms resolve to a single node once merged. A merge is proposed rather than applied automatically. Every merge is reversible and its decision is recorded. Two distinct entities with similar names are proposed rather than merged, tested with a crafted pair. Do not build yet: No links or summaries yet. ### Phase 3 · Links that mean something Build: Create links only above a strength threshold, typed by relationship and carrying the sources that evidence them. Report graph density and refuse to create links that would connect an entity to most of the graph. Trap: Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. Done when: Links are created only above a strength threshold and carry their evidencing sources. Graph density is reported and a hub entity connecting to most of the graph is flagged rather than accepted. Each link has a relationship type rather than being an untyped association. Removing a source removes the links it alone evidenced. Do not build yet: No summaries or navigation yet. ### Phase 4 · Summaries you can check Build: Generate per-source summaries with citations into the stored text, validated before display. Reject a summary containing an uncited claim. Run locally by default. Trap: A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. Done when: Every summary line cites a span that resolves to the stored text, asserted in the suite. A crafted uncited summary is rejected. Clicking a citation shows the source text at that position. Summarisation runs locally by default with hosted use as an explicit per-source choice. Do not build yet: No navigation or export yet. ### Phase 5 · Finding your way back Build: Navigation from an entity to its sources and its strongest links, with search across everything. Export the graph and the sources. Prune weak links and unused entities on a policy. Trap: A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. Done when: Navigation starts from an entity and shows its sources and strongest links legibly. Search covers source text, summaries and entity names. Weak links and orphaned entities are pruned on a stated policy. Export contains sources, entities, links and summaries and re-imports identically. Do not build yet: Nothing after this. ### Out of scope (and why) - Answering questions across your library, which needs retrieval quality well beyond what a personal graph supports and which invites confident wrong answers. - Collaborative or shared graphs, which changes both the synchronisation problem and what people are willing to save. - Automatic topic modelling as a replacement for entities, which produces clusters nobody can name or navigate. - Importing an existing knowledge base with its own link semantics, which rarely maps cleanly onto another model. ### README must contain - That source text is stored at capture, because a link rots and a page changes - That entity merges are proposed and reversible, since combining two people is not recoverable - That links require a strength threshold and rising graph density is treated as a defect - That every summary line cites text that resolves, and an uncited summary is rejected entirely - That navigation starts from an entity, because a whole-graph view answers no question ===== ARCHITECTURE.md ===== # Architecture · Recall ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Sources saved with their extracted text at capture time | a link rots and a page changes, so the text is what you actually have | | Entities | Extracted with resolution across sources | the same person named three ways is one node or the graph is useless | | Links | Typed, evidenced and thresholded rather than generated freely | an unthresholded graph connects everything and means nothing | | Summaries | Generated with citations into the stored text | a summary you cannot check is a summary you should not trust | | Runtime | TypeScript with a local application and SQLite | reading history is a record of what you were thinking about | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Sources stored with their text. | Any implementation, provided text is stored at capture rather than fetched later. | | Entities | Extraction and proposed resolution. | Any extractor, provided merges are proposed and reversible. | | Links | Thresholded, typed, evidenced connections. | Any scoring, provided density is treated as a health metric. | | Summaries | Cited generation with validation. | Any model, provided uncited output is rejected. | | Navigation | Entity-first exploration. | Any interface, provided the whole-graph view is not the product. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Sources, entities and links. A record of what you have been reading. - `EXTRACTION_MODEL` · required · Runs over everything you save, so local is the sensible default. - `LINK_STRENGTH_THRESHOLD` · required · Below this no link is created. Unthresholded graphs connect everything. - `MAX_ENTITY_DEGREE` · required · An entity connecting to more than this is flagged as a hub rather than accepted. - `AUTO_MERGE_ENTITIES` · required · Present as a constant. Combining two people is not recoverable. - `PRUNE_AFTER_DAYS` · required · Weak links and orphaned entities are pruned on a policy to keep the graph legible. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Recall product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Sources saved with their extracted text at capture time, Extracted with resolution across sources, Typed, evidenced and thresholded rather than generated freely, Generated with citations into the stored text, TypeScript with a local application and SQLite. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. - A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. ===== MILESTONES.md ===== # Delivery milestones · Recall Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Sources saved whole What you read is stored as text, not as a link. ### Steps 1. Store extracted text at capture Fetching later means a page that changed or vanished. Files: `src/capture.ts` 2. Capture author and date where present A claim's provenance depends on both. Files: `src/extract.ts` 3. Ask for nothing at capture Any field at capture time is a decision, and decisions end habits. Files: `src/capture.ts` ### Done when - [ ] Sources store extracted text at capture - [ ] A changed page still shows what was captured - [ ] Documents and logged-in pages capture correctly - [ ] Capture asks for nothing ### Watch out - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. ## M2 · The same thing named three ways Entity mentions across sources resolve to one node. ### Steps 1. Extract mentions with positions So a link can point at the sentence rather than the document. Files: `src/entities.ts` 2. Propose merges, never apply them Combining two people is not recoverable from the mentions. Files: `src/merge.ts` 3. Keep merges reversible With the decision recorded. You will get some wrong. Files: `src/merge.ts`, `tests/merge-reversal.test.ts` ### Done when - [ ] Variant surface forms resolve to one node - [ ] Merges are proposed, not applied - [ ] Merges are reversible with recorded decisions - [ ] Similar-named distinct entities are not merged ### Watch out - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. ## M3 · Links that mean something The graph is sparse enough to be useful. ### Steps 1. Threshold on strength Co-occurrence alone connects everything to everything. Files: `src/links.ts` 2. Type the relationship An untyped association is barely more useful than none. Files: `src/links.ts` 3. Report density and flag hubs Rising density is a defect, not coverage. Files: `src/graph-health.ts` ### Done when - [ ] Links require a strength threshold - [ ] Density is reported and hubs are flagged - [ ] Links carry a relationship type - [ ] Removing a source removes links it alone evidenced ### Watch out - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. ## M4 · Summaries you can check Every summary line cites the text it came from. ### Steps 1. Require citations per line Validated against the stored text, not against the source URL. Files: `src/summarise.ts`, `src/validate.test.ts` 2. Reject rather than trim A summary with one invented line silently removed is still untrustworthy. Files: `src/summarise.ts` 3. Default to local processing Your reading history is a record of what you were thinking about. Files: `src/policy.ts` ### Done when - [ ] Every summary line cites resolvable text - [ ] An uncited summary is rejected - [ ] Citations navigate to the source position - [ ] Local processing is the default ### Watch out - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ## M5 · Finding your way back (production only) The graph helps you answer a question rather than looking impressive. ### Steps 1. Navigate from an entity outward A whole-graph view of thousands of nodes answers nothing. Files: `src/navigate.ts` 2. Search everything, not only titles You remember a phrase, not a heading. Files: `src/search.ts` 3. Prune weak links on a policy Density grows and legibility falls without it. Files: `src/prune.ts` ### Done when - [ ] Entity navigation is legible and useful - [ ] Search covers text, summaries and names - [ ] Weak links and orphans are pruned on a policy - [ ] Export round trips the whole graph ### Watch out - A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. ===== OPERATIONS.md ===== # Operations · Recall ## Backup Sources and their captured text are the irreplaceable part. Entities, links and summaries are all derived and regenerable, so back up the sources first and treat the rest as a cache with opinions. ## Restore Restore and confirm entity merge decisions survived, since those are human judgements rather than derived data, and regenerating without them recreates every duplicate you already resolved. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Graph density and hub count are the health metrics. Both grow silently as sources accumulate, and a graph that has become dense is useless without ever having failed. ## Incident checklist A summary asserting something the source did not is the failure that matters, and it means citation validation passed something it should not have. The stored spans make it checkable rather than arguable. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Source text is confirmed stored at capture - [ ] Entity merges are confirmed proposed rather than automatic - [ ] Links are confirmed to require a strength threshold - [ ] An uncited summary is confirmed rejected - [ ] Entity navigation is confirmed legible at two hundred sources ## Launch constraint Do not market omitted Recall capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Sources, entities and links. A record of what you have been reading. DB_PATH=~/.local/share/recall/graph.db # Required. Runs over everything you save, so local is the sensible default. EXTRACTION_MODEL=local # Required. Below this no link is created. Unthresholded graphs connect everything. LINK_STRENGTH_THRESHOLD=0.6 # Required. An entity connecting to more than this is flagged as a hub rather than accepted. MAX_ENTITY_DEGREE=200 # Required. Present as a constant. Combining two people is not recoverable. AUTO_MERGE_ENTITIES=never # Required. Weak links and orphaned entities are pruned on a policy to keep the graph legible. PRUNE_AFTER_DAYS=365
# Recall · indie build A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Sources saved with their extracted text at capture time | a link rots and a page changes, so the text is what you actually have | | Entities | Extracted with resolution across sources | the same person named three ways is one node or the graph is useless | | Links | Typed, evidenced and thresholded rather than generated freely | an unthresholded graph connects everything and means nothing | | Summaries | Generated with citations into the stored text | a summary you cannot check is a summary you should not trust | | Runtime | TypeScript with a local application and SQLite | reading history is a record of what you were thinking about | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the local application and the extraction pipeline. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A local model runner** · free, needs memory - Why: Reading history and summaries are personal, and entity extraction runs over everything you save. - Get it: Any local runner with a model that returns structured output reliably for extraction. - Verify: Ten extractions return valid structured output with positions - [ ] **Two hundred real sources you have read** · free - Why: Graph density, entity resolution and link quality are all uninteresting below a hundred sources and become the entire problem above two hundred. - Get it: Import from wherever you save things now, including the duplicates and the articles about the same topic from different angles. - Verify: Two hundred sources exist with overlapping subject matter - [ ] **Your link threshold and density limit** · free - Why: An unthresholded graph connects everything, and choosing the threshold after seeing the graph means choosing whichever makes it look best. - Get it: Decide the minimum link strength and the density above which you consider the graph degraded, before generating any links. - Verify: Both numbers are written down and configured - [ ] **Ten entities you can check by hand** · free - Why: Entity resolution quality is only assessable where you know the right answer, including the cases where two similar names are genuinely different people. - Get it: From your sources, list ten entities including two pairs that look similar and are distinct. - Verify: The list exists with the correct merges and non-merges noted ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. - hosted extraction, model routing, browser capture, sync, and knowledge-graph polish - publisher bypasses - cross-device mobile capture - high-quality recommendation graph - licensed full-text feeds If one of those is essential to you, that is the reason to keep paying for Recall, and the README should say so rather than pretend.
# Build brief · Recall The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a knowledge graph built from what you read, where a link means something to replace Recall. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Capture: Sources saved with their extracted text at capture time · a link rots and a page changes, so the text is what you actually have - Entities: Extracted with resolution across sources · the same person named three ways is one node or the graph is useless - Links: Typed, evidenced and thresholded rather than generated freely · an unthresholded graph connects everything and means nothing - Summaries: Generated with citations into the stored text · a summary you cannot check is a summary you should not trust - Runtime: TypeScript with a local application and SQLite · reading history is a record of what you were thinking about ### Data model (create this before Phase 1) - sources(id, kind, url, title, text, author, published_at, captured_at) - entities(id, kind, canonical_name, aliases_json, first_seen_source_id) · kind is person, organisation, concept or work - mentions(source_id, entity_id, char_start, char_end, surface_form, confidence) - links(id, from_entity_id, to_entity_id, kind, strength, evidence_source_ids_json, created_at) · kind names the relationship - summaries(source_id, body, cited_spans_json, model, generated_at, validated) - reviews(entity_id, merged_into_id, decided_by, at) · entity merges are decisions and are reversible - A link stores the sources that evidence it, so a connection can be inspected rather than trusted, and a weak link can be found and removed. ### Phase 1 · Sources saved whole Build: Capture sources with their extracted readable text, title, author and date. Store the text at capture rather than fetching later. Handle articles, documents and pages behind a login. Trap: Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. Done when: Sources store their extracted text at capture. A source whose page later changes still shows what you captured. Documents and login-protected pages capture what was on screen. Capture is one action with no prompt for tags or categories. Do not build yet: No entities, links or summaries. ### Phase 2 · The same thing named three ways Build: Extract entity mentions with their positions, and resolve variants to a canonical entity. Propose merges rather than performing them automatically. Make every merge reversible with its decision recorded. Trap: Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. Done when: Mentions of one entity under different surface forms resolve to a single node once merged. A merge is proposed rather than applied automatically. Every merge is reversible and its decision is recorded. Two distinct entities with similar names are proposed rather than merged, tested with a crafted pair. Do not build yet: No links or summaries yet. ### Phase 3 · Links that mean something Build: Create links only above a strength threshold, typed by relationship and carrying the sources that evidence them. Report graph density and refuse to create links that would connect an entity to most of the graph. Trap: Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. Done when: Links are created only above a strength threshold and carry their evidencing sources. Graph density is reported and a hub entity connecting to most of the graph is flagged rather than accepted. Each link has a relationship type rather than being an untyped association. Removing a source removes the links it alone evidenced. Do not build yet: No summaries or navigation yet. ### Phase 4 · Summaries you can check Build: Generate per-source summaries with citations into the stored text, validated before display. Reject a summary containing an uncited claim. Run locally by default. Trap: A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. Done when: Every summary line cites a span that resolves to the stored text, asserted in the suite. A crafted uncited summary is rejected. Clicking a citation shows the source text at that position. Summarisation runs locally by default with hosted use as an explicit per-source choice. Do not build yet: No navigation or export yet. ### Phase 5 · Finding your way back Build: Navigation from an entity to its sources and its strongest links, with search across everything. Export the graph and the sources. Prune weak links and unused entities on a policy. Trap: A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. Done when: Navigation starts from an entity and shows its sources and strongest links legibly. Search covers source text, summaries and entity names. Weak links and orphaned entities are pruned on a stated policy. Export contains sources, entities, links and summaries and re-imports identically. Do not build yet: Nothing after this. ### Out of scope (and why) - Answering questions across your library, which needs retrieval quality well beyond what a personal graph supports and which invites confident wrong answers. - Collaborative or shared graphs, which changes both the synchronisation problem and what people are willing to save. - Automatic topic modelling as a replacement for entities, which produces clusters nobody can name or navigate. - Importing an existing knowledge base with its own link semantics, which rarely maps cleanly onto another model. ### README must contain - That source text is stored at capture, because a link rots and a page changes - That entity merges are proposed and reversible, since combining two people is not recoverable - That links require a strength threshold and rising graph density is treated as a defect - That every summary line cites text that resolves, and an uncited summary is rejected entirely - That navigation starts from an entity, because a whole-graph view answers no question
# Agent instructions · Recall indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Sources saved with their extracted text at capture time, Extracted with resolution across sources, Typed, evidenced and thresholded rather than generated freely, Generated with citations into the stored text, TypeScript with a local application and SQLite. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line.
# Build plan · Recall A personal knowledge tool that summarises sources and connects them automatically, where the whole difficulty is that automatic linking produces mostly noise, and a graph where everything connects to everything is the same as a graph with no links at all. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Sources saved whole What you read is stored as text, not as a link. ### Steps 1. Store extracted text at capture Fetching later means a page that changed or vanished. Files: `src/capture.ts` 2. Capture author and date where present A claim's provenance depends on both. Files: `src/extract.ts` 3. Ask for nothing at capture Any field at capture time is a decision, and decisions end habits. Files: `src/capture.ts` ### Done when - [ ] Sources store extracted text at capture - [ ] A changed page still shows what was captured - [ ] Documents and logged-in pages capture correctly - [ ] Capture asks for nothing ### Watch out - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. ## Phase 2 · The same thing named three ways Entity mentions across sources resolve to one node. ### Steps 1. Extract mentions with positions So a link can point at the sentence rather than the document. Files: `src/entities.ts` 2. Propose merges, never apply them Combining two people is not recoverable from the mentions. Files: `src/merge.ts` 3. Keep merges reversible With the decision recorded. You will get some wrong. Files: `src/merge.ts`, `tests/merge-reversal.test.ts` ### Done when - [ ] Variant surface forms resolve to one node - [ ] Merges are proposed, not applied - [ ] Merges are reversible with recorded decisions - [ ] Similar-named distinct entities are not merged ### Watch out - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. ## Phase 3 · Links that mean something The graph is sparse enough to be useful. ### Steps 1. Threshold on strength Co-occurrence alone connects everything to everything. Files: `src/links.ts` 2. Type the relationship An untyped association is barely more useful than none. Files: `src/links.ts` 3. Report density and flag hubs Rising density is a defect, not coverage. Files: `src/graph-health.ts` ### Done when - [ ] Links require a strength threshold - [ ] Density is reported and hubs are flagged - [ ] Links carry a relationship type - [ ] Removing a source removes links it alone evidenced ### Watch out - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. ## Phase 4 · Summaries you can check Every summary line cites the text it came from. ### Steps 1. Require citations per line Validated against the stored text, not against the source URL. Files: `src/summarise.ts`, `src/validate.test.ts` 2. Reject rather than trim A summary with one invented line silently removed is still untrustworthy. Files: `src/summarise.ts` 3. Default to local processing Your reading history is a record of what you were thinking about. Files: `src/policy.ts` ### Done when - [ ] Every summary line cites resolvable text - [ ] An uncited summary is rejected - [ ] Citations navigate to the source position - [ ] Local processing is the default ### Watch out - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ## Not in this build - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. ## After v1, if you want it - Surfacing an old source when a new one connects to it, which is where a graph earns its keep - Contradiction detection between sources on one entity, which is genuinely useful and hard - Reading queue ordering by how connected a source is to what you already have
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Sources, entities and links. A record of what you have been reading. DB_PATH=~/.local/share/recall/graph.db # Required. Runs over everything you save, so local is the sensible default. EXTRACTION_MODEL=local # Required. Below this no link is created. Unthresholded graphs connect everything. LINK_STRENGTH_THRESHOLD=0.6 # Required. An entity connecting to more than this is flagged as a hub rather than accepted. MAX_ENTITY_DEGREE=200 # Required. Present as a constant. Combining two people is not recoverable. AUTO_MERGE_ENTITIES=never # Required. Weak links and orphaned entities are pruned on a policy to keep the graph legible. PRUNE_AFTER_DAYS=365
# Recall · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Recall, save user-provided sources, summarize them, and build a linked personal knowledge graph. The hard boundary is hosted extraction, model routing, browser capture, sync, and knowledge-graph polish, plus capture polish, sync, and content partnerships. ## Product outcome A knowledge graph sparse enough to be useful, where every link can be inspected, every summary can be checked, and nothing was merged without you agreeing to it. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - local disk - optional browser bookmarklet - user-supplied RSS feeds ## Explicit non-goals for v1 - Question answering across the library. - Collaborative or shared graphs. - Topic modelling as a replacement for entities. - Importing another knowledge base's link semantics. - hosted extraction, model routing, browser capture, sync, and knowledge-graph polish - publisher bypasses - cross-device mobile capture - high-quality recommendation graph - licensed full-text feeds ## Success criteria - Source text is confirmed stored at capture - Entity merges are confirmed proposed rather than automatic - Links are confirmed to require a strength threshold - An uncited summary is confirmed rejected - Entity navigation is confirmed legible at two hundred sources
# Build brief · Recall The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a knowledge graph built from what you read, where a link means something to replace Recall. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Capture: Sources saved with their extracted text at capture time · a link rots and a page changes, so the text is what you actually have - Entities: Extracted with resolution across sources · the same person named three ways is one node or the graph is useless - Links: Typed, evidenced and thresholded rather than generated freely · an unthresholded graph connects everything and means nothing - Summaries: Generated with citations into the stored text · a summary you cannot check is a summary you should not trust - Runtime: TypeScript with a local application and SQLite · reading history is a record of what you were thinking about ### Data model (create this before Phase 1) - sources(id, kind, url, title, text, author, published_at, captured_at) - entities(id, kind, canonical_name, aliases_json, first_seen_source_id) · kind is person, organisation, concept or work - mentions(source_id, entity_id, char_start, char_end, surface_form, confidence) - links(id, from_entity_id, to_entity_id, kind, strength, evidence_source_ids_json, created_at) · kind names the relationship - summaries(source_id, body, cited_spans_json, model, generated_at, validated) - reviews(entity_id, merged_into_id, decided_by, at) · entity merges are decisions and are reversible - A link stores the sources that evidence it, so a connection can be inspected rather than trusted, and a weak link can be found and removed. ### Phase 1 · Sources saved whole Build: Capture sources with their extracted readable text, title, author and date. Store the text at capture rather than fetching later. Handle articles, documents and pages behind a login. Trap: Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. Done when: Sources store their extracted text at capture. A source whose page later changes still shows what you captured. Documents and login-protected pages capture what was on screen. Capture is one action with no prompt for tags or categories. Do not build yet: No entities, links or summaries. ### Phase 2 · The same thing named three ways Build: Extract entity mentions with their positions, and resolve variants to a canonical entity. Propose merges rather than performing them automatically. Make every merge reversible with its decision recorded. Trap: Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. Done when: Mentions of one entity under different surface forms resolve to a single node once merged. A merge is proposed rather than applied automatically. Every merge is reversible and its decision is recorded. Two distinct entities with similar names are proposed rather than merged, tested with a crafted pair. Do not build yet: No links or summaries yet. ### Phase 3 · Links that mean something Build: Create links only above a strength threshold, typed by relationship and carrying the sources that evidence them. Report graph density and refuse to create links that would connect an entity to most of the graph. Trap: Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. Done when: Links are created only above a strength threshold and carry their evidencing sources. Graph density is reported and a hub entity connecting to most of the graph is flagged rather than accepted. Each link has a relationship type rather than being an untyped association. Removing a source removes the links it alone evidenced. Do not build yet: No summaries or navigation yet. ### Phase 4 · Summaries you can check Build: Generate per-source summaries with citations into the stored text, validated before display. Reject a summary containing an uncited claim. Run locally by default. Trap: A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. Done when: Every summary line cites a span that resolves to the stored text, asserted in the suite. A crafted uncited summary is rejected. Clicking a citation shows the source text at that position. Summarisation runs locally by default with hosted use as an explicit per-source choice. Do not build yet: No navigation or export yet. ### Phase 5 · Finding your way back Build: Navigation from an entity to its sources and its strongest links, with search across everything. Export the graph and the sources. Prune weak links and unused entities on a policy. Trap: A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product. Done when: Navigation starts from an entity and shows its sources and strongest links legibly. Search covers source text, summaries and entity names. Weak links and orphaned entities are pruned on a stated policy. Export contains sources, entities, links and summaries and re-imports identically. Do not build yet: Nothing after this. ### Out of scope (and why) - Answering questions across your library, which needs retrieval quality well beyond what a personal graph supports and which invites confident wrong answers. - Collaborative or shared graphs, which changes both the synchronisation problem and what people are willing to save. - Automatic topic modelling as a replacement for entities, which produces clusters nobody can name or navigate. - Importing an existing knowledge base with its own link semantics, which rarely maps cleanly onto another model. ### README must contain - That source text is stored at capture, because a link rots and a page changes - That entity merges are proposed and reversible, since combining two people is not recoverable - That links require a strength threshold and rising graph density is treated as a defect - That every summary line cites text that resolves, and an uncited summary is rejected entirely - That navigation starts from an entity, because a whole-graph view answers no question
# Architecture · Recall ## Stack | Part | Choice | Why | | --- | --- | --- | | Capture | Sources saved with their extracted text at capture time | a link rots and a page changes, so the text is what you actually have | | Entities | Extracted with resolution across sources | the same person named three ways is one node or the graph is useless | | Links | Typed, evidenced and thresholded rather than generated freely | an unthresholded graph connects everything and means nothing | | Summaries | Generated with citations into the stored text | a summary you cannot check is a summary you should not trust | | Runtime | TypeScript with a local application and SQLite | reading history is a record of what you were thinking about | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Capture | Sources stored with their text. | Any implementation, provided text is stored at capture rather than fetched later. | | Entities | Extraction and proposed resolution. | Any extractor, provided merges are proposed and reversible. | | Links | Thresholded, typed, evidenced connections. | Any scoring, provided density is treated as a health metric. | | Summaries | Cited generation with validation. | Any model, provided uncited output is rejected. | | Navigation | Entity-first exploration. | Any interface, provided the whole-graph view is not the product. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Sources, entities and links. A record of what you have been reading. - `EXTRACTION_MODEL` · required · Runs over everything you save, so local is the sensible default. - `LINK_STRENGTH_THRESHOLD` · required · Below this no link is created. Unthresholded graphs connect everything. - `MAX_ENTITY_DEGREE` · required · An entity connecting to more than this is flagged as a hub rather than accepted. - `AUTO_MERGE_ENTITIES` · required · Present as a constant. Combining two people is not recoverable. - `PRUNE_AFTER_DAYS` · required · Weak links and orphaned entities are pruned on a policy to keep the graph legible. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Recall product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Sources saved with their extracted text at capture time, Extracted with resolution across sources, Typed, evidenced and thresholded rather than generated freely, Generated with citations into the stored text, TypeScript with a local application and SQLite. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. - A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product.
# Delivery milestones · Recall Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Sources saved whole What you read is stored as text, not as a link. ### Steps 1. Store extracted text at capture Fetching later means a page that changed or vanished. Files: `src/capture.ts` 2. Capture author and date where present A claim's provenance depends on both. Files: `src/extract.ts` 3. Ask for nothing at capture Any field at capture time is a decision, and decisions end habits. Files: `src/capture.ts` ### Done when - [ ] Sources store extracted text at capture - [ ] A changed page still shows what was captured - [ ] Documents and logged-in pages capture correctly - [ ] Capture asks for nothing ### Watch out - Storing a link and fetching the content when needed means a source that changed or disappeared takes your notes and summaries with it, and the failure appears months later when the reference is most valuable. Extract and store the text at capture. ## M2 · The same thing named three ways Entity mentions across sources resolve to one node. ### Steps 1. Extract mentions with positions So a link can point at the sentence rather than the document. Files: `src/entities.ts` 2. Propose merges, never apply them Combining two people is not recoverable from the mentions. Files: `src/merge.ts` 3. Keep merges reversible With the decision recorded. You will get some wrong. Files: `src/merge.ts`, `tests/merge-reversal.test.ts` ### Done when - [ ] Variant surface forms resolve to one node - [ ] Merges are proposed, not applied - [ ] Merges are reversible with recorded decisions - [ ] Similar-named distinct entities are not merged ### Watch out - Merging entities automatically on name similarity combines two different people who share a surname, and afterwards there is no way to separate their mentions because the information about which was which is gone. Propose, require confirmation, and keep merges reversible. ## M3 · Links that mean something The graph is sparse enough to be useful. ### Steps 1. Threshold on strength Co-occurrence alone connects everything to everything. Files: `src/links.ts` 2. Type the relationship An untyped association is barely more useful than none. Files: `src/links.ts` 3. Report density and flag hubs Rising density is a defect, not coverage. Files: `src/graph-health.ts` ### Done when - [ ] Links require a strength threshold - [ ] Density is reported and hubs are flagged - [ ] Links carry a relationship type - [ ] Removing a source removes links it alone evidenced ### Watch out - Linking every pair of entities that co-occur in any source produces a graph where a common concept connects to everything, and a graph where everything is connected carries exactly as much information as one with no links. Threshold on strength, type the relationship, and treat rising density as a defect rather than as coverage. ## M4 · Summaries you can check Every summary line cites the text it came from. ### Steps 1. Require citations per line Validated against the stored text, not against the source URL. Files: `src/summarise.ts`, `src/validate.test.ts` 2. Reject rather than trim A summary with one invented line silently removed is still untrustworthy. Files: `src/summarise.ts` 3. Default to local processing Your reading history is a record of what you were thinking about. Files: `src/policy.ts` ### Done when - [ ] Every summary line cites resolvable text - [ ] An uncited summary is rejected - [ ] Citations navigate to the source position - [ ] Local processing is the default ### Watch out - A summary of a source you have not read is exactly the artefact people quote later without checking, and a model summarising a long article will confidently smooth over a qualification. Require a citation per line, validate that it resolves, and reject the whole summary rather than dropping the offending line. ## M5 · Finding your way back (production only) The graph helps you answer a question rather than looking impressive. ### Steps 1. Navigate from an entity outward A whole-graph view of thousands of nodes answers nothing. Files: `src/navigate.ts` 2. Search everything, not only titles You remember a phrase, not a heading. Files: `src/search.ts` 3. Prune weak links on a policy Density grows and legibility falls without it. Files: `src/prune.ts` ### Done when - [ ] Entity navigation is legible and useful - [ ] Search covers text, summaries and names - [ ] Weak links and orphans are pruned on a policy - [ ] Export round trips the whole graph ### Watch out - A graph visualisation of several thousand nodes looks impressive and answers no question, because at that size nothing is legible and nothing can be traced. Start from an entity and show its immediate neighbourhood, and treat a whole-graph view as a diagnostic rather than as the product.
# Operations · Recall ## Backup Sources and their captured text are the irreplaceable part. Entities, links and summaries are all derived and regenerable, so back up the sources first and treat the rest as a cache with opinions. ## Restore Restore and confirm entity merge decisions survived, since those are human judgements rather than derived data, and regenerating without them recreates every duplicate you already resolved. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Graph density and hub count are the health metrics. Both grow silently as sources accumulate, and a graph that has become dense is useless without ever having failed. ## Incident checklist A summary asserting something the source did not is the failure that matters, and it means citation validation passed something it should not have. The stored spans make it checkable rather than arguable. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Source text is confirmed stored at capture - [ ] Entity merges are confirmed proposed rather than automatic - [ ] Links are confirmed to require a strength threshold - [ ] An uncited summary is confirmed rejected - [ ] Entity navigation is confirmed legible at two hundred sources ## Launch constraint Do not market omitted Recall capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Sources, entities and links. A record of what you have been reading. DB_PATH=~/.local/share/recall/graph.db # Required. Runs over everything you save, so local is the sensible default. EXTRACTION_MODEL=local # Required. Below this no link is created. Unthresholded graphs connect everything. LINK_STRENGTH_THRESHOLD=0.6 # Required. An entity connecting to more than this is flagged as a hub rather than accepted. MAX_ENTITY_DEGREE=200 # Required. Present as a constant. Combining two people is not recoverable. AUTO_MERGE_ENTITIES=never # Required. Weak links and orphaned entities are pruned on a policy to keep the graph legible. PRUNE_AFTER_DAYS=365
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for Recall because people pay when capture works everywhere, reading state syncs instantly, and the archive remains clean without constant parser repairs. The recurring cost buys feed quirks, parsing, paywalls, canonical URLs, deduplication, images, search, sync, browser extensions, and publisher changes, not just the visible interface.
xhosted extraction, model routing, browser capture, sync, and knowledge-graph polish
xpublisher bypasses
xcross-device mobile capture
xhigh-quality recommendation graph
xlicensed full-text feeds
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Recall pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0 | $0 | 10 AI summaries/month and unlimited saved content; API and MCP access included. |
| plus | — | $10 | Unlimited AI summaries under fair use; import up to 1,000 bookmarks or 10,000 Markdown notes. |
| max | — | $38 | Unlimited summaries under fair use; bulk actions on up to 100 items and model choice. |
free tier10 AI summaries per month; unlimited saved content.
billingannual prices published with 20% savings; monthly selector exists but current monthly amounts were not independently recoverable; 30-day refund
hidden costsPaid 'unlimited' summaries remain subject to fair use and can be paused, reviewed or adjusted. Student pricing is 20% off where eligibility is accepted.
verified 2026-08-11 · source ↗
Is Recall free?
The free plan saves unlimited content but caps AI summaries at 10 a month. Paid is Plus at $10/mo (checked 2026-08-07).
Vibecode Recall
Kinda. The core of Recall is buildable in a weekend with the prompt on this page, but there are real gaps: hosted extraction, model routing, browser capture, sync, and knowledge-graph polish, publisher bypasses. Read the honest list above before committing.
How much does Recall cost?
Recall costs about $10/month (Plus, checked 2026-08-07), which is $120 per year.
What do I lose by replacing Recall?
Honestly: hosted extraction, model routing, browser capture, sync, and knowledge-graph polish; publisher bypasses; cross-device mobile capture; high-quality recommendation graph; licensed full-text feeds. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Recall?
Yes: Atomic (Saved sources become summaries and a linked semantic graph; bring your own model.) The prompt is for when you want it exactly your way.