Vibecode Scholarcy
track this build4 phases, 12 steps, beginner friendly0%The visible paper summarization loop is buildable, but a credible replacement needs more than the first screen. Scholarcy earns its keep through data, import reliability, so expect a weekend or multi-day build and a narrower personal scope.
You are building a lean indie version of Scholarcy. 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 ===== # Scholarcy · indie build A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. 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 | | --- | --- | --- | | Extraction | Layout-aware parsing with section and reference detection | a paper is a structured document and flattening it loses the structure | | Structure | Sections, figures, tables and references as separate objects | the methods section and the discussion make different kinds of claim | | Claims | Extracted with a quoted source span and its location | a claim you cannot quote is a claim you should not make | | Qualifiers | Sample size, population and hedging as structured fields | these are what paraphrase destroys first | | Runtime | Python 3.12 with local processing | unpublished and licensed papers should not be uploaded anywhere | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs extraction and processing. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A layout-aware PDF library** · free - Why: Reading order across columns is the first problem and a plain text extractor has already lost the information needed to solve it. - Get it: A library exposing text with coordinates and block structure. - Verify: A two-column paper extracts with per-block positions - [ ] **Ten papers you have the right to process** · free for open access - Why: Extraction, citation parsing and section detection all break differently across venues, and a single publisher's template proves nothing. - Get it: Papers from at least four venues, including one with unusual section headings, one heavy with tables, and one animal study. - Verify: Ten papers from four or more venues are available locally - [ ] **A local model for claim extraction** · free, needs memory - Why: Papers may be licensed or unpublished, and uploading a colleague's preprint to a service is not a decision to make casually. - Get it: A local runner with a model that returns structured output reliably for extraction tasks. - Verify: Ten extractions return valid structured output with quotes - [ ] **Three papers you know well** · free - Why: Summary honesty cannot be evaluated on papers you have not read, and the failure mode is a summary that sounds right and drops a qualification. - Get it: Pick three you could summarise yourself, including one whose main finding is heavily hedged. - Verify: You can state each paper's main finding and its qualifications before running the tool ## 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: - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. - licensed scholarly metadata - publisher-specific import reliability - citation graph scale - team libraries and institutional access If one of those is essential to you, that is the reason to keep paying for Scholarcy, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Scholarcy 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 paper summaries that quote the paper and never state a finding it did not make to replace Scholarcy. 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. ### Rule zero Summarising research is where a confident-sounding wrong sentence does the most damage, because the person reading it is deciding whether to trust a finding and often will not go back to the source. The specific failures are consistent: dropping the hedge, so a paper saying an association was observed in a small sample becomes a summary saying the thing causes the other thing; losing the population, so a result in mice reads as a result in people; and stating an effect size without its interval. So every claim in a summary quotes the sentence it came from and cites its location. A claim without a quotable source is not written. Hedging language, sample size and population are extracted as structured fields rather than left to survive paraphrase, and displayed with the finding rather than beneath it. This makes the summaries longer and less quotable than the alternative. That is the correct trade for a tool whose output is used to decide what to believe. ### Stack (fixed, do not substitute) - Extraction: Layout-aware parsing with section and reference detection · a paper is a structured document and flattening it loses the structure - Structure: Sections, figures, tables and references as separate objects · the methods section and the discussion make different kinds of claim - Claims: Extracted with a quoted source span and its location · a claim you cannot quote is a claim you should not make - Qualifiers: Sample size, population and hedging as structured fields · these are what paraphrase destroys first - Runtime: Python 3.12 with local processing · unpublished and licensed papers should not be uploaded anywhere ### Data model (create this before Phase 1) - papers(id, path, title, authors_json, year, doi, venue, extracted_at) - sections(id, paper_id, ordinal, kind, heading, text) · kind is abstract, methods, results, discussion or other - claims(id, paper_id, section_id, text, quote, char_start, char_end, claim_kind) · claim_kind is finding, method or limitation - qualifiers(claim_id, sample_size, population, hedging, effect_size, interval) · extracted, not paraphrased - references(id, paper_id, ordinal, raw, parsed_json, doi, resolved) · the bibliography, parsed - figures(id, paper_id, kind, caption, page, bbox_json, image_path) - cards(id, claim_id, front, back, created_at) · study cards, generated only from cited claims - A claim without a quote and a character range cannot be stored, which is enforced by the schema rather than checked afterwards. ### Phase 1 · Getting the text out of a paper Build: Extract with layout awareness, reconstructing reading order across columns and separating figure captions, footnotes and headers from body text. Detect section boundaries. Handle a paper that is a scan with no text layer. Trap: Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. Done when: A two-column paper extracts in the correct reading order, verified by reading a page of output. Figure captions and footnotes are separated from body text. Sections are detected including papers with unconventional headings. A scanned paper without a text layer is reported rather than producing empty output. Do not build yet: No claims, references or summaries. ### Phase 2 · References parsed and resolvable Build: Parse the reference list into structured entries across the citation styles you encounter. Resolve identifiers where present. Link in-text citations to their entries. Report entries that could not be parsed rather than dropping them. Trap: Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. Done when: References parse across at least three citation styles, tested against real papers from different venues. Unparsed entries are retained with their raw text and reported. In-text citations link to their reference entries. Identifiers are resolved where present and their absence is recorded rather than assumed. Do not build yet: No claims or summaries yet. ### Phase 3 · Claims that quote the paper Build: Extract claims with a quoted span and character range. Refuse to store a claim without one. Classify by whether it is a finding, a method or a stated limitation, since those are read very differently. Trap: A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. Done when: A claim cannot be stored without a quote and a character range, enforced by the schema. Every claim's quote resolves to the actual text at that position, asserted in the suite. Claims are classified as finding, method or limitation. A crafted claim citing a nonexistent span is rejected. Do not build yet: No qualifiers or summaries yet. ### Phase 4 · Keeping the hedges and the population Build: Extract sample size, population, hedging language and effect size with interval as structured fields per claim. Display them with the claim rather than below it. Flag a finding whose qualifiers could not be extracted. Trap: Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. Done when: Sample size, population and hedging are extracted as fields and shown with the claim. A finding in a non-human population is labelled as such in every display, tested against a real animal study. A claim whose qualifiers could not be extracted is marked incomplete rather than shown bare. Effect sizes appear with their intervals or not at all. Do not build yet: No summaries or cards yet. ### Phase 5 · Summaries and cards that stay honest Build: Generate summaries and study cards only from stored claims, each carrying its citation. Validate that every output line maps to a claim. Keep processing local and export with citations intact. Trap: Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. Done when: Every summary line maps to a stored claim, asserted by a validator that rejects unmapped output. Study cards carry their citation on the card. Processing runs with networking disabled. Export contains claims, quotes and citations so a summary can be checked against the paper. Do not build yet: Nothing after this. ### Out of scope (and why) - Any judgement of study quality, which requires domain expertise and is where an automated verdict would be most harmful. - Access to papers you do not have, which is a licensing matter rather than a technical one. - Cross-paper synthesis or meta-analysis, which is skilled work and where automated summarisation goes most badly wrong. - Replacing reading the paper. This is a tool for deciding what to read and for recalling what you read. ### README must contain - That a claim cannot be stored without a quote and a character range, enforced by the schema - That sample size, population and hedging are extracted as fields because paraphrase destroys them first - That a finding in a non-human population is labelled as such wherever it appears - That study cards are generated from claims rather than from summaries, so citations survive - That the tool is for deciding what to read, not for replacing reading it ===== AGENTS.md ===== # Agent instructions · Scholarcy indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Layout-aware parsing with section and reference detection, Sections, figures, tables and references as separate objects, Extracted with a quoted source span and its location, Sample size, population and hedging as structured fields, Python 3.12 with local processing. 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 - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ===== BUILD_PLAN.md ===== # Build plan · Scholarcy A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Getting the text out of a paper A two-column paper with figures parses into ordered sections. ### Steps 1. Reconstruct reading order across columns Crossing columns produces plausible nonsense that poisons everything after. Files: `src/extract.py`, `tests/test_reading_order.py` 2. Separate captions and footnotes They interrupt sentences and belong to different objects. Files: `src/structure.py` 3. Detect sections robustly Headings vary by venue. Fall back rather than failing. Files: `src/sections.py` ### Done when - [ ] A two-column paper reads in the correct order - [ ] Captions and footnotes are separated - [ ] Sections are detected across heading styles - [ ] A scan without text is reported, not empty ### Watch out - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. ## Phase 2 · References parsed and resolvable The bibliography becomes structured entries you can follow. ### Steps 1. Parse across several citation styles A parser tuned on one venue mangles another silently. Files: `src/references.py` 2. Keep unparsed entries visible A bibliography missing a third of itself looks complete. Files: `src/references.py` 3. Link in-text citations So a claim's support can be followed. Files: `src/citations.py` ### Done when - [ ] Three citation styles parse correctly - [ ] Unparsed entries are retained and reported - [ ] In-text citations link to entries - [ ] Identifier absence is recorded ### Watch out - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. ## Phase 3 · Claims that quote the paper Every extracted claim carries the sentence it came from. ### Steps 1. Require quote and position in the schema Not validated afterwards. Unstorable rather than discouraged. Files: `src/schema.sql` 2. Assert quotes resolve to the text In the suite, against the stored document. Files: `tests/test_quotes_resolve.py` 3. Classify claim kind A limitation and a finding are read very differently. Files: `src/claims.py` ### Done when - [ ] A claim without a quote cannot be stored - [ ] Every quote resolves to the actual text - [ ] Claims are classified by kind - [ ] A crafted bad span is rejected ### Watch out - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. ## Phase 4 · Keeping the hedges and the population A finding never loses the conditions it was found under. ### Steps 1. Extract qualifiers as structured fields Paraphrase destroys these first and silently. Files: `src/qualifiers.py` 2. Show them with the claim Alongside, not beneath. Position is what gets read. Files: `src/render.py` 3. Mark incompletely characterised claims Missing qualifiers is information, not something to hide. Files: `src/claims.py` ### Done when - [ ] Qualifiers are extracted as fields - [ ] A non-human population is labelled everywhere - [ ] Missing qualifiers mark the claim incomplete - [ ] Effect sizes appear with intervals or not at all ### Watch out - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ## Not in this build - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. ## After v1, if you want it - Figure and table extraction as data rather than images, which is where a lot of the findings live - A reading queue ordered by how much a paper's claims bear on your own question - Following a citation into a paper you also hold, which is where a library starts compounding ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Source documents, read only. PAPERS_DIR=./papers # Required. Extracted structure, claims and citations. DB_PATH=./library.db # Required. Local, since papers may be licensed or unpublished. EXTRACTION_MODEL=local # Required. Present as a constant. A claim you cannot quote is one you should not make. REQUIRE_QUOTE_PER_CLAIM=always # Required. Alongside the claim rather than beneath it. Position is what gets read. SHOW_QUALIFIERS_INLINE=always # Required. Present as a constant. Papers do not need to leave the machine. ALLOW_NETWORK=never
You are building a lean indie version of Scholarcy. 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 ===== # Scholarcy · indie build A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. 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 | | --- | --- | --- | | Extraction | Layout-aware parsing with section and reference detection | a paper is a structured document and flattening it loses the structure | | Structure | Sections, figures, tables and references as separate objects | the methods section and the discussion make different kinds of claim | | Claims | Extracted with a quoted source span and its location | a claim you cannot quote is a claim you should not make | | Qualifiers | Sample size, population and hedging as structured fields | these are what paraphrase destroys first | | Runtime | Python 3.12 with local processing | unpublished and licensed papers should not be uploaded anywhere | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs extraction and processing. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A layout-aware PDF library** · free - Why: Reading order across columns is the first problem and a plain text extractor has already lost the information needed to solve it. - Get it: A library exposing text with coordinates and block structure. - Verify: A two-column paper extracts with per-block positions - [ ] **Ten papers you have the right to process** · free for open access - Why: Extraction, citation parsing and section detection all break differently across venues, and a single publisher's template proves nothing. - Get it: Papers from at least four venues, including one with unusual section headings, one heavy with tables, and one animal study. - Verify: Ten papers from four or more venues are available locally - [ ] **A local model for claim extraction** · free, needs memory - Why: Papers may be licensed or unpublished, and uploading a colleague's preprint to a service is not a decision to make casually. - Get it: A local runner with a model that returns structured output reliably for extraction tasks. - Verify: Ten extractions return valid structured output with quotes - [ ] **Three papers you know well** · free - Why: Summary honesty cannot be evaluated on papers you have not read, and the failure mode is a summary that sounds right and drops a qualification. - Get it: Pick three you could summarise yourself, including one whose main finding is heavily hedged. - Verify: You can state each paper's main finding and its qualifications before running the tool ## 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: - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. - licensed scholarly metadata - publisher-specific import reliability - citation graph scale - team libraries and institutional access If one of those is essential to you, that is the reason to keep paying for Scholarcy, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Scholarcy 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 paper summaries that quote the paper and never state a finding it did not make to replace Scholarcy. 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. ### Rule zero Summarising research is where a confident-sounding wrong sentence does the most damage, because the person reading it is deciding whether to trust a finding and often will not go back to the source. The specific failures are consistent: dropping the hedge, so a paper saying an association was observed in a small sample becomes a summary saying the thing causes the other thing; losing the population, so a result in mice reads as a result in people; and stating an effect size without its interval. So every claim in a summary quotes the sentence it came from and cites its location. A claim without a quotable source is not written. Hedging language, sample size and population are extracted as structured fields rather than left to survive paraphrase, and displayed with the finding rather than beneath it. This makes the summaries longer and less quotable than the alternative. That is the correct trade for a tool whose output is used to decide what to believe. ### Stack (fixed, do not substitute) - Extraction: Layout-aware parsing with section and reference detection · a paper is a structured document and flattening it loses the structure - Structure: Sections, figures, tables and references as separate objects · the methods section and the discussion make different kinds of claim - Claims: Extracted with a quoted source span and its location · a claim you cannot quote is a claim you should not make - Qualifiers: Sample size, population and hedging as structured fields · these are what paraphrase destroys first - Runtime: Python 3.12 with local processing · unpublished and licensed papers should not be uploaded anywhere ### Data model (create this before Phase 1) - papers(id, path, title, authors_json, year, doi, venue, extracted_at) - sections(id, paper_id, ordinal, kind, heading, text) · kind is abstract, methods, results, discussion or other - claims(id, paper_id, section_id, text, quote, char_start, char_end, claim_kind) · claim_kind is finding, method or limitation - qualifiers(claim_id, sample_size, population, hedging, effect_size, interval) · extracted, not paraphrased - references(id, paper_id, ordinal, raw, parsed_json, doi, resolved) · the bibliography, parsed - figures(id, paper_id, kind, caption, page, bbox_json, image_path) - cards(id, claim_id, front, back, created_at) · study cards, generated only from cited claims - A claim without a quote and a character range cannot be stored, which is enforced by the schema rather than checked afterwards. ### Phase 1 · Getting the text out of a paper Build: Extract with layout awareness, reconstructing reading order across columns and separating figure captions, footnotes and headers from body text. Detect section boundaries. Handle a paper that is a scan with no text layer. Trap: Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. Done when: A two-column paper extracts in the correct reading order, verified by reading a page of output. Figure captions and footnotes are separated from body text. Sections are detected including papers with unconventional headings. A scanned paper without a text layer is reported rather than producing empty output. Do not build yet: No claims, references or summaries. ### Phase 2 · References parsed and resolvable Build: Parse the reference list into structured entries across the citation styles you encounter. Resolve identifiers where present. Link in-text citations to their entries. Report entries that could not be parsed rather than dropping them. Trap: Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. Done when: References parse across at least three citation styles, tested against real papers from different venues. Unparsed entries are retained with their raw text and reported. In-text citations link to their reference entries. Identifiers are resolved where present and their absence is recorded rather than assumed. Do not build yet: No claims or summaries yet. ### Phase 3 · Claims that quote the paper Build: Extract claims with a quoted span and character range. Refuse to store a claim without one. Classify by whether it is a finding, a method or a stated limitation, since those are read very differently. Trap: A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. Done when: A claim cannot be stored without a quote and a character range, enforced by the schema. Every claim's quote resolves to the actual text at that position, asserted in the suite. Claims are classified as finding, method or limitation. A crafted claim citing a nonexistent span is rejected. Do not build yet: No qualifiers or summaries yet. ### Phase 4 · Keeping the hedges and the population Build: Extract sample size, population, hedging language and effect size with interval as structured fields per claim. Display them with the claim rather than below it. Flag a finding whose qualifiers could not be extracted. Trap: Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. Done when: Sample size, population and hedging are extracted as fields and shown with the claim. A finding in a non-human population is labelled as such in every display, tested against a real animal study. A claim whose qualifiers could not be extracted is marked incomplete rather than shown bare. Effect sizes appear with their intervals or not at all. Do not build yet: No summaries or cards yet. ### Phase 5 · Summaries and cards that stay honest Build: Generate summaries and study cards only from stored claims, each carrying its citation. Validate that every output line maps to a claim. Keep processing local and export with citations intact. Trap: Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. Done when: Every summary line maps to a stored claim, asserted by a validator that rejects unmapped output. Study cards carry their citation on the card. Processing runs with networking disabled. Export contains claims, quotes and citations so a summary can be checked against the paper. Do not build yet: Nothing after this. ### Out of scope (and why) - Any judgement of study quality, which requires domain expertise and is where an automated verdict would be most harmful. - Access to papers you do not have, which is a licensing matter rather than a technical one. - Cross-paper synthesis or meta-analysis, which is skilled work and where automated summarisation goes most badly wrong. - Replacing reading the paper. This is a tool for deciding what to read and for recalling what you read. ### README must contain - That a claim cannot be stored without a quote and a character range, enforced by the schema - That sample size, population and hedging are extracted as fields because paraphrase destroys them first - That a finding in a non-human population is labelled as such wherever it appears - That study cards are generated from claims rather than from summaries, so citations survive - That the tool is for deciding what to read, not for replacing reading it ===== AGENTS.md ===== # Agent instructions · Scholarcy indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Layout-aware parsing with section and reference detection, Sections, figures, tables and references as separate objects, Extracted with a quoted source span and its location, Sample size, population and hedging as structured fields, Python 3.12 with local processing. 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 - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ===== BUILD_PLAN.md ===== # Build plan · Scholarcy A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Getting the text out of a paper A two-column paper with figures parses into ordered sections. ### Steps 1. Reconstruct reading order across columns Crossing columns produces plausible nonsense that poisons everything after. Files: `src/extract.py`, `tests/test_reading_order.py` 2. Separate captions and footnotes They interrupt sentences and belong to different objects. Files: `src/structure.py` 3. Detect sections robustly Headings vary by venue. Fall back rather than failing. Files: `src/sections.py` ### Done when - [ ] A two-column paper reads in the correct order - [ ] Captions and footnotes are separated - [ ] Sections are detected across heading styles - [ ] A scan without text is reported, not empty ### Watch out - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. ## Phase 2 · References parsed and resolvable The bibliography becomes structured entries you can follow. ### Steps 1. Parse across several citation styles A parser tuned on one venue mangles another silently. Files: `src/references.py` 2. Keep unparsed entries visible A bibliography missing a third of itself looks complete. Files: `src/references.py` 3. Link in-text citations So a claim's support can be followed. Files: `src/citations.py` ### Done when - [ ] Three citation styles parse correctly - [ ] Unparsed entries are retained and reported - [ ] In-text citations link to entries - [ ] Identifier absence is recorded ### Watch out - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. ## Phase 3 · Claims that quote the paper Every extracted claim carries the sentence it came from. ### Steps 1. Require quote and position in the schema Not validated afterwards. Unstorable rather than discouraged. Files: `src/schema.sql` 2. Assert quotes resolve to the text In the suite, against the stored document. Files: `tests/test_quotes_resolve.py` 3. Classify claim kind A limitation and a finding are read very differently. Files: `src/claims.py` ### Done when - [ ] A claim without a quote cannot be stored - [ ] Every quote resolves to the actual text - [ ] Claims are classified by kind - [ ] A crafted bad span is rejected ### Watch out - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. ## Phase 4 · Keeping the hedges and the population A finding never loses the conditions it was found under. ### Steps 1. Extract qualifiers as structured fields Paraphrase destroys these first and silently. Files: `src/qualifiers.py` 2. Show them with the claim Alongside, not beneath. Position is what gets read. Files: `src/render.py` 3. Mark incompletely characterised claims Missing qualifiers is information, not something to hide. Files: `src/claims.py` ### Done when - [ ] Qualifiers are extracted as fields - [ ] A non-human population is labelled everywhere - [ ] Missing qualifiers mark the claim incomplete - [ ] Effect sizes appear with intervals or not at all ### Watch out - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ## Not in this build - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. ## After v1, if you want it - Figure and table extraction as data rather than images, which is where a lot of the findings live - A reading queue ordered by how much a paper's claims bear on your own question - Following a citation into a paper you also hold, which is where a library starts compounding ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Source documents, read only. PAPERS_DIR=./papers # Required. Extracted structure, claims and citations. DB_PATH=./library.db # Required. Local, since papers may be licensed or unpublished. EXTRACTION_MODEL=local # Required. Present as a constant. A claim you cannot quote is one you should not make. REQUIRE_QUOTE_PER_CLAIM=always # Required. Alongside the claim rather than beneath it. Position is what gets read. SHOW_QUALIFIERS_INLINE=always # Required. Present as a constant. Papers do not need to leave the machine. ALLOW_NETWORK=never
You are building a production product version of Scholarcy. 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 ===== # Scholarcy · product brief ## Problem The visible paper summarization loop is buildable, but a credible replacement needs more than the first screen. Scholarcy earns its keep through data, import reliability, so expect a weekend or multi-day build and a narrower personal scope. ## Product outcome A paper tool whose summaries can always be checked against the paper, and which never turns an association observed in forty mice into a statement about people. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - browser automation for user-authorized imports - optional OpenAI API key stored in .env ## Explicit non-goals for v1 - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. - licensed scholarly metadata - publisher-specific import reliability - citation graph scale - team libraries and institutional access ## Success criteria - A two-column paper is confirmed to extract in reading order - A claim without a quote is confirmed unstorable - Every quote is confirmed to resolve to the actual text - A non-human population is confirmed labelled everywhere - Every summary line is confirmed to map to a stored claim ===== BRIEF.md ===== # Build brief · Scholarcy 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 paper summaries that quote the paper and never state a finding it did not make to replace Scholarcy. 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. ### Rule zero Summarising research is where a confident-sounding wrong sentence does the most damage, because the person reading it is deciding whether to trust a finding and often will not go back to the source. The specific failures are consistent: dropping the hedge, so a paper saying an association was observed in a small sample becomes a summary saying the thing causes the other thing; losing the population, so a result in mice reads as a result in people; and stating an effect size without its interval. So every claim in a summary quotes the sentence it came from and cites its location. A claim without a quotable source is not written. Hedging language, sample size and population are extracted as structured fields rather than left to survive paraphrase, and displayed with the finding rather than beneath it. This makes the summaries longer and less quotable than the alternative. That is the correct trade for a tool whose output is used to decide what to believe. ### Stack (fixed, do not substitute) - Extraction: Layout-aware parsing with section and reference detection · a paper is a structured document and flattening it loses the structure - Structure: Sections, figures, tables and references as separate objects · the methods section and the discussion make different kinds of claim - Claims: Extracted with a quoted source span and its location · a claim you cannot quote is a claim you should not make - Qualifiers: Sample size, population and hedging as structured fields · these are what paraphrase destroys first - Runtime: Python 3.12 with local processing · unpublished and licensed papers should not be uploaded anywhere ### Data model (create this before Phase 1) - papers(id, path, title, authors_json, year, doi, venue, extracted_at) - sections(id, paper_id, ordinal, kind, heading, text) · kind is abstract, methods, results, discussion or other - claims(id, paper_id, section_id, text, quote, char_start, char_end, claim_kind) · claim_kind is finding, method or limitation - qualifiers(claim_id, sample_size, population, hedging, effect_size, interval) · extracted, not paraphrased - references(id, paper_id, ordinal, raw, parsed_json, doi, resolved) · the bibliography, parsed - figures(id, paper_id, kind, caption, page, bbox_json, image_path) - cards(id, claim_id, front, back, created_at) · study cards, generated only from cited claims - A claim without a quote and a character range cannot be stored, which is enforced by the schema rather than checked afterwards. ### Phase 1 · Getting the text out of a paper Build: Extract with layout awareness, reconstructing reading order across columns and separating figure captions, footnotes and headers from body text. Detect section boundaries. Handle a paper that is a scan with no text layer. Trap: Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. Done when: A two-column paper extracts in the correct reading order, verified by reading a page of output. Figure captions and footnotes are separated from body text. Sections are detected including papers with unconventional headings. A scanned paper without a text layer is reported rather than producing empty output. Do not build yet: No claims, references or summaries. ### Phase 2 · References parsed and resolvable Build: Parse the reference list into structured entries across the citation styles you encounter. Resolve identifiers where present. Link in-text citations to their entries. Report entries that could not be parsed rather than dropping them. Trap: Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. Done when: References parse across at least three citation styles, tested against real papers from different venues. Unparsed entries are retained with their raw text and reported. In-text citations link to their reference entries. Identifiers are resolved where present and their absence is recorded rather than assumed. Do not build yet: No claims or summaries yet. ### Phase 3 · Claims that quote the paper Build: Extract claims with a quoted span and character range. Refuse to store a claim without one. Classify by whether it is a finding, a method or a stated limitation, since those are read very differently. Trap: A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. Done when: A claim cannot be stored without a quote and a character range, enforced by the schema. Every claim's quote resolves to the actual text at that position, asserted in the suite. Claims are classified as finding, method or limitation. A crafted claim citing a nonexistent span is rejected. Do not build yet: No qualifiers or summaries yet. ### Phase 4 · Keeping the hedges and the population Build: Extract sample size, population, hedging language and effect size with interval as structured fields per claim. Display them with the claim rather than below it. Flag a finding whose qualifiers could not be extracted. Trap: Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. Done when: Sample size, population and hedging are extracted as fields and shown with the claim. A finding in a non-human population is labelled as such in every display, tested against a real animal study. A claim whose qualifiers could not be extracted is marked incomplete rather than shown bare. Effect sizes appear with their intervals or not at all. Do not build yet: No summaries or cards yet. ### Phase 5 · Summaries and cards that stay honest Build: Generate summaries and study cards only from stored claims, each carrying its citation. Validate that every output line maps to a claim. Keep processing local and export with citations intact. Trap: Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. Done when: Every summary line maps to a stored claim, asserted by a validator that rejects unmapped output. Study cards carry their citation on the card. Processing runs with networking disabled. Export contains claims, quotes and citations so a summary can be checked against the paper. Do not build yet: Nothing after this. ### Out of scope (and why) - Any judgement of study quality, which requires domain expertise and is where an automated verdict would be most harmful. - Access to papers you do not have, which is a licensing matter rather than a technical one. - Cross-paper synthesis or meta-analysis, which is skilled work and where automated summarisation goes most badly wrong. - Replacing reading the paper. This is a tool for deciding what to read and for recalling what you read. ### README must contain - That a claim cannot be stored without a quote and a character range, enforced by the schema - That sample size, population and hedging are extracted as fields because paraphrase destroys them first - That a finding in a non-human population is labelled as such wherever it appears - That study cards are generated from claims rather than from summaries, so citations survive - That the tool is for deciding what to read, not for replacing reading it ===== ARCHITECTURE.md ===== # Architecture · Scholarcy ## Stack | Part | Choice | Why | | --- | --- | --- | | Extraction | Layout-aware parsing with section and reference detection | a paper is a structured document and flattening it loses the structure | | Structure | Sections, figures, tables and references as separate objects | the methods section and the discussion make different kinds of claim | | Claims | Extracted with a quoted source span and its location | a claim you cannot quote is a claim you should not make | | Qualifiers | Sample size, population and hedging as structured fields | these are what paraphrase destroys first | | Runtime | Python 3.12 with local processing | unpublished and licensed papers should not be uploaded anywhere | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Extraction | Reading order, sections and figures. | Any library exposing positions. A plain text extractor cannot do this. | | References | Citation parsing with visible failures. | Any parser, provided unparsed entries are retained rather than dropped. | | Claims | Quoted, positioned claims. | Not swappable for unquoted paraphrase, which cannot be checked. | | Qualifiers | Sample, population and hedging as fields. | Any extractor, provided qualifiers are structured rather than left in prose. | | Output | Summaries and cards mapped to claims. | Any generator, provided every line maps back and validation rejects otherwise. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PAPERS_DIR` · required · Source documents, read only. - `DB_PATH` · required · Extracted structure, claims and citations. - `EXTRACTION_MODEL` · required · Local, since papers may be licensed or unpublished. - `REQUIRE_QUOTE_PER_CLAIM` · required · Present as a constant. A claim you cannot quote is one you should not make. - `SHOW_QUALIFIERS_INLINE` · required · Alongside the claim rather than beneath it. Position is what gets read. - `ALLOW_NETWORK` · required · Present as a constant. Papers do not need to leave the machine. ## 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 · Scholarcy product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Layout-aware parsing with section and reference detection, Sections, figures, tables and references as separate objects, Extracted with a quoted source span and its location, Sample size, population and hedging as structured fields, Python 3.12 with local processing. - 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 - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. - Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. ===== MILESTONES.md ===== # Delivery milestones · Scholarcy Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Getting the text out of a paper A two-column paper with figures parses into ordered sections. ### Steps 1. Reconstruct reading order across columns Crossing columns produces plausible nonsense that poisons everything after. Files: `src/extract.py`, `tests/test_reading_order.py` 2. Separate captions and footnotes They interrupt sentences and belong to different objects. Files: `src/structure.py` 3. Detect sections robustly Headings vary by venue. Fall back rather than failing. Files: `src/sections.py` ### Done when - [ ] A two-column paper reads in the correct order - [ ] Captions and footnotes are separated - [ ] Sections are detected across heading styles - [ ] A scan without text is reported, not empty ### Watch out - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. ## M2 · References parsed and resolvable The bibliography becomes structured entries you can follow. ### Steps 1. Parse across several citation styles A parser tuned on one venue mangles another silently. Files: `src/references.py` 2. Keep unparsed entries visible A bibliography missing a third of itself looks complete. Files: `src/references.py` 3. Link in-text citations So a claim's support can be followed. Files: `src/citations.py` ### Done when - [ ] Three citation styles parse correctly - [ ] Unparsed entries are retained and reported - [ ] In-text citations link to entries - [ ] Identifier absence is recorded ### Watch out - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. ## M3 · Claims that quote the paper Every extracted claim carries the sentence it came from. ### Steps 1. Require quote and position in the schema Not validated afterwards. Unstorable rather than discouraged. Files: `src/schema.sql` 2. Assert quotes resolve to the text In the suite, against the stored document. Files: `tests/test_quotes_resolve.py` 3. Classify claim kind A limitation and a finding are read very differently. Files: `src/claims.py` ### Done when - [ ] A claim without a quote cannot be stored - [ ] Every quote resolves to the actual text - [ ] Claims are classified by kind - [ ] A crafted bad span is rejected ### Watch out - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. ## M4 · Keeping the hedges and the population A finding never loses the conditions it was found under. ### Steps 1. Extract qualifiers as structured fields Paraphrase destroys these first and silently. Files: `src/qualifiers.py` 2. Show them with the claim Alongside, not beneath. Position is what gets read. Files: `src/render.py` 3. Mark incompletely characterised claims Missing qualifiers is information, not something to hide. Files: `src/claims.py` ### Done when - [ ] Qualifiers are extracted as fields - [ ] A non-human population is labelled everywhere - [ ] Missing qualifiers mark the claim incomplete - [ ] Effect sizes appear with intervals or not at all ### Watch out - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ## M5 · Summaries and cards that stay honest (production only) Every line of output can be traced back to the paper. ### Steps 1. Generate only from stored claims Never from the summary. Citations are lost one step at a time. Files: `src/summarise.py` 2. Validate every output line maps back Reject the whole output otherwise. Files: `src/validate.py`, `tests/test_mapping.py` 3. Carry citations onto cards A flashcard with no source is what gets believed years later. Files: `src/cards.py` ### Done when - [ ] Every summary line maps to a stored claim - [ ] Cards carry their citation - [ ] Processing works with networking disabled - [ ] Export lets a summary be checked against the paper ### Watch out - Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. ===== OPERATIONS.md ===== # Operations · Scholarcy ## Backup Extracted claims and their citations represent real reading time. Source papers are usually re-obtainable; the annotations and claim sets are not, so back those up. ## Restore Restore and confirm quotes still resolve against their papers, since a restore that loses the source documents leaves claims whose citations cannot be checked, which is the same as having no citations. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring The proportion of claims marked incompletely characterised is the useful signal: a rise usually means extraction quality dropped for a new venue's format rather than that papers got vaguer. ## Incident checklist A summary asserting something the paper did not is the failure this exists to prevent, and it means a claim was stored without a resolving quote or a qualifier was dropped. Both are checkable against the stored spans rather than being a matter of opinion. 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 - [ ] A two-column paper is confirmed to extract in reading order - [ ] A claim without a quote is confirmed unstorable - [ ] Every quote is confirmed to resolve to the actual text - [ ] A non-human population is confirmed labelled everywhere - [ ] Every summary line is confirmed to map to a stored claim ## Launch constraint Do not market omitted Scholarcy 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. Source documents, read only. PAPERS_DIR=./papers # Required. Extracted structure, claims and citations. DB_PATH=./library.db # Required. Local, since papers may be licensed or unpublished. EXTRACTION_MODEL=local # Required. Present as a constant. A claim you cannot quote is one you should not make. REQUIRE_QUOTE_PER_CLAIM=always # Required. Alongside the claim rather than beneath it. Position is what gets read. SHOW_QUALIFIERS_INLINE=always # Required. Present as a constant. Papers do not need to leave the machine. ALLOW_NETWORK=never
# Scholarcy · indie build A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. 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 | | --- | --- | --- | | Extraction | Layout-aware parsing with section and reference detection | a paper is a structured document and flattening it loses the structure | | Structure | Sections, figures, tables and references as separate objects | the methods section and the discussion make different kinds of claim | | Claims | Extracted with a quoted source span and its location | a claim you cannot quote is a claim you should not make | | Qualifiers | Sample size, population and hedging as structured fields | these are what paraphrase destroys first | | Runtime | Python 3.12 with local processing | unpublished and licensed papers should not be uploaded anywhere | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Runs extraction and processing. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A layout-aware PDF library** · free - Why: Reading order across columns is the first problem and a plain text extractor has already lost the information needed to solve it. - Get it: A library exposing text with coordinates and block structure. - Verify: A two-column paper extracts with per-block positions - [ ] **Ten papers you have the right to process** · free for open access - Why: Extraction, citation parsing and section detection all break differently across venues, and a single publisher's template proves nothing. - Get it: Papers from at least four venues, including one with unusual section headings, one heavy with tables, and one animal study. - Verify: Ten papers from four or more venues are available locally - [ ] **A local model for claim extraction** · free, needs memory - Why: Papers may be licensed or unpublished, and uploading a colleague's preprint to a service is not a decision to make casually. - Get it: A local runner with a model that returns structured output reliably for extraction tasks. - Verify: Ten extractions return valid structured output with quotes - [ ] **Three papers you know well** · free - Why: Summary honesty cannot be evaluated on papers you have not read, and the failure mode is a summary that sounds right and drops a qualification. - Get it: Pick three you could summarise yourself, including one whose main finding is heavily hedged. - Verify: You can state each paper's main finding and its qualifications before running the tool ## 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: - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. - licensed scholarly metadata - publisher-specific import reliability - citation graph scale - team libraries and institutional access If one of those is essential to you, that is the reason to keep paying for Scholarcy, and the README should say so rather than pretend.
# Build brief · Scholarcy 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 paper summaries that quote the paper and never state a finding it did not make to replace Scholarcy. 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. ### Rule zero Summarising research is where a confident-sounding wrong sentence does the most damage, because the person reading it is deciding whether to trust a finding and often will not go back to the source. The specific failures are consistent: dropping the hedge, so a paper saying an association was observed in a small sample becomes a summary saying the thing causes the other thing; losing the population, so a result in mice reads as a result in people; and stating an effect size without its interval. So every claim in a summary quotes the sentence it came from and cites its location. A claim without a quotable source is not written. Hedging language, sample size and population are extracted as structured fields rather than left to survive paraphrase, and displayed with the finding rather than beneath it. This makes the summaries longer and less quotable than the alternative. That is the correct trade for a tool whose output is used to decide what to believe. ### Stack (fixed, do not substitute) - Extraction: Layout-aware parsing with section and reference detection · a paper is a structured document and flattening it loses the structure - Structure: Sections, figures, tables and references as separate objects · the methods section and the discussion make different kinds of claim - Claims: Extracted with a quoted source span and its location · a claim you cannot quote is a claim you should not make - Qualifiers: Sample size, population and hedging as structured fields · these are what paraphrase destroys first - Runtime: Python 3.12 with local processing · unpublished and licensed papers should not be uploaded anywhere ### Data model (create this before Phase 1) - papers(id, path, title, authors_json, year, doi, venue, extracted_at) - sections(id, paper_id, ordinal, kind, heading, text) · kind is abstract, methods, results, discussion or other - claims(id, paper_id, section_id, text, quote, char_start, char_end, claim_kind) · claim_kind is finding, method or limitation - qualifiers(claim_id, sample_size, population, hedging, effect_size, interval) · extracted, not paraphrased - references(id, paper_id, ordinal, raw, parsed_json, doi, resolved) · the bibliography, parsed - figures(id, paper_id, kind, caption, page, bbox_json, image_path) - cards(id, claim_id, front, back, created_at) · study cards, generated only from cited claims - A claim without a quote and a character range cannot be stored, which is enforced by the schema rather than checked afterwards. ### Phase 1 · Getting the text out of a paper Build: Extract with layout awareness, reconstructing reading order across columns and separating figure captions, footnotes and headers from body text. Detect section boundaries. Handle a paper that is a scan with no text layer. Trap: Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. Done when: A two-column paper extracts in the correct reading order, verified by reading a page of output. Figure captions and footnotes are separated from body text. Sections are detected including papers with unconventional headings. A scanned paper without a text layer is reported rather than producing empty output. Do not build yet: No claims, references or summaries. ### Phase 2 · References parsed and resolvable Build: Parse the reference list into structured entries across the citation styles you encounter. Resolve identifiers where present. Link in-text citations to their entries. Report entries that could not be parsed rather than dropping them. Trap: Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. Done when: References parse across at least three citation styles, tested against real papers from different venues. Unparsed entries are retained with their raw text and reported. In-text citations link to their reference entries. Identifiers are resolved where present and their absence is recorded rather than assumed. Do not build yet: No claims or summaries yet. ### Phase 3 · Claims that quote the paper Build: Extract claims with a quoted span and character range. Refuse to store a claim without one. Classify by whether it is a finding, a method or a stated limitation, since those are read very differently. Trap: A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. Done when: A claim cannot be stored without a quote and a character range, enforced by the schema. Every claim's quote resolves to the actual text at that position, asserted in the suite. Claims are classified as finding, method or limitation. A crafted claim citing a nonexistent span is rejected. Do not build yet: No qualifiers or summaries yet. ### Phase 4 · Keeping the hedges and the population Build: Extract sample size, population, hedging language and effect size with interval as structured fields per claim. Display them with the claim rather than below it. Flag a finding whose qualifiers could not be extracted. Trap: Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. Done when: Sample size, population and hedging are extracted as fields and shown with the claim. A finding in a non-human population is labelled as such in every display, tested against a real animal study. A claim whose qualifiers could not be extracted is marked incomplete rather than shown bare. Effect sizes appear with their intervals or not at all. Do not build yet: No summaries or cards yet. ### Phase 5 · Summaries and cards that stay honest Build: Generate summaries and study cards only from stored claims, each carrying its citation. Validate that every output line maps to a claim. Keep processing local and export with citations intact. Trap: Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. Done when: Every summary line maps to a stored claim, asserted by a validator that rejects unmapped output. Study cards carry their citation on the card. Processing runs with networking disabled. Export contains claims, quotes and citations so a summary can be checked against the paper. Do not build yet: Nothing after this. ### Out of scope (and why) - Any judgement of study quality, which requires domain expertise and is where an automated verdict would be most harmful. - Access to papers you do not have, which is a licensing matter rather than a technical one. - Cross-paper synthesis or meta-analysis, which is skilled work and where automated summarisation goes most badly wrong. - Replacing reading the paper. This is a tool for deciding what to read and for recalling what you read. ### README must contain - That a claim cannot be stored without a quote and a character range, enforced by the schema - That sample size, population and hedging are extracted as fields because paraphrase destroys them first - That a finding in a non-human population is labelled as such wherever it appears - That study cards are generated from claims rather than from summaries, so citations survive - That the tool is for deciding what to read, not for replacing reading it
# Agent instructions · Scholarcy indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Layout-aware parsing with section and reference detection, Sections, figures, tables and references as separate objects, Extracted with a quoted source span and its location, Sample size, population and hedging as structured fields, Python 3.12 with local processing. 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 - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare.
# Build plan · Scholarcy A paper processing tool where extraction is the hard engineering and honesty is the hard design: parsing academic documents whose layout defeats naive extraction, pulling out references and figures correctly, and producing summaries whose every claim can be traced to a sentence. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Getting the text out of a paper A two-column paper with figures parses into ordered sections. ### Steps 1. Reconstruct reading order across columns Crossing columns produces plausible nonsense that poisons everything after. Files: `src/extract.py`, `tests/test_reading_order.py` 2. Separate captions and footnotes They interrupt sentences and belong to different objects. Files: `src/structure.py` 3. Detect sections robustly Headings vary by venue. Fall back rather than failing. Files: `src/sections.py` ### Done when - [ ] A two-column paper reads in the correct order - [ ] Captions and footnotes are separated - [ ] Sections are detected across heading styles - [ ] A scan without text is reported, not empty ### Watch out - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. ## Phase 2 · References parsed and resolvable The bibliography becomes structured entries you can follow. ### Steps 1. Parse across several citation styles A parser tuned on one venue mangles another silently. Files: `src/references.py` 2. Keep unparsed entries visible A bibliography missing a third of itself looks complete. Files: `src/references.py` 3. Link in-text citations So a claim's support can be followed. Files: `src/citations.py` ### Done when - [ ] Three citation styles parse correctly - [ ] Unparsed entries are retained and reported - [ ] In-text citations link to entries - [ ] Identifier absence is recorded ### Watch out - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. ## Phase 3 · Claims that quote the paper Every extracted claim carries the sentence it came from. ### Steps 1. Require quote and position in the schema Not validated afterwards. Unstorable rather than discouraged. Files: `src/schema.sql` 2. Assert quotes resolve to the text In the suite, against the stored document. Files: `tests/test_quotes_resolve.py` 3. Classify claim kind A limitation and a finding are read very differently. Files: `src/claims.py` ### Done when - [ ] A claim without a quote cannot be stored - [ ] Every quote resolves to the actual text - [ ] Claims are classified by kind - [ ] A crafted bad span is rejected ### Watch out - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. ## Phase 4 · Keeping the hedges and the population A finding never loses the conditions it was found under. ### Steps 1. Extract qualifiers as structured fields Paraphrase destroys these first and silently. Files: `src/qualifiers.py` 2. Show them with the claim Alongside, not beneath. Position is what gets read. Files: `src/render.py` 3. Mark incompletely characterised claims Missing qualifiers is information, not something to hide. Files: `src/claims.py` ### Done when - [ ] Qualifiers are extracted as fields - [ ] A non-human population is labelled everywhere - [ ] Missing qualifiers mark the claim incomplete - [ ] Effect sizes appear with intervals or not at all ### Watch out - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ## Not in this build - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. ## After v1, if you want it - Figure and table extraction as data rather than images, which is where a lot of the findings live - A reading queue ordered by how much a paper's claims bear on your own question - Following a citation into a paper you also hold, which is where a library starts compounding
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Source documents, read only. PAPERS_DIR=./papers # Required. Extracted structure, claims and citations. DB_PATH=./library.db # Required. Local, since papers may be licensed or unpublished. EXTRACTION_MODEL=local # Required. Present as a constant. A claim you cannot quote is one you should not make. REQUIRE_QUOTE_PER_CLAIM=always # Required. Alongside the claim rather than beneath it. Position is what gets read. SHOW_QUALIFIERS_INLINE=always # Required. Present as a constant. Papers do not need to leave the machine. ALLOW_NETWORK=never
# Scholarcy · product brief ## Problem The visible paper summarization loop is buildable, but a credible replacement needs more than the first screen. Scholarcy earns its keep through data, import reliability, so expect a weekend or multi-day build and a narrower personal scope. ## Product outcome A paper tool whose summaries can always be checked against the paper, and which never turns an association observed in forty mice into a statement about people. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - browser automation for user-authorized imports - optional OpenAI API key stored in .env ## Explicit non-goals for v1 - Any judgement of study quality. - Access to papers you do not have. - Cross-paper synthesis or meta-analysis. - Replacing reading the paper. - licensed scholarly metadata - publisher-specific import reliability - citation graph scale - team libraries and institutional access ## Success criteria - A two-column paper is confirmed to extract in reading order - A claim without a quote is confirmed unstorable - Every quote is confirmed to resolve to the actual text - A non-human population is confirmed labelled everywhere - Every summary line is confirmed to map to a stored claim
# Build brief · Scholarcy 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 paper summaries that quote the paper and never state a finding it did not make to replace Scholarcy. 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. ### Rule zero Summarising research is where a confident-sounding wrong sentence does the most damage, because the person reading it is deciding whether to trust a finding and often will not go back to the source. The specific failures are consistent: dropping the hedge, so a paper saying an association was observed in a small sample becomes a summary saying the thing causes the other thing; losing the population, so a result in mice reads as a result in people; and stating an effect size without its interval. So every claim in a summary quotes the sentence it came from and cites its location. A claim without a quotable source is not written. Hedging language, sample size and population are extracted as structured fields rather than left to survive paraphrase, and displayed with the finding rather than beneath it. This makes the summaries longer and less quotable than the alternative. That is the correct trade for a tool whose output is used to decide what to believe. ### Stack (fixed, do not substitute) - Extraction: Layout-aware parsing with section and reference detection · a paper is a structured document and flattening it loses the structure - Structure: Sections, figures, tables and references as separate objects · the methods section and the discussion make different kinds of claim - Claims: Extracted with a quoted source span and its location · a claim you cannot quote is a claim you should not make - Qualifiers: Sample size, population and hedging as structured fields · these are what paraphrase destroys first - Runtime: Python 3.12 with local processing · unpublished and licensed papers should not be uploaded anywhere ### Data model (create this before Phase 1) - papers(id, path, title, authors_json, year, doi, venue, extracted_at) - sections(id, paper_id, ordinal, kind, heading, text) · kind is abstract, methods, results, discussion or other - claims(id, paper_id, section_id, text, quote, char_start, char_end, claim_kind) · claim_kind is finding, method or limitation - qualifiers(claim_id, sample_size, population, hedging, effect_size, interval) · extracted, not paraphrased - references(id, paper_id, ordinal, raw, parsed_json, doi, resolved) · the bibliography, parsed - figures(id, paper_id, kind, caption, page, bbox_json, image_path) - cards(id, claim_id, front, back, created_at) · study cards, generated only from cited claims - A claim without a quote and a character range cannot be stored, which is enforced by the schema rather than checked afterwards. ### Phase 1 · Getting the text out of a paper Build: Extract with layout awareness, reconstructing reading order across columns and separating figure captions, footnotes and headers from body text. Detect section boundaries. Handle a paper that is a scan with no text layer. Trap: Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. Done when: A two-column paper extracts in the correct reading order, verified by reading a page of output. Figure captions and footnotes are separated from body text. Sections are detected including papers with unconventional headings. A scanned paper without a text layer is reported rather than producing empty output. Do not build yet: No claims, references or summaries. ### Phase 2 · References parsed and resolvable Build: Parse the reference list into structured entries across the citation styles you encounter. Resolve identifiers where present. Link in-text citations to their entries. Report entries that could not be parsed rather than dropping them. Trap: Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. Done when: References parse across at least three citation styles, tested against real papers from different venues. Unparsed entries are retained with their raw text and reported. In-text citations link to their reference entries. Identifiers are resolved where present and their absence is recorded rather than assumed. Do not build yet: No claims or summaries yet. ### Phase 3 · Claims that quote the paper Build: Extract claims with a quoted span and character range. Refuse to store a claim without one. Classify by whether it is a finding, a method or a stated limitation, since those are read very differently. Trap: A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. Done when: A claim cannot be stored without a quote and a character range, enforced by the schema. Every claim's quote resolves to the actual text at that position, asserted in the suite. Claims are classified as finding, method or limitation. A crafted claim citing a nonexistent span is rejected. Do not build yet: No qualifiers or summaries yet. ### Phase 4 · Keeping the hedges and the population Build: Extract sample size, population, hedging language and effect size with interval as structured fields per claim. Display them with the claim rather than below it. Flag a finding whose qualifiers could not be extracted. Trap: Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. Done when: Sample size, population and hedging are extracted as fields and shown with the claim. A finding in a non-human population is labelled as such in every display, tested against a real animal study. A claim whose qualifiers could not be extracted is marked incomplete rather than shown bare. Effect sizes appear with their intervals or not at all. Do not build yet: No summaries or cards yet. ### Phase 5 · Summaries and cards that stay honest Build: Generate summaries and study cards only from stored claims, each carrying its citation. Validate that every output line maps to a claim. Keep processing local and export with citations intact. Trap: Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself. Done when: Every summary line maps to a stored claim, asserted by a validator that rejects unmapped output. Study cards carry their citation on the card. Processing runs with networking disabled. Export contains claims, quotes and citations so a summary can be checked against the paper. Do not build yet: Nothing after this. ### Out of scope (and why) - Any judgement of study quality, which requires domain expertise and is where an automated verdict would be most harmful. - Access to papers you do not have, which is a licensing matter rather than a technical one. - Cross-paper synthesis or meta-analysis, which is skilled work and where automated summarisation goes most badly wrong. - Replacing reading the paper. This is a tool for deciding what to read and for recalling what you read. ### README must contain - That a claim cannot be stored without a quote and a character range, enforced by the schema - That sample size, population and hedging are extracted as fields because paraphrase destroys them first - That a finding in a non-human population is labelled as such wherever it appears - That study cards are generated from claims rather than from summaries, so citations survive - That the tool is for deciding what to read, not for replacing reading it
# Architecture · Scholarcy ## Stack | Part | Choice | Why | | --- | --- | --- | | Extraction | Layout-aware parsing with section and reference detection | a paper is a structured document and flattening it loses the structure | | Structure | Sections, figures, tables and references as separate objects | the methods section and the discussion make different kinds of claim | | Claims | Extracted with a quoted source span and its location | a claim you cannot quote is a claim you should not make | | Qualifiers | Sample size, population and hedging as structured fields | these are what paraphrase destroys first | | Runtime | Python 3.12 with local processing | unpublished and licensed papers should not be uploaded anywhere | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Extraction | Reading order, sections and figures. | Any library exposing positions. A plain text extractor cannot do this. | | References | Citation parsing with visible failures. | Any parser, provided unparsed entries are retained rather than dropped. | | Claims | Quoted, positioned claims. | Not swappable for unquoted paraphrase, which cannot be checked. | | Qualifiers | Sample, population and hedging as fields. | Any extractor, provided qualifiers are structured rather than left in prose. | | Output | Summaries and cards mapped to claims. | Any generator, provided every line maps back and validation rejects otherwise. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PAPERS_DIR` · required · Source documents, read only. - `DB_PATH` · required · Extracted structure, claims and citations. - `EXTRACTION_MODEL` · required · Local, since papers may be licensed or unpublished. - `REQUIRE_QUOTE_PER_CLAIM` · required · Present as a constant. A claim you cannot quote is one you should not make. - `SHOW_QUALIFIERS_INLINE` · required · Alongside the claim rather than beneath it. Position is what gets read. - `ALLOW_NETWORK` · required · Present as a constant. Papers do not need to leave the machine. ## 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 · Scholarcy product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Layout-aware parsing with section and reference detection, Sections, figures, tables and references as separate objects, Extracted with a quoted source span and its location, Sample size, population and hedging as structured fields, Python 3.12 with local processing. - 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 - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. - Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself.
# Delivery milestones · Scholarcy Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Getting the text out of a paper A two-column paper with figures parses into ordered sections. ### Steps 1. Reconstruct reading order across columns Crossing columns produces plausible nonsense that poisons everything after. Files: `src/extract.py`, `tests/test_reading_order.py` 2. Separate captions and footnotes They interrupt sentences and belong to different objects. Files: `src/structure.py` 3. Detect sections robustly Headings vary by venue. Fall back rather than failing. Files: `src/sections.py` ### Done when - [ ] A two-column paper reads in the correct order - [ ] Captions and footnotes are separated - [ ] Sections are detected across heading styles - [ ] A scan without text is reported, not empty ### Watch out - Naive extraction reads a two-column paper by crossing both columns on every line, producing text that is grammatically plausible and semantically scrambled, and every downstream step then operates on nonsense. Reconstruct columns first and verify against a paper you can read. ## M2 · References parsed and resolvable The bibliography becomes structured entries you can follow. ### Steps 1. Parse across several citation styles A parser tuned on one venue mangles another silently. Files: `src/references.py` 2. Keep unparsed entries visible A bibliography missing a third of itself looks complete. Files: `src/references.py` 3. Link in-text citations So a claim's support can be followed. Files: `src/citations.py` ### Done when - [ ] Three citation styles parse correctly - [ ] Unparsed entries are retained and reported - [ ] In-text citations link to entries - [ ] Identifier absence is recorded ### Watch out - Citation styles vary enough that a parser tuned on one venue silently mangles another, and dropping the entries it cannot handle leaves a bibliography that looks complete and is missing a third of itself. Report unparsed entries with their raw text so the gap is visible. ## M3 · Claims that quote the paper Every extracted claim carries the sentence it came from. ### Steps 1. Require quote and position in the schema Not validated afterwards. Unstorable rather than discouraged. Files: `src/schema.sql` 2. Assert quotes resolve to the text In the suite, against the stored document. Files: `tests/test_quotes_resolve.py` 3. Classify claim kind A limitation and a finding are read very differently. Files: `src/claims.py` ### Done when - [ ] A claim without a quote cannot be stored - [ ] Every quote resolves to the actual text - [ ] Claims are classified by kind - [ ] A crafted bad span is rejected ### Watch out - A summary that paraphrases without quoting cannot be checked, and the paraphrase is exactly where hedging and qualification get lost. Store the quote and its position, and make an unquotable claim impossible to store rather than merely discouraged. ## M4 · Keeping the hedges and the population A finding never loses the conditions it was found under. ### Steps 1. Extract qualifiers as structured fields Paraphrase destroys these first and silently. Files: `src/qualifiers.py` 2. Show them with the claim Alongside, not beneath. Position is what gets read. Files: `src/render.py` 3. Mark incompletely characterised claims Missing qualifiers is information, not something to hide. Files: `src/claims.py` ### Done when - [ ] Qualifiers are extracted as fields - [ ] A non-human population is labelled everywhere - [ ] Missing qualifiers mark the claim incomplete - [ ] Effect sizes appear with intervals or not at all ### Watch out - Paraphrasing destroys hedging first, so an association observed in forty mice becomes a statement that the thing works, and the reader has no signal that anything was dropped. Extract the qualifiers as fields, show them alongside, and mark a claim whose qualifiers are missing as incompletely characterised rather than presenting it bare. ## M5 · Summaries and cards that stay honest (production only) Every line of output can be traced back to the paper. ### Steps 1. Generate only from stored claims Never from the summary. Citations are lost one step at a time. Files: `src/summarise.py` 2. Validate every output line maps back Reject the whole output otherwise. Files: `src/validate.py`, `tests/test_mapping.py` 3. Carry citations onto cards A flashcard with no source is what gets believed years later. Files: `src/cards.py` ### Done when - [ ] Every summary line maps to a stored claim - [ ] Cards carry their citation - [ ] Processing works with networking disabled - [ ] Export lets a summary be checked against the paper ### Watch out - Generating study cards from the summary rather than from the claims loses the citations one step at a time, and a flashcard asserting a finding with no source is exactly the artefact that ends up being believed years later. Generate from claims, and carry the citation onto the card itself.
# Operations · Scholarcy ## Backup Extracted claims and their citations represent real reading time. Source papers are usually re-obtainable; the annotations and claim sets are not, so back those up. ## Restore Restore and confirm quotes still resolve against their papers, since a restore that loses the source documents leaves claims whose citations cannot be checked, which is the same as having no citations. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring The proportion of claims marked incompletely characterised is the useful signal: a rise usually means extraction quality dropped for a new venue's format rather than that papers got vaguer. ## Incident checklist A summary asserting something the paper did not is the failure this exists to prevent, and it means a claim was stored without a resolving quote or a qualifier was dropped. Both are checkable against the stored spans rather than being a matter of opinion. 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 - [ ] A two-column paper is confirmed to extract in reading order - [ ] A claim without a quote is confirmed unstorable - [ ] Every quote is confirmed to resolve to the actual text - [ ] A non-human population is confirmed labelled everywhere - [ ] Every summary line is confirmed to map to a stored claim ## Launch constraint Do not market omitted Scholarcy 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. Source documents, read only. PAPERS_DIR=./papers # Required. Extracted structure, claims and citations. DB_PATH=./library.db # Required. Local, since papers may be licensed or unpublished. EXTRACTION_MODEL=local # Required. Present as a constant. A claim you cannot quote is one you should not make. REQUIRE_QUOTE_PER_CLAIM=always # Required. Alongside the claim rather than beneath it. Position is what gets read. SHOW_QUALIFIERS_INLINE=always # Required. Present as a constant. Papers do not need to leave the machine. ALLOW_NETWORK=never
$ choose a build depth, inspect the files, then open the complete pack in your agent
Scholarcy: Researchers pay for correct metadata, resilient importers, citation coverage, and workflows that survive publisher and browser changes.
xlicensed scholarly metadata
xpublisher-specific import reliability
xcitation graph scale
xteam libraries and institutional access
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Scholarcy pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free article summarizer | $0/user | $0/user | Up to 1 summary/day; live pricing card also states a 10-summary limit; flashcards export 1 at a time. |
| scholarcy plus monthly | custom | — | Unlimited summaries; export up to 100 flashcards at once. |
| scholarcy plus annual | custom | — | Unlimited summaries; export up to 100 flashcards at once. |
free tier1 summary/day; pricing card also says 10 summaries; export 1 flashcard at a time
billingmonthly + annual; annual advertised at 25% off; 7-day trial; exact live prices were not exposed
hidden coststhe official page contains an unresolved 1-per-day versus 10-summary free-limit ambiguity; Plus price widgets were blank
verified 2026-08-13 · source ↗
Vibecode Scholarcy
Kinda. The core of Scholarcy is buildable in a weekend with the prompt on this page, but there are real gaps: licensed scholarly metadata, publisher-specific import reliability. Read the honest list above before committing.
How much does Scholarcy cost?
Scholarcy's pricing is usage-based or varies by plan · The official pricing URL is recorded, but a stable current amount was not safely recoverable in this pass. Do not publish a number until rechecked..
What do I lose by replacing Scholarcy?
Honestly: licensed scholarly metadata; publisher-specific import reliability; citation graph scale; team libraries and institutional access. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Scholarcy?
Yes: NotebookLM (Turns papers into grounded summaries, reports, flashcards and quizzes within generous free limits.) The prompt is for when you want it exactly your way.