Workflows · phases 0–11
How the pieces move
Nine diagrams of what the commands actually do, drawn from the scripts under skills/ref-manager/scripts/. Command names in the diagrams link to their reference entries. Wide figures scroll sideways on small screens.
1 · Command map
The 40 commands fall into seven groups, and every group works through the same library folder on disk. Acquire fills it. Organize adds your project state and review decisions. The remaining groups mostly read from it and write their own derived outputs next to it.
/ref:extract claims feed find, write and graph. project, screening and saved-query state feeds the selectors used by write. /ref:verify rejections remove claims from retrieval and the graph. /ref:audit status changes show up when write artifacts are refreshed.2 · Ingest & commit
/ref:add never overwrites a paper and never lets one paper's failure block another. A per-PMID lock serializes all writes to a single paper. A library-wide lock is taken only for the short moment a citekey is allocated. Later stages add versioned content through one staging-then-swap routine.
/ref:add only resolves and deduplicates. It writes meta.json, authorship.json and funding.json directly, with atomic replacement. Versioned content comes later from /ref:fetch, /ref:attach and /ref:extract, each through commit_version. Readers only follow current.json, so a half-written staging directory is never seen. That is also why the SQLite catalog can always be thrown away and rebuilt (/ref:index --rebuild).3 · Full-text acquisition
fetch.py tries each paper's sources in a fixed order and stops at the first one that applies. The PubMed and publisher lookups are done by the command itself; the script calls only the Unpaywall API. Local PDFs go through /ref:attach, which refuses a file unless it can match the file to the paper.
/ref:fetch records an Unpaywall hit (oa_location_found) and stops there. It does not download the PDF: you download it and hand it to /ref:attach. The live PubMed full-text tool returns plain text, so it belongs in plain_text, not jats_xml. Section structure and figures are then lost, and a diagnostic says so. PDF conversion produces no figures.json entries. /ref:describe-figure can only describe figures whose image bytes and hash exist; its output is labelled as a model interpretation and stored next to the caption, never in place of it.4 · Extraction fan-out
/ref:extract starts one ref-extractor subagent per PMID, all in parallel. extract.py never sees the paper itself: it validates what each agent returned, assigns stable claim IDs and commits each paper on its own.
/ref:extract sets the evidence tier from what is committed: full if current.json points at a source.md, abstract otherwise. It also re-checks retraction status for each paper. When two candidates are ambiguous, a new ID is minted rather than guessing a supersession. A missed supersession can be fixed with /ref:verify; a wrong one would silently drop a real claim.5 · Human review overlays
Your review decisions live in corrections.json, outside the regeneration path. Re-running extraction never erases them. If the evidence a decision points at changes, the decision is flagged pending_review instead of being silently reapplied or dropped.
/ref:verify show re-checks the records against the claim registry before printing them, and so does every /ref:extract commit, which is how superseded targets become pending_review. Two limits in phases 0–11. First, a claim edit stores its replacement value, but no reader applies it to the claim; only reject changes what synthesis sees. Second, /ref:compare cells and a fresh /ref:summarize filter by status: active only, so a rejected claim can still appear there.6 · Selector resolution
Every set-based command takes the same selector grammar from lib_selector.py. It follows three steps: resolve the set, report what is in it, then do the work. The resolved PMID list is frozen into the artifact's manifest, so its membership can only change when you run an explicit --refresh, which then shows what changed.
/ref:compare, /ref:summarize, /ref:brief, /ref:export, /ref:ask and /ref:audit. Audit is the one exception to the rule that an empty selector is an error: with no selector it sweeps the whole library. /ref:brief stores the list as pmids_at_resolution. The tier report counts abstract, full, unavailable and missing_record. A bare PMID list and an equivalent --project selector produce identical comparison rows.7 · Grounded answer loop
/ref:ask works without embeddings. Claude suggests extra search terms, a lexical FTS5 search fills a fixed budget of evidence, one synthesizer writes the answer, and a script checks that every citation points at evidence the synthesizer was actually given.
/ref:ask answers once and keeps nothing. /ref:brief wraps the same four steps and saves the result as a snapshot under projects/<slug>/briefs/<key>/, or under briefs/<key>/ at the library root when you give no project. Refreshing is always explicit: nothing re-runs on a schedule. Citation validation only checks that each cited PMID was among the candidates given to the synthesizer. Whether the evidence actually supports the sentence is checked by /ref:check-citations.8 · Reading & screening state
Reading and screening state belong to one paper's membership in one project, not to the paper itself. Only /ref:project, /ref:queue and /ref:screen can change them. Fetching full text or extracting claims never marks a paper as read.
/ref:project add-paper can set a starting state, and /ref:queue set moves a paper to any of the four states, together with priority and a why-saved note. /ref:screen logs every decision and copies the latest onto the membership. These records feed the --read, --queue <state> and --screened selectors (figure 6), and the PRISMA counts in /ref:review --prisma.9 · Library maintenance
Maintenance never erases what the library already knows. A failed retraction check keeps the previous status. Citation counts are only ever appended. The SQLite catalog is a projection of the files that you can rebuild at any time.
/ref:audit changes a paper's status, it does not rewrite saved artifacts. propagate lists the tables/, summaries/ and briefs/ entries (including those inside projects) that cite that paper, and each one stays frozen until you refresh it. --citations is fully built, but no connected tool provides a real cited-by count yet, so the command says so rather than recording word-similarity results as citations. /ref:index --rebuild runs relation.py refresh, which flags edges for re-review and never clears a review decision. /ref:status reads only what the catalog currently holds.