# Changelog Release notes for Academic Harness. For releases before 0.18.0, see the [GitHub releases](https://github.com/raktim-mondol/academic-harness/releases). ## Unreleased ## 0.44.0 Phase 7 of [ScientistTwo → Academic Harness](SCIENTISTTWO-ROADMAP.md): the reviewer-response coverage ledger, shipped as a new tool with its own skill and workflow template. - **New `rebuttal` tool.** `rebuttal.js` + `rebuttal-store.js` under `.dsh/rebuttal//`: write-once `rebuttal.json` round definition; append-only `points.jsonl` (`p0001…`), `tasks.jsonl` (`t0001…`), `links.jsonl` (`l0001…`), `answers.jsonl` (`a0001…`), `declines.jsonl` (`d0001…`), and per-import provenance envelopes in `imports.jsonl`; `exports/` reports. Nine actions — `import`, `plan`, `link`, `respond`, `decline`, `coverage`, `draft`, `export`, `status`. It is an advisory coverage ledger with no execution of its own: no network call (all openreview data is caller-supplied), never runs or creates an experiment (it links runs that already exist, by id), never edits a manuscript, never writes reviewer prose, stores no numbers of its own, and gates no turn — only `ah_check` blocks. Mutating calls take the shared ledger lock and re-derive counters, dedupe, and the cap inside it; foreign-ledger reads happen before the lock; `coverage`, `draft`, and `status` are lock-free re-derivations. - **Three import sources, reviewer identity kept with each point.** A stored `deep_review` round (the latest prefers `review.json`'s full result arrays, older rounds read `rounds/.json`; the engine's own persisted match keys are reused, built per kind otherwise — question and claim keys are wording-based, never positional ids), `ah_review` items from an `ah.yaml` project (the engine stores no per-item person, so the reviewer is `unattributed`), or supplied openreview `reviews`/`reviews_path` — `forum_id` is refused because the tool never fetches. Every point carries its `reviewer` (`identity_source` `round_model`, `signature`, `note_id`, `invitation`, or `review_not_person`) and `evaluator_kind` with `declared`/`inferred`/`not_applicable` provenance. A re-import dedupes by source + native id and reports in-place payload changes as drift; history is never rewritten, and a non-creating import says out loud that the write-once definition did not take its metadata. Deep-review native ids are round-scoped, so a later round's positional points are stored as separate rows rather than merged into the first round's row. - **A capped plan replaces the paper's `N_t`.** `plan` stubs one supplementary task per point: dataset, one baseline arm (`reproduced` or `reported`, never mixed), metric with direction, replicate count from a supplied `power_analysis` result (stored verbatim plus two provenance keys) or a declared basis (`power_analysis`, `pilot`, `literature`, `budget`, `venue_requirement`, `author_judgement`), expected evidence type, and validation readings against the named `experiment_log` spec (recorded, not refused). Growth is capped by the new `REBUTTAL_MAX_TASKS` setting (default 20, integer 1–200; an invalid value falls back to 20 with a warning) and no argument overrides it — the paper's Rebuttal Planner instantiated `N_t` supplementary experiments and integrated their results with no performance gate at all; this tool never computes `N_t` and never instantiates one. - **Digest-pinned links.** `link` pairs a task with an existing `experiment_log` run (metrics read live by id, never copied) or a hashed `analysis_run` run plus the caller's `reading` of its hashed outputs. Each link pins a SHA-256 digest of the whole linked run event; `coverage` and `status` re-read it as `verified`, `drifted`, or `unreadable`. - **Five coverage states, and coverage is not outcome.** `answered`, `answered_asserted`, `declined_verified`, `declined_recorded`, `unaddressed`. `respond` records the author's own answer citing link ids and `manuscript_diff` change ids pinned to their recorded version pair; an answer whose citations resolve nowhere stays `answered_asserted` — unresolved is not unanswered. `decline` records a decline the author already made: with `ah_item` it is verified against the engine's live `declined` status (run `ah_author action=decline` first), without one it is stored as an author-recorded decline, always labelled unverified; the tool never declines anything itself. A linked run with a `bad`/`engineer` verdict, a `spec_drift` state, a drifted digest, or a non-zero exit appears beside the point it was run for and never reduces coverage. - **`draft` builds the handoffs; `coverage` closes the loop.** The letter skeleton (markdown or latex; the full text is kept under `drafts/` and may also be written to a caller-supplied `out`) cites only recorded hunks and linked artifacts, lists `unresolved` ids and `EVIDENCE STALE` flags, returns the `manuscript_diff` `items` array (each item starts `open` or `declined`; a recorded response becomes `addressed` only where a hunk resolves and matches its pinned version pair, `rebuttal` otherwise; grouped per revision slug) and the `ah_review_respond` handoff for engine items. With a fresh `deep_review` round, `coverage` pairs the deep-review-sourced points against it and reports `closed`, `still_present`, or `variance_only`; points from the other sources read `not_compared` because there is no matching engine round. A closed finding is a lead, not proof, and identical manuscript bytes make every movement reviewer variance. Inside an `ah.yaml` project the engine remains authoritative for item status and the response letter; the ledger's coverage is its own reading, and a divergence surfaces as a warning, never a silent reconciliation. `export` writes `report.md`, `rebuttal.json`, or `coverage.csv`. - **OpenReview passthrough.** `openreview.js` review records now additively pass `signatures`, `questions`, and `forum` through — the reviewer identity, question field, and forum binding the ledger imports; absent stays absent (`[]`/`null`, never inferred). - **Skill and workflow.** New `rebuttal-experiments` skill (`skills/rebuttal-experiments`) carrying the import → plan → link → respond → coverage → draft → re-review loop and the ledger rules; `rebuttal-guidance` stays the strategy sibling — it supplies per-concern plans, this ledger keeps the evidence. `workflows.json` gains the `rebuttal-plan` template (`{slug}`, `{source}` placeholders, `requiresFiles: false`); `prompt.js` carries one Submission-and-communication paragraph with the coverage-not-outcome and engine-authority wording. - Tests: `scripts/test-rebuttal-store.mjs` and `scripts/test-rebuttal-tool.mjs`. Docs: [Writing](WRITING.md#reviewer-response-ledger), the TOOLS row, CONFIGURATION (the new setting), ARCHITECTURE (state row and component map), [Skills](SKILLS.md), the roadmap Phase 7 record, and the capability diagram; skill count 767 → 768. ## 0.43.1 - **Fix `deep_research` run ordering.** `listRuns` now breaks identical `created_at` timestamps by descending run id, so `runs` and `status` reliably report the newest run first even when two runs start in the same millisecond. This was exposed by CI, where the previous tie fell back to directory order. ## 0.43.0 Phases 3–6 of [ScientistTwo → Academic Harness](SCIENTISTTWO-ROADMAP.md): the advisory review score with evaluator provenance, the staged experiment contract, the ablation ledger, and limitation, trace, selection, and grounded-novelty records on the hypothesis tournament. - **New `score` action on `deep_review`.** One bounded model call reads a stored review round and returns advisory `soundness`, `originality`, `clarity`, and `significance` readings on a declared numeric scale, each citing one to four stored comment ids. A dimension with no numeric reading or no valid comment id is `not_checked`, never guessed. The overall reading is the model's score or the mean of the scored dimensions (source recorded), with confidence and limitations. The scale and rubric source (`caller`, `setting`, or `default`) are recorded; no acceptance decision, venue outcome, or gate is produced. - **No model route is not a pass.** A session without the host LLM service, a failed call, or a non-JSON reply still completes and stores `score.json` with `state: not_checked` and the reason. `show section=score` and `status` read it back; `score.json` (`schemaVersion` 1) and `meta.score` are new artifacts on the existing v2 store, with the same v1 materialization as `validity`. - **Evaluator provenance per round.** Every review round records `evaluator_kind: held_out | in_distribution`. The default is `held_out`; pass `rubric_round` on `review` when a prior round's rubric or report fed the revision, and the next round is recorded `in_distribution` with the referenced round. Old rounds and v1 stores are read as `held_out` with `evaluator_kind_source: inferred`. The score records the selected round's kind, and `show rounds`, `compare`, and `status` expose it. - **Settings.** `DEEP_REVIEW_DEFAULT_RUBRIC` sets the score rubric and its declared scale; `DEEP_REVIEW_MAX_ROUNDS` refuses a review once that many rounds are stored; `DEEP_REVIEW_COST_CEILING_USD` checks the engine's median estimate before a review and refuses if it exceeds the ceiling. All three are read per call, default to no restriction, and are editable in Settings → Academic Harness API keys. - **Phase 0 score correlation.** `scripts/deep-review-calibration.mjs --report` now reads stored `score.json`, joins the overall score to accepted/rejected decisions, and reports point-biserial `r`, AUC, mean scores by bucket, evaluator-kind counts, and a separate held-out subset. It prints `not_checked` below the existing sample floor instead of a number the data cannot carry. `scripts/calibration-metrics.mjs` holds the dependency-free metrics and has an offline test. - **Wording.** `prompt.js`, `docs/PAPERS.md`, and `docs/WRITING.md` state the advisory posture explicitly: the score is an automated reading, not an acceptance decision, and nothing keys a gate off it. - Tests: `scripts/test-deep-review-tool.mjs` covers the score, citation filtering, evaluator provenance, round limit, cost ceiling, scale source, and store/show/status surfaces; `scripts/test-deep-review-score-correlation.mjs` tests the metrics; `scripts/test-deep-review-calibration-report.mjs` exercises the report end-to-end on synthetic scored stores. - **Phase 4: staged experimentation contract on `experiment_log`.** `start` now accepts a benchmark spec (`datasets`, `splits`, `baseline_arm`, `task_rules`) and an optional protocol (`eval_script` + `metric_params`); `log` accepts `role: baseline|variant`, `baseline_ref: reproduced|reported`, `stage: subset|slice|full`, and a sampling rule. The baseline captures `protocol_digest` (SHA-256 of the eval-script hash plus canonical metric parameters); every variant must name a matching baseline, and a changed script or parameter set is stored as `spec_drift` with expected/actual digests and changed components. `verdict` records an advisory human `good|engineer|bad` with reason, `engineering_round`, and budget; a budget-exhausted engineer is force-`bad`. `best dominance: true` returns the multi-metric Pareto front (incomplete runs are not ranked). `verdicts.jsonl`, stage/protocol fields, and drift are surfaced by `compare`, `show`, `status`, and the exports. `spec_drift` is now a sixth `provenance_audit` unsupported state, indexed from drifted experiment runs. Tests: `scripts/test-experiment-log.mjs` and `scripts/test-provenance-audit.mjs`. - **Phase 5: `ablation` tool.** New `ablation.js` + `ablation-store.js` under `.dsh/ablation//` with `start`, `plan`, `record`, `report`, `verdict`, `prune`, `show`, `status`, and `export`. `start` binds a method spec and champion idea id; `plan` freezes component × arm × dataset × metric arms with expected changes, datasets, metric directions, replicates, and a reference arm; `record` links a cell to an existing `experiment_log` run (metrics from that run) or an `analysis_run` with hashed outputs (the reading is supplied), so it never executes; `report` computes paired removal deltas against the reference, lists ties and missing cells, and gives an advisory attributed/unattributed/partial/not_checked reading; `verdict` records a human `good|refine|unattributed`; `prune` is append-only. It never claims a scientific result. Tests: `scripts/test-ablation-store.mjs` and `scripts/test-ablation-tool.mjs`; docs: ANALYSIS, TOOLS, and the `ablation-attribution` skill. - **Phase 6: limitation records, traced evolution, selection, and grounded novelty on the tournament.** `coscientist` gains `limitation` (`add`/`verify`/`status`), `trace`, and `select`; `add` accepts `addresses_limitation`. Limitation records follow the paper's published artifact — a flat numbered list carrying the methodological flaw, a **verbatim source quote span** verified against a hypothesis or workspace text file, why it limits the method, and **at least two remedy families**. Sufficiency is binary `sufficient`/`insufficient`, the loop is round-capped (default 16, `CO_SCIENTIST_LIMITATION_ROUNDS`), and a spent cap is reported `exhausted` rather than accepted. Refinement is set-append only: nothing is rewritten or deleted. Coverage by active ideas is exposed by `status`, `graph` (limitation nodes and `addresses` edges), and both exports. `trace` reads `experiment_log` runs — failed and pruned first — and an `evolution` dispatch from `next` carries the failed/pruned traces and their reasons as constraints. `select` freezes an active champion with its compared survivors, rationale, and a SHA-256 digest over the tournament readings, review scores, citation/novelty checks, and matching experiment traces; it is history, not an acceptance gate. `coscientist_verify novelty` retrieves a small reference set (default 2, the paper's only published number; max 10), stores it, computes a fully documented lexical prior-art reading (`10 × (1 − max token coverage)` — ours, because the paper gives no formula, range, or threshold), and pins it to verbatim quote spans; a failed retrieval is stored `not_checked` with the reason and no score. Tests: `scripts/test-coscientist-store.mjs`, `test-coscientist-tool.mjs`, `test-coscientist-verify.mjs`, and `test-nonsearch-tools.mjs`; docs: RESEARCH, TOOLS, CONFIGURATION, ARCHITECTURE, this changelog, the roadmap, `prompt.js`, and the `co-scientist-tournament` skill (loop, limitation/selection section, and briefs §13). ## 0.42.0 Phase 2 of [ScientistTwo → Academic Harness](SCIENTISTTWO-ROADMAP.md): the evaluation-validity criteria, landed wrapper-side as one new action. Nothing in `vendor/` moved, so `VENDOR_COMMIT` stays unpinned-dragged. - **New `validity` action on `deep_review`.** Four advisory evaluation-validity checks — `attribution`, `evaluator_drift`, `benchmark_coverage`, `unmeasured_trade_off` — run over one stored review round (`round` takes a round number or `run_id`, latest by default; `review.json` when that round is the latest and still stored, otherwise `rounds/.json`) plus a deterministic inventory of the manuscript itself: its text (from `.tex`/`.md`/`.txt`, or the stored `paper.md` for a PDF when its recorded source hash matches the current file, else a fresh conversion), its results grids parsed from LaTeX `table`/`tabular`/`array`/`longtable` bodies and Markdown pipe rows, and the numeric half from `numbers_audit`, whose own row counter is blind to multi-column grids and so is not reused for counting them. Coverage axes and arm labels are aggregated across every parsed grid, so a second table can widen a thin first one. The four criteria are the paper's own AutoSOTA failure cases; the vocabularies and the cut-offs are the plugin's, because the paper states neither. - **Three states, and none of them is a score.** `clear`, `flagged`, or `not_checked`, each carrying a reason string, line-level evidence, and tallies. A `flagged` state names an absence in the text, not a flaw in the work; `not_checked` is never a pass, and a manuscript that cannot be read leaves all four `not_checked` with the read error as the reason instead of a silent clean sheet. Counting is keyword and grid based: LaTeX prose is matched through a mask so comments, math, and commands cannot produce a hit, while the drift check reads comment-masked raw lines — a `\texttt` parameter change survives, a commented-out one does not. Markdown HTML comments and fenced code blocks are masked for every check. Inventory caps (400k characters, 40 grids, 60 rows per grid, 10 evidence items per check) are disclosed in the artifact. - **One bounded critic call, on by default.** With `critic: true` the pass makes at most one model call — temperature 0, 8,000 output tokens (live reasoning session model: 700 was spent entirely on thinking, and 4,000 ran out on two of three calls) — through the existing session route (`startLlmBridge`), so no new key, no new endpoint, no new Settings field. The reply is validated and attached only as annotation: `check.advisory`, `critic.counter_metrics`, `critic.generic_controls`. It cannot change a state, and a session with no model route, a failed call, or a non-JSON reply still completes the deterministic pass and records the critic `not_checked` with the reason. `critic: false` makes no model call. - **New artifact, new summary, no migration.** `extra_checks.json` (`schemaVersion` 1) is written beside the review, so it appears in `meta.artifacts`; `meta.validity` (`run_id`, `round`, `review_run_id`, `input_sha1`, `states`, `flagged_total`, `not_checked_total`, `critic_used`) is a new field on the existing `schemaVersion` 2 store, so nothing needs migrating and a pre-rounds v1 store is still read tolerantly — it is materialized into `rounds/` on this write rather than left half-upgraded, and `meta.params` keeps the review's params. A `validity` row lands in `runs.jsonl`, `status` reports the stored summary and whether it still matches the manuscript, `show section=validity` reads it back, and the pass appends its own notebook entry `deep-review-validity-` tagged `validity`. - **Two corrections to the roadmap's Phase 2 plan**, now recorded in [the roadmap](SCIENTISTTWO-ROADMAP.md). `paper_context` is not used for the local inventory: it takes an external identifier and does network tiering, so it cannot inventory the file under review. And the 4-figures/2-tables versus 9/12 coverage contrast the plan called "the paper's v1 contrast" is in the bundle a **ScientistOne-vs-ScientistTwo** comparison (`sections/4_experiment.tex:13`) whose counts are an author self-report including the appendix. - **Not wired yet.** `provenance_audit` does not read `extra_checks.json`, so these findings do not appear in a cross-ledger audit; it still joins only the stored `deep_review` claims and citation verdicts. Nothing here gates a turn — only `ah_check` blocks. - **Phase 0 validity calibration.** `scripts/deep-review-validity-calibration.mjs` (also `deep-review-calibration.mjs --validity --corpus`) runs the four checks over a labeled corpus, converting PDFs with the simple text converter (no OCR; OCR-flagged pages are disclosed, not processed) and reporting state agreement, flag rates, precision/recall, missed clears, abstentions, false alarms, false-positive papers, and determinism. `evaluateValidity` is exported so the harness and the action share one implementation; `scripts/fixtures/deep-review/validity/corpus.json` is the offline self-test, and a local 14-paper measurement corpus (the manifest and JSON report carry absolute paths and stay outside the package; the Markdown report is `reports/deep-review-validity-20261103T235613.md`). On that corpus: 14/14 converted, 0 false-positive papers, 0 false alarms on every check, 43/56 overall agreement, determinism mismatches 0; `attribution` 14/14, `evaluator_drift` 8/14 (one missed clear), `benchmark_coverage` 10/14 and `unmeasured_trade_off` 11/14 (no false alarms; abstentions dominate). The full Markdown report is `reports/deep-review-validity-20261103T235613.md`. - **Calibrated against real papers.** The corpus loop fixed four general failures: references/bibliography masked before counting (stopping at an appendix/supplementary heading), leading PDF line numbers stripped, coverage counting numeric value columns and treating any second arm as a comparator (with Identity/Grayscale/from-scratch baseline synonyms), and removal evidence requiring an ablation mention to carry a table/figure while a review/synthesis with no mechanism answers `not_checked`. Trade-off vocabularies gained `correct`, `avgbio`, `workload`, `reading time`, and a wider counter window; `HARNESS_PHRASES` dropped bare `shown to`/`displayed`. The first pass had 10 false-positive papers and 39.3% agreement; the final pass has 0 and 76.8%. - Tests: offline on fixtures modelled on the paper's four cases with `critic: false` — a five-of-six-metrics gain with EMA and label smoothing over a single `Accuracy` column flags `attribution` and `benchmark_coverage`; `act_threshold_frac 0.01 → 0.05` changing what the judge displays flags `evaluator_drift`; a solver-iteration and latency gain reported with speed only flags `unmeasured_trade_off`; a well-formed study with `w/o` arms, baseline and reference rows, three metric columns and three dataset columns, and a latency gain reported beside constraint violation and accuracy flags nothing. Identical bytes reproduce identical states, and a LaTeX comment mentioning latency and ablation is ignored while the monospace threshold change is still caught. `scripts/test-academic-harness.mjs` covers the prompt, skill, and preset surfaces changed here, and `node scripts/build-cordis-patch.mjs --check` the regenerated bundle patch; the committed cases are `scripts/test-deep-review-validity.mjs` (in `npm run test:deep-review`) over `scripts/fixtures/deep-review/validity/`, which adds the unreadable-format `not_checked` path, store-artifact and schema-version checks, and the critic's one-call, annotate-only, failure-tolerant behaviour. The suite also pins the regressions a live Web UI pass found: a commented-out drift, an HTML comment or fenced example table, `"we do not perform an ablation"` as an absence rather than removal evidence, a monospaced evaluator parameter with no harness word, a baseline-free grid beside a cost claim, the v1-store upgrade, the `round`/`run_id` selector, the orphan `rounds/` file a review after `validity` used to write, the stale-PDF conversion gate, and untrusted-tag escaping in evidence. `scripts/test-deep-review-validity-calibration.mjs` runs the corpus harness on the committed fixture corpus and asserts perfect agreement, zero false-positive papers, and zero determinism mismatches. Docs: [Papers](PAPERS.md#evaluation-validity-checks), [Tool index](TOOLS.md), [Architecture](ARCHITECTURE.md), [Development](DEVELOPMENT.md), [Skills](SKILLS.md), [Documentation home](README.md), [roadmap](SCIENTISTTWO-ROADMAP.md), `README.md`, the `deep-review` skill, `prompt.js`, and the preset's review guidance. ## 0.41.0 Phases 0 and 1 of [ScientistTwo → Academic Harness](SCIENTISTTWO-ROADMAP.md), which documents what the roadmap takes from the paper and what it deliberately does not. - **`deep_review` keeps review rounds.** Every `review` is now a numbered round: a finding digest in `rounds/.json` and its report in `rounds/.md`, while `review.json`/`review.md` keep holding the latest pass. Previously the key was derived from the manuscript path and a second review overwrote the only stored report, so a revision's effect could not be measured at all. `meta.json` is schema v2 (`rounds[]`, `latest_round`); a v1 store reads as one round without being rewritten, and the next `review` materializes that pass into `rounds/` first. - **New `compare` action.** Diffs two rounds — findings closed, opened, and persisting with severity moves, claim-evidence status changes, reproducibility-checklist transitions, and citation verdicts that cleared or newly tripped. Pairing runs by wording, then wording similarity (token overlap ≥ 0.6), then the quoted span; comment ids are tried first, but the engine re-issues them per run, so ids rarely match. `compare` prints a `Matched by` line naming which rule paired the findings, and states whether the manuscript bytes changed: with identical bytes, every difference is reviewer variance. Read-only, makes no model call, and reports readings rather than acceptance. - **Live end-to-end check.** Driven through the DSH Web UI against the real engine on a scratch manuscript: three rounds on one file, a revision between rounds 1 and 2, a re-review of unchanged bytes, and a hand-written v1 store read and then upgraded. The ladder is non-destructive, the v1 upgrade preserves the original report, and same-round / unknown-round / single-round compare errors surface verbatim. It also caught exact-wording pairing splitting a reworded finding into one closed and one opened — the reason the similarity tier and the `Matched by` line exist — and measured reviewer variance on an unchanged manuscript at 9 findings closed and 6 opened per pass. - **`status` and `show section=rounds`** expose the ladder with per-round counts, topology, model, and whether each round reviewed the current bytes. Each round records its own model-call and token tally, summed in `status`, so the cost of a review-revise-review loop is visible rather than assumed. - **Calibration harness.** `scripts/deep-review-calibration.mjs` adds `--collect` (real ratings and decisions through `openreview`; network, no model call) and `--report` (census of stored rounds; reviewer variance as finding overlap between two passes of an unchanged manuscript, and severity agreement across accepted versus rejected papers). Sections report `not_checked` below their sample floor. Not named `test-*.mjs`, because `npm test` globs that prefix and CI would inherit the network dependency. `deep_review` itself needs the host llm bridge, so reviews are produced in a session and measured afterwards. - **`image_generate` falls back to `mermaid_render` instead of dropping the figure.** When image generation is unconfigured or fails, the agent writes the figure as Mermaid and renders it with Pele, says in chat that it is a Mermaid fallback, and explains how to enable image generation. Real-data plots stay exempt — matplotlib, never a Mermaid stand-in. The tool description, its failure text, and the `visual` schematic template carry the same instruction. - Tests: `scripts/test-deep-review-tool.mjs` gains a two-round ladder (matching by wording after re-issued ids, citation clearing, checklist and claim transitions), a v1-store upgrade path, and a token-tally assertion; the fixture engine now emits a distinct second-round report. Docs: [Papers](PAPERS.md#deep-review), [Tool index](TOOLS.md), `README.md`, the `deep-review` skill, and the preset's review guidance. ## 0.40.0 Cleanup release. No tool or skill is removed; the tool list gains nine tools (previously opt-in) and loses two MCP aliases of tools that already exist natively. - **Optional tools are now always on.** `author_lookup`, `openreview`, `pdf_references` (still needs `GROBID_BASE_URL`), `coscientist_verify`, `stat_audit`, `figure_audit`, `repo_audit`, `grant_search`, and `patent_search` register without `AH_ENABLE_OPTIONAL_TOOLS`. The flag and `optional-tools.js` are gone; the tools have a new prompt section and a guide, [Identity, funding, and audit tools](IDENTITY-AND-AUDIT-TOOLS.md) (formerly `OPTIONAL-TOOLS.md`). - **Parallel MCP connector removed.** `mcp__parallel__web_search` (locked to `basic`) and `mcp__parallel__web_fetch` duplicated native `parallel_search` and `parallel_extract`, which share `PARALLEL_API_KEY` and support every mode. Firecrawl and Scite connectors are unchanged. A Parallel key now takes effect on the next call with no restart. - **ResearchCraft-era compatibility removed.** `scripts/migrate-profile.mjs`, `scripts/test-rename.mjs`, the `dsh-researchcraft` launcher alias, the `RESEARCHCRAFT_*` environment fallbacks, the 0.1.x/pre-0.16 API-key import in `settings-keys.js`, and the notebook-skill rename in `seed.js`. Upgrading from a pre-0.16 profile now needs a fresh install. Attribution in `NOTICE` and `LICENSE` is unchanged. - **Stale material removed:** eight finished plan documents under `docs/`, `academic-harness_docs/` (an old mod archive), `.e2e-artifacts/` screenshots, an unrelated run log in `reports/`, and the committed `main.pdf`/`main.bbl` build outputs of `paper/rsi-local/` (now git-ignored; `make` rebuilds them). Comments that pointed at the deleted plans were reworded. - **Tests and CI.** `npm test` (`scripts/run-tests.mjs`) runs every `scripts/test-*.mjs`; CI calls it, which adds the twelve suites CI never ran (the seven `test-coscientist-*`, `test-library`, `test-power`, `test-reference-library`, `test-rsi`, `test-zg-excludes`). `test-optional-tools.mjs` became `test-identity-tools.mjs`. CI installs the `dsh` CLI at `0.2.1-rc.5`, matching `package.json`. ## 0.39.0 - **Discovery and reference checks (Phase 7, partial).** Four preset tools and one `citation` action, none needing a key: `literature_watch`, `paper_recommend`, `citation_network`, `paper_artifacts`, and `citation` `action=audit`. Guide: [Discovery and reference checks](DISCOVERY.md); skills: `research-monitoring`, `citation-network`, `reference-audit`. The catalogue is now 766 skills. - `literature_watch` keeps saved searches in append-only ledgers under `.dsh/watch/`, runs them through the `literature_search` sources, and shows only records the watch has not seen. A failed source makes the run `partial` and is named; nothing is scheduled, emailed, or ingested. - `paper_recommend` merges Semantic Scholar recommendations and OpenAlex related works, removes the seeds and PaperMemory library duplicates, and states a measured reason for each result. - `citation_network` freezes an OpenAlex sample with its source, retrieval time, truncation flag, and a hash that analysis re-checks, then computes degree, PageRank, betweenness, components, coupling, co-citation, Louvain clusters, and shortest paths in dependency-free JavaScript, with GraphML, CSV, and Mermaid export. - `paper_artifacts` extracts repository links, accessions, and availability statements and checks links with HEAD requests to public hosts only (redirects re-checked, private addresses refused, capped). Accessions are never reported verified. - `citation` `action=audit` adds a batch reference audit: DOI validity, Crossref metadata match, retraction/concern/correction via `integrity_check`, duplicates, missing fields, uncited entries, and style fit, each with a suggested fix. A check that could not run is `unverified`, never clean. `privateHostname` and `privateAddress` in `citation.js` are now exported for reuse. - **Not built.** 7C trial registries and `clinicaltrials compliance`, 7D grey and regional literature sources, and 7H the `openfda` signal action; these need registry and API contracts that were not verified. Also deferred: local-library similarity in `paper_recommend`, license lookup in `paper_artifacts`, a notebook entry from digests, SVG export, and OpenCitations/Semantic Scholar sources for `citation_network`. The graph algorithms are pure JavaScript, so the plan's `networkx` helper is not used. - Found in live Web UI testing: `digest` and `path` returned `undefined` fields, which the host rejects as "not lossless JSON" (every new tool now normalises its result); a digest reported "no new records" for watches whose runs had failed (it now adds a run-health section); Semantic Scholar 404'd on arXiv-style DOIs (now sent as `ARXIV:` ids); and Zenodo links failed in Node although curl connected, because of Node's 250 ms per-address connect timeout (raised to 2 s for the link checker). Failed-source detail is kept in watch runs. - Live verification: driven through the DSH Web UI with Crossref, arXiv, Semantic Scholar, and OpenAlex (with a key, after the keyless daily budget was spent). Watch runs deduplicated (10 new, then 0; 5 new, then 0), the OpenAlex citation graph built 30 nodes and 77 edges with coherent clusters, and every tool returned valid output after the fixes above. - Tests: `scripts/test-discovery.mjs` (injected fetchers, no network), in `verify.yml` and `npm run test:discovery`. ## 0.38.0 - **Scientific data connectors and local toolkits (Phase 6).** Four preset-only tools, none needing a key: `science_source`, `chem_toolkit`, `seq_toolkit`, and `structure_toolkit`. Guide: [Scientific data and toolkits](SCIENCE-DATA.md); skill: `science-data`. The catalogue is now 763 skills. - `science_source` (`science-source.js`, adapters in `science-sources/`) is one multiplexed tool with `catalog`, `search`, `get`, `fetch`, and `resolve`. Adapters: UniProt, RCSB PDB, AlphaFold DB, PubChem. Every record carries `source`, `id`, `url`, `retrieved_at`, `license`, and the endpoint called. Failures are named states (`not_found`, `bad_request`, `rate_limited`, `provider_blocked`, `unavailable`, `not_supported`, `size_limit_exceeded`); an empty search says it is not proof of absence. `resolve` covers UniProt→PDB, UniProt→AlphaFold, and PubChem CID→InChIKey. `fetch` is https-only and limited to the adapter's own hosts, capped at 1 MB by default and 25 MB at most, and `save_to` writes inside the workspace with a hashed line in `.dsh/science/retrievals.jsonl`. RCSB details come from one batched GraphQL call. AlphaFold records are labelled predictions. Adapters are exported as `academic-harness/science-sources/*` for extension. - `chem_toolkit` (RDKit): `validate`, `standardize`, `descriptors`, `similarity`, `substructure`, `conformers`. `seq_toolkit` (pure Python, no new dependency): `stats`, `translate`, `orfs`, `primer_tm` (SantaLucia 1998 with sodium correction), `align` (global and local; BLOSUM62 when Biopython is installed, identity scoring otherwise, stated in the result). `structure_toolkit` (gemmi): `chains`, `ligands`, `contacts` (crystal-packing neighbours flagged), `superpose`. - Every toolkit run appends its tool, action, input SHA-256 hashes, and tool versions to `.dsh/science/runs.jsonl` (`science-toolkit-kit.js`); inputs are workspace-confined and size-limited, outputs are capped and say so, and absolute paths are not recorded. - **Not built.** `astro_toolkit` (optional in the plan) and the optional BLAST action. ChEMBL, NCBI Gene/Ensembl, GEO, and Reactome adapters are deferred; the plan asked for three to five adapters and four shipped. - Found by the tests: `conformers` failed when the output directory did not exist; a PubChem unknown CID returns an empty stub with HTTP 200 and is now `not_found`; a text file saved as `.pdb` parsed to a model with no atoms and is now an error. - Docs: prompt section `## Scientific databases and toolkits`, `docs/TOOLS.md` rows, `docs/SCIENCE-DATA.md`, preset rows, regenerated `cordis.patch.yml`. - Tests: `scripts/test-science-source.mjs` (115 checks, injected fetchers, no network) and `scripts/test-science-toolkits.mjs` (84 checks on synthetic inputs through the real helpers); both are in `verify.yml` and `npm run test:science`. ## 0.37.1 - **Remote compute survives transient failures.** Adapted in idea (not code) from `deepseek-ai/dsh-superagent-prototype`: separate "the provider or network could not answer right now" from "your request is wrong", and only retry the first kind. - `search-http.js` exports `isTransientNetworkError`, `isTransientStatus`, `isTransientError`, `isAuthStatus`, `withTransientRetry`, and `retryQuietly`. Transient means HTTP 408, 429, or 5xx (501 excluded as permanent), or a connection-level failure found by `code` anywhere in the `cause` chain, because undici wraps the real failure in `TypeError: fetch failed`. The HTTP client's fetch-throw path now retries only these, not every thrown error. - `runpod_run` and `vastai_run` SSH readiness polls tolerate transient blips until their deadline; 401/403 and 404 still fail on the first call. - `runpod_run`, `vastai_run`, and `modal_run` report `*_transient` (retry the same call), `*_auth` (permanent; fix the credential), or `*_failure`, with the verdict stated in the tool text. `modal_run` also maps gRPC `UNAVAILABLE`/`DEADLINE_EXCEEDED` to transient and `PERMISSION_DENIED`/`UNAUTHENTICATED` to auth. The four hand-rolled teardown loops now share `retryQuietly`; `modal_run` retries idempotent sandbox setup calls but never the user's command. - Prompt: one bullet of attention discipline (act on what was asked, stay quiet while working, speak at completion, a blocker, or a decision that is the user's). - Docs: `docs/COMPUTE.md` documents the error taxonomy and poll resilience. - **Verification.** `npm run test:compute` plus the search suites that share `search-http.js` pass. Live, in the web UI on the `academic-harness` profile: a real `modal_run` succeeded and a non-zero exit came back as a normal result; with `MODAL_SERVER_URL` pointed at a closed port Modal returned `UNAVAILABLE … ECONNREFUSED` and the tool said `TRANSIENT`; with a bad `MODAL_TOKEN_SECRET` it returned `UNAUTHENTICATED` and the tool said `PERMANENT`. - **Known limits.** Both live failures occur at Modal's `AuthTokenGet`, before the setup-retry wrapper, so the in-tool retry itself is covered by stubbed tests only. Against the prototype's own transient/permanent vectors the classifier agrees on 8 of 9; a plain `Error('getaddrinfo EAI_AGAIN …')` with no `code` is classed permanent, because this classifier reads `code` and two phrases rather than matching message text. Errors from real Node/undici carry the code and are caught. The Runpod and Vast.ai error paths were exercised only up to "not configured" in the live run. ## 0.37.0 - **Deep research (`deep_research`).** A self-driving, ledger-backed investigation tool, adapted in design from `jordan-gibbs/hyperresearch` (MIT; see `NOTICE`) and built entirely on this plugin's existing primitives — no vendored code, no second search client, no new runtime. State lives under `.dsh/deep-research//` as append-only JSONL plus write-once `query.md`/`manifest.json`. - `deep_research` (`deep-research.js`, `inject = ['tools','llm','jobs']`) drives the pipeline through the **session's own model route** via `ctx.llm.stream` (`text-delta`/`usage`/`finish`), inside a host job so a long run stays cancellable and reports step progress. Actions: `start`, `run`, `resume`, `status`, `verify`, `export`, `list`, `record`, `gate`, `patch`, `answers`. Retrieval reuses `literature_search`'s `chooseSources`/`runSource`/`mergeRanked` (now exported) and its RRF dedup; retraction flags reuse `integrity_check`; full text reuses `paper-download`'s Open Access resolution. A `deps` injection seam keeps every test offline. - **Two mechanisms the harness did not have.** `independence-cluster.js` clusters syndicated and derivative copies (DOI, exact/near title, identical body across hosts; **conflicting DOIs are never merged**) so a gate can count independent voices instead of copies. `untrusted-fence.js` wraps every retrieved body in `` with an explicit notice and escapes a payload's own fence tags, so a fetched page cannot end its fence and issue instructions. - **Structural enforcement is in the store, not the prompt.** The query is hashed at `start` and re-checked before every step (`query_drift`); a `blocked` step halts both `run` and `resume` until an explicit `from_step`; `applyHunks` refuses absent/ambiguous context, oversized hunks, and rewrite-sized patch budgets, escalating them instead; a patched artifact can never be regenerated; critic findings without `severity`/`target`/`primary_check`/`verifier`, duplicated on (`target`, `primary_check`), or resting on `unobserved` evidence are rejected with a named reason; `budget.max_llm_calls`/`max_sources`/`timeout_minutes` block rather than balloon. This is the Phase 9C finding shape applied to a reviewer tool, and `deep_research` is the first tool to emit it. - **Ship gate** (`verify`) reports `pass`/`blocked`/`not_checked` per check across query integrity, report presence, RQ coverage, quote integrity, retracted citations, independence, open High findings, patch-only compliance, numeric consistency, locus coverage, and hygiene — with a caveat naming whatever could not be verified. Quote integrity requires `fetch_full_text: true`, which caches bodies content-addressed per run; unverified is never reported as true. - Deviations from the upstream design: the light tier keeps the cite check (upstream skips it, which contradicts "verification never softens"); tiers are 5, 17, and chaptered steps rather than 16 numbered ones; there is no per-source reputation score, no vault (PaperMemory is the library of record), and no second report renderer. Fetched-text fencing and independence clustering are first-party. - Skills: `skills/deep-research/SKILL.md` gains a first-party "Running it as a tracked run" section, and `skills/deep-research/references/run-ledger.md` documents the ledger, step/gate ids, and error states. Catalogue count unchanged (762). - Docs: new prompt section `## Deep research`, `docs/TOOLS.md` row, `docs/RESEARCH.md#deep-research`, preset row `academic-harness/deep-research`, regenerated `cordis.patch.yml`. - **Web lane and PaperMemory integration.** The sweep now has two retrieval lanes instead of one, and fetched text joins the library of record. - **Web lane.** After the scholarly perspectives, the model proposes `web_queries` and they run through the plugin's existing keyed web clients — Parallel (one objective with up to five queries, which is the shape a research lane actually has), Exa, Tavily, Brave — for guidance documents, trial registers, and reporting that no literature index returns. Results are tagged `lane: web` and `work_type: web_page` so a press release is never weighed like a trial report, and `lane_coverage` in the ship gate reports which kinds of evidence actually arrived. Providers with no key are named as `key_missing`, never counted as retrieval failures. `web_sources` overrides the order, and the choice is frozen into `manifest.execution` like every other run option. To keep this honest rather than parallel-implemented, `runParallelSearch`, `runTavilySearch`, `runBraveSearch`, `runExaSearch`, and `runPerplexitySearch` were extracted from those tools' `execute` bodies (each tool now delegates, so there is one implementation) and `runSource` was exported from `literature-search.js`. - **PaperMemory.** `quoteCheck()` was added to `papermemory-cli.js` as a wrapper over `papermemory quote-check --paper --json`, and fetched open-access full text is deposited with the existing `ingestConvertedMarkdown()` under `originChannel: deep-research`, linked from its corpus row in the new append-only `papers.jsonl`. Quote verification now consults the library first and falls back to the run's own cache; a library that cannot be read is `not_checked`, never a failed quote. A failed deposit is recorded with its reason and the run keeps its copy. This closes the gap where a deep-research run read papers that never entered the library. - **One scale knob: `effort=low|medium|high`.** `deep_research` exposed tier, gear, `max_llm_calls`, `max_sources`, `timeout_minutes`, `fetch_full_text`, and `web_lane` — parameters a researcher has no reason to learn. `deep-research-effort.js` collapses them into three levels (`low`: light path, 8 calls, minutes, abstracts and metadata; `medium`: full pipeline, 45 calls, full text and the web lane; `high`: premier scale, 140 calls) and reads the level from the request when the caller does not name one. Explicit arguments still win, in this order: explicit argument > stated level > inference. - **Asks rather than guessing at cost.** A request pointing both ways ("quickly, but make it a comprehensive systematic review") returns `status: needs_input` with a one-line `ask_user` question and creates nothing — no run directory, no model call — until the caller answers. The prompt and skill tell the agent to put that question to the user verbatim and never to ask the user to learn `tier`/`gear` to get a normal run. - **The decision is recorded and frozen.** `manifest.execution` carries `effort`, `effort_source` (`stated` / `explicit-pipeline` / `inferred`), `effort_basis` (the phrases that decided), and any explicit overrides, so a resumed run never re-guesses and a reader can see what was chosen on their behalf. `status` and the prepared-run result report the level and what to expect from it. - Two precedence bugs found while testing this: an explicit `tier=full` inherited a guessed `low` budget and starved a full run at eight calls, and an explicit `tier=light` inherited `medium`'s settings and switched on full-text downloads nobody asked for. A level's cost posture now follows the pipeline the caller named. Shortness is also no longer treated as simplicity — only a short *question* is a lookup, so "describe what the evidence says about X" stays medium. - Live check of the ask path, and what it exposed: the agent followed the new guidance correctly — it left `effort` unset precisely so the tool would ask rather than pick a cost unilaterally — but it had already tidied *"Quickly, but make it a comprehensive systematic review of the entire field"* into a clean query, so the contradiction never reached the tool and the run started at `high` with no question asked. The `query` parameter description and the prompt now say plainly that the user's own words are the evidence for the level and must be passed unedited. This is guidance, not enforcement: nothing can stop a caller from paraphrasing away the signal, and rather than add a second parameter that could be tidied just the same, the limitation is stated where the caller will read it. - Tests: `scripts/test-deep-research-effort.mjs` (46 checks) covers the presets and their ordering, inference prose, the ambiguous-ask path including the nothing-was-created guarantee, stated/explicit/inferred precedence, override recording, and unknown-level refusal. Suite total is now 444 deep-research checks. - **Live verification of both integrations.** A real session swept 477 scholarly records plus a web lane that returned 10 pages from Parallel, with exa/tavily/brave named as skipped for want of a key rather than counted as failures. Four open-access full texts were fetched and deposited into PaperMemory (`pap_b4ffbb5dc3e3` for DOI 10.1056/NEJMoa2307563, plus Nature Medicine, Frontiers, and Diabetes Care papers), each linked from its corpus row in `papers.jsonl`. The first live web sweep returned nothing for one reason and one reason only: Parallel was not in the default provider list, so the lane consisted of three providers this install has no keys for. A direct authenticated call confirmed the response shape is `results` with rows of `{url, title, publish_date, excerpts}` — matching what `parallel_search`'s own renderer already reads, so that tool is fine and was left alone. The lane now reports an `empty_response` naming the keys it did see whenever a provider answers with no rows and no error, which is the failure mode worth making loud. - Tests: 398 deep-research checks (137 tool, 96 ledger, 42 mechanisms) covering both lanes, lane tagging, key-skipping, library deposit and failure, library-over-cache quote precedence, and the `lane_coverage` gate. `test-search-execute`, `test-search-normalize`, `test-academic-search-parity`, `test-domain-sources`, and `test-citation-format` confirm the five extracted clients still behave. - **Live DSH Web UI verification** (agent-browser against a real `academic-harness` profile session, on a clinical question with real retrieval). Seven defects surfaced that 197 offline checks had missed, all fixed with a regression test each: the host's `ctx.llm.stream` takes message `content` as **text blocks**, and plain strings failed every in-run model call — the unit fake had been more permissive than the real service, so the fakes now assert the host contract; the `run` flag was never declared in `parameters`, so `run=true` was silently dropped and the model had to call `run` separately; the light tier has no digest step, so `draft` was handed an empty digest and honestly reported "insufficient evidence" for a corpus of 495 records; `resolvePdfSources` yields `{candidate:{pdfUrl}}` and `fetchPdf` returns a **PDF buffer**, so the fetch path as first written could never read a paper and produced zero bodies; failed retrievals were invisible, leaving `not_checked` with no explanation; `fetch_full_text` was a per-call option that `resume` silently lost, so execution options are now frozen into `manifest.execution`; and `cite_check` marked bindings "partial" when *we* had never read the source, blocking a correct report for a publisher paywall — title-only records are now reported `title_only`/`not_checked` instead of challenged. A live agent also showed why stale findings matter: after a `from_step` restart the previous pass's open findings still blocked the gate, so a re-run now supersedes its own findings. - Live run outcome after the fixes: 432 records over 3 model-proposed perspectives, 428 independent voices, 12 full-text attempts of which 2 succeeded (NEJM, Lancet, JAMA, Wiley, OUP, AHA and ScienceDirect all returned HTTP 403 — recorded per record with the reason), `cite_check` `challenged=0 title_only=12`, all five light-tier steps `done`, and a report that named SELECT's design and secondary analyses while refusing to state a hazard ratio it could not read. Progress streamed to the jobs panel as `[NN%] [i/n step]` lines with per-perspective, per-locus, per-critic and per-model-call detail, including token counts. - Tests: `scripts/test-deep-research-mechanisms.mjs` (42 checks: fence breakout, forgery-statelessness, caps, DOI/title/normalisation, clustering, near-title corroboration, DOI-conflict veto), `scripts/test-deep-research-store.mjs` (85 checks: write-once query, drift, resume selection, cluster-on-read, finding rejection reasons, patch escalation and regeneration refusal, budget caps, gate pass/blocked/not_checked and caveat), `scripts/test-deep-research.mjs` (70 checks: registration surface, tier routing, the full 17-step tier end to end with a fake route and injected retrieval, blocked-run halting, export formats and clobber refusal). All three are wired into `verify.yml`; `test-nonsearch-tools.mjs` asserts the preset row and package wiring. No test reaches the network. - Found and fixed while testing: `Number(null) === 0` turned an unset budget cap into a zero cap that blocked every run at step one; a `blocked` step was being skipped rather than halting the run; a quote no verifier had read was counted as fabricated instead of unverified. ## 0.36.0 - **Phase 5 — study conduct, ethics, and grants.** Seven new preset-only tools. State lives under `.dsh/{preregister,causal,design,ethics,deidentify,dmp,budget}//`; none submits anywhere, invents an approval/rate/repository, or issues a compliance or causality verdict. - `preregister` (`preregister.js`, `preregister-store.js`) freezes a study plan into write-once, hashed versions: `start`, `import` (review protocol, coscientist design protocol, `analysis_run` plan, `causal_dag`, or workspace JSON; a stateless `power_analysis` result is supplied as an object), `freeze` (required fields; a data-collection guard requires a reason), `status`, `verify` (re-hashes every version and reports `verified`/`changed`/`tampered`), `export` (Markdown, JSON with OSF/AsPredicted-shaped fields, or PDF when LaTeX is available), and an explicitly disabled `submit`. No OSF call is made. Tests: `scripts/test-preregister.mjs`. - `causal_dag` (`causal-dag.js`, `causal-dag-store.js`, `causal-graph.js`) stores a user-specified DAG and derives d-separation, minimal backdoor and front-door sets, collider/M-bias and adjustment warnings, instrument candidates, topological order, local-Markov testable implications, and VanderWeele E-values. DOT/Mermaid/edge-list import and Mermaid/DOT/edge-list/JSON export; edits are versioned with a reason. No model is fitted and no effect is estimated. Tests: `scripts/test-causal-dag.mjs` (known DAG adjustment/front-door/instrument/latent/collider fixtures and E-value arithmetic). - `design` (`design.js`, `design-store.js`) generates reproducible schedules from a required seed: simple, permuted-block, stratified, cluster, crossover, and minimization randomization, allocation/blinding codes, realized-balance diagnostics (numeric SMD and categorical shares), full and fractional factorial, Plackett–Burman (8/12/16 runs), central composite, and Box–Behnken designs with defining relation/resolution, aliasing, and a randomized run sheet. Balance is reported, never guaranteed; DOE is a design, not an analysis. Tests: `scripts/test-design.mjs`. - `ethics_protocol` (`ethics-protocol.js`, `ethics-protocol-store.js`) keeps an append-only protocol, consent drafts, and a concept checklist (consent, risk/benefit, privacy, retention, sharing, compensation, incidental findings, vulnerable populations, GDPR/HIPAA, animal welfare, institutional requirements). It is not legal advice; approvals are recorded, never verified or invented, and manual items stay manual. Tests: `scripts/test-ethics-deidentify.mjs`. - `deidentify` (`deidentify.js`, `deidentify-store.js`) scans text/JSON/CSV/TSV for PHI/PII locally with regex/heuristics, stores only masked reports (custom terms are HMAC-hashed with a workspace-local `.dsh/deidentify/.term-key`), redacts to a separate file with per-type/per-column actions, and rescans the output for residual matches. Overlapping matches are merged, a file with more than 1000 candidate matches is refused rather than partially redacted, and `verify` detects an output changed after the redaction record. No model/network call, no reversible mapping, and no HIPAA/GDPR certification; a human must review. Tests cover synthetic PHI, CSV column actions, JSON, custom-term hash handling, residual verification, truncation refusal, and changed-output detection. - `dmp` (`dmp.js`, `dmp-store.js`) drafts NIH DMS, NSF DMP, Horizon Europe, Wellcome, ERC, and generic plans with append-only sections and a checklist; templates are paraphrased concepts, and repository/embargo/cost/official requirements stay `manual_check`. `grant_budget` (`grant-budget.js`, `grant-budget-store.js`, `python-helpers/grant_budget.py`) builds a multi-year budget from explicit salaries, effort/person-months, fringe, escalation, line items, subawards, cost share, indirect rate/caps, with arithmetic checks, a justification, staleness detection, and CSV/Markdown/JSON/XLSX export. It never invents a rate or decides allowability. Tests: `scripts/test-dmp-budget.mjs`. - Added five skills (`causal-dag`, `experimental-design`, `research-ethics`, `data-management-plan`, `grant-budget`; catalogue now 762), updated `preregistering-analysis`, new prompt section `## Study conduct, ethics, and grants`, `docs/RESEARCH.md` sections, `docs/TOOLS.md` rows, preset rows, regenerated `cordis.patch.yml`, `test:phase5`, and the five suites in CI. - **Live DSH Web UI verification.** A real Academic Harness agent session exercised all seven ledgers, exports (including LaTeX PDF and XLSX), and failure paths. The live run exposed two defects that unit tests had missed: block randomization put an in-memory `n: undefined` in `design.strata[]`, so DSH rejected the successful schedule as non-lossless JSON, and `design list` spread a joined string and printed one character per line. Both are fixed, causal-to-preregistration import no longer carries an undefined adjustment property, and `causal_dag create` validates before creating its ledger directory. - Deviations from the plan: causal graph algorithms, DOE construction, and de-identification are dependency-free JavaScript rather than `networkx`, `pyDOE3`, and Presidio helpers; OSF submission stays disabled (export JSON/Markdown for manual submission); `causal_dag` exports Mermaid/DOT/edge-list/JSON (GraphML and SVG were not added); PDF export is implemented for `preregister` only (via LaTeX), while DMP/ethics/budget export Markdown/JSON, budget also CSV/XLSX, and DOCX/PDF remain deferred; `provenance_audit` does not index the new preregister ledger yet and continues to report it as `unknown`. ## 0.35.0 - **Phase 4 — submission and communication.** Five new preset-only tools, one extended search tool, and custom CSL support. Nothing submits, emails, or logs into a journal portal; drafts stay drafts and unknown requirements stay `manual_check`. - `submission_pack` (`submission-pack.js`, `submission-store.js`) plans and builds a journal bundle under `.dsh/submission//`: word/abstract/reference/figure/table limits, figure format/DPI/color/size/naming, citation style and `manuscript_citations`, `reporting_check` evidence, `numbers_audit` candidates, required statements/sections, suggested reviewers, and a manuscript PDF; then copies/compiles the PDF and sources, figures, supplementary files, author statements, cover letter, highlights, checklist, and a SHA-256 manifest into a zip reproducible from that manifest. Cover letter/highlights are author-supplied or explicit drafts; reviewers are only user entries. `status` and `export` inspect/copy the result. Tests: `scripts/test-submission-pack.mjs`. - `journal_search` `action: "match"` ranks venues for a manuscript using the existing OpenAlex/DOAJ/Crossref pipeline plus keyword/topic/title overlap and returned metrics. It filters only known constraints (OA, max APC, indexation), reports missing data, labels fit advisory, and returns DOAJ listing, publisher membership, ISSN, APC transparency, and homepage as manual-check due-diligence signals. Custom CSL styles can now be loaded in `citation` with `style_file` or `style_url`; they are validated (CSL namespace/version/citation block, size/DTD limits), URL styles are cached under `.dsh/styles/`, and local/private hosts are refused. Tests: `scripts/test-journal-match.mjs`. - `manuscript_diff` (`manuscript-diff.js`) offers `diff` (latexdiff for `.tex`, unified diff for `.md`/`.txt`; `not_installed` when latexdiff is absent), `changes` (changed blocks with ids and source hashes), `docx` (tracked insertions/deletions, comments, Zotero/CSL field text; no rewrite), and `letter` (an `ah_review` id delegates to the engine; generic items must map to recorded change ids, and addressed items without a hunk are flagged/unresolved). Source files are never edited. Tests: `scripts/test-manuscript-diff.mjs`. - `numbers_audit` (`numbers-audit.js` plus a `numbers` mode in `python-helpers/stat_audit.py`) reports p-values recomputed from test statistics, out-of-range/zero p-values, reversed/zero-width CIs, estimates outside their CI, non-positive ratio measures, total-N inconsistency, subgroup/flow count problems, fraction/percentage mismatches, and label-matched table/text values. Everything it cannot recompute or match is listed as `not_checked`; it never claims statistical validity. Tests: `scripts/test-numbers-audit.mjs`. - `presentation_build` (`presentation-build.js`, `python-helpers/presentation_helper.py`) builds `tikzposter`/`beamerposter`/`baposter` posters and Beamer or python-pptx slide decks from explicit sections or a manuscript, copies only existing figures, compiles PDFs, writes PNG previews when `pdftoppm` exists, tracks missing figures/compile failures, and returns a visual-QA checklist for `subagent_vision`; it never claims the visual check passed. Tests: `scripts/test-presentation-build.mjs`. - `authorship` (`authorship.js`, `authorship-store.js`) records authors, CRediT roles, and ICMJE criteria in an append-only ledger under `.dsh/authorship//`, generates Markdown/JSON statements, and exports Markdown/CSV/JSON. It never assigns order or qualification, never invents affiliations/ORCIDs/funding/conflicts, and reports duplicate positions/ORCIDs and changed values as unresolved disagreements. Tests: `scripts/test-authorship.mjs`. - Five new skills (`submission-package`, `manuscript-revision`, `numbers-audit`, `presentation-build`, `authorship-credit`) bring the catalogue to 757; new prompt section `## Submission and communication`; new guide sections in `docs/WRITING.md`; preset rows and regenerated `cordis.patch.yml`; `npm run test:phase4` and the six suites added to CI. - Deviations recorded in `docs/SCIENTIFIC-EXPANSION-PLAN.md`: PowerPoint uses a small `python-pptx` helper (the slide skills ship no deck generator), match does not call `journal_policy` per result, generic letters use recorded change ids while `ah_review` delegates to the engine, and table/text matching is deliberately conservative; GRIMMER remains unimplemented. ## 0.34.0 - **Phase 0 foundations.** Added `ledger-kit.js` (stable JSON hashing, JSONL/write-once helpers, overwrite-safe text export), `provenance.js` (typed link/ID conventions), example schemas under `schemas/`, and a shared test workspace fixture. Existing ledgers are unchanged and do not import the new helpers yet. - **Structured extraction in `review`.** New `extract` and `extract_status` actions, a write-once extraction template (`generic`, `pico`, `peco`, `diagnostic_accuracy`, `prognostic`, `qualitative`, or inline), field-level `reconcile scope=extraction`, multi-row extraction via `row_id`, and `extraction-table`/`extraction-matrix`/`extraction-json` exports. Values are reviewer-supplied and recorded, never inferred; missing required fields are counted, and field-level quotes are checked through PaperMemory when `paper` is supplied. The Markdown report and JSON ledger include extraction output when a template is frozen. - **PRISMA flow export.** `review export format=prisma-flow` writes `prisma-flow.svg` and a counts JSON sidecar through a dependency-free `python-helpers/prisma_flow.py`. Counts come from the ledger; missing counts stay `not recorded`, and the diagram is labelled PRISMA-style, never a compliance statement. - **Pairwise meta-analysis (`meta_analysis`).** New preset-only ledger under `.dsh/meta//` with effect-size conversion (2×2, continuous, correlation, HR, diagnostic 2×2, generic effect+CI/SE), fixed/random pooling using DerSimonian–Laird, Paule–Mandel, or REML τ², normal or Hartung–Knapp CIs, Q/I²/H²/τ²/prediction intervals, subgroup analysis, numeric-covariate meta-regression, leave-one-out, Egger/Begg/funnel publication-bias diagnostics, univariate diagnostic-accuracy pooling, forest/funnel/SROC SVG plots, Markdown/CSV/JSON exports, and `import_review` from a completed extraction table. The protocol is write-once; every result echoes the model and warns below 5 studies or 10 studies for funnel tests. Trim-and-fill, network meta-analysis, dose-response, and bivariate/HSROC diagnostic models are explicitly not implemented in this release. The pairwise core is validated against the published BCG example (fixed, DL, PM, REML, subgroup Q-between) in `scripts/test-meta-analysis.mjs`. - **GRADE certainty and summary of findings (`review`).** New `grade_start`, `grade_judge`, `grade_overall`, and `grade_status` actions on the existing review ledger. Outcomes are write-once (`grade.json`); domain ratings and overall certainties are append-only (`grade.jsonl`). Downgrade domains are risk of bias, inconsistency, indirectness, imprecision, and publication bias; observational upgrade domains are large effect, dose-response, and plausible confounding. Every rating needs a reason; optional quotes can be verified through PaperMemory; dual reviewers produce missing-reviewer and conflict states. The tool derives a suggestion from the domains but stores the human overall certainty; differing overalls export as `conflict`. New `grade-profile` (Markdown/CSV) and `sof-table` (Markdown/CSV/LaTeX) exports. `scripts/test-grade.mjs` covers single/dual reviewers, conflicts, overrides, quote verification, and all export formats. - **More appraisal instruments.** The `review appraise` library grew from four templates to eleven: `amstar-2` (16 items, critical items flagged), `probast` (risk-of-bias plus applicability cells), `quips`, `syrcle`, and consolidated `casp-cohort`, `casp-case-control`, and `casp-qualitative` checklists. Each ships as a paraphrased, versioned JSON template under `appraisal/` linking to the official source; only RoB 2 and ROBINS-I derive an overall, matching the instruments' own guidance. Decisive ratings (checklist `yes`/`no`) require verbatim quotes like existing instruments; completeness, reconciliation, agreement, and the rob-table/rob-summary/rob-figure paths work unchanged. - **Phase 2 — reproducible analysis and data layer.** Five new preset-only tools. `analysis_run` freezes an analysis plan (question, inputs/outputs, methods, alpha, seed; changes need `new_version` plus a reason) and executes argv commands — never a shell — under an environment allowlist with explicit-env redaction, hashed inputs/outputs, a hard timeout, notebook execution via jupyter/papermill, run-vs-run and run-vs-plan diffs, lab-notebook entries, and Markdown/JSON run reports; a nonzero exit or timeout is recorded, not hidden. `experiment_log` registers sweeps and model comparisons as append-only runs (params, metrics, hashed artifacts, seeds, parents) with compare, best-by-named-metric-and-direction, links, and CSV/JSON/Markdown/MLflow-compatible export; missing metrics stay blank and reruns are never deduplicated. `dataset_get` resolves DOIs through DataCite/Zenodo/Figshare and downloads into `data//` with size caps and checksum verification (mismatches recorded `verification_failed`, files kept), plus `verify`/`status` against a provenance manifest; `dataset_search` stays search-only. `data_profile` profiles local files through pandas — schema, missingness, duplicates, ID/date heuristics, target balance, split overlap, small cells, and leakage-risk findings with explicit `not_checked` items and a data dictionary. `repro_package` builds a reader-facing package (manifest with SHA-256, environment snapshot, generated Dockerfile, README, CITATION.cff, codemeta.json, RO-Crate 1.1, checksums) from the other ledgers, verifies it by re-hashing, and offers a two-step, confirm-gated, draft-only Zenodo deposit that never publishes. New skills `reproducible-analysis` and `experiment-tracking` (catalogue now 751); new guide `docs/ANALYSIS.md` with a lifecycle diagram; prompt section `## Reproducible analysis`; tests `scripts/test-analysis-run.mjs`, `test-experiment-log.mjs`, `test-dataset-get.mjs`, `test-data-profile.mjs`, and `test-repro-package.mjs` in CI; helper environment gains pandas and openpyxl. Live verification also fixed `dataset_search files` for Zenodo's newer API (the record payload now inlines the file list and `/files` can 404) and made `dataset_get get` surface a failed file listing as unavailable with the reason instead of an empty list. - **Phase 3 — cross-ledger provenance audit (`provenance_audit`).** One new preset-only tool, strictly **read-only**: `provenance-store.js` builds an index by joining the ledgers that already exist — `review` records, screening, evidence, extraction rows and field quotes, appraisals and GRADE events, `reporting_check` verdicts with their manuscript snapshot, stored `deep_review` claim–evidence maps and citation verdicts, `notebook` entries and their artifact links, `analysis_run` plans/manifests with hashed inputs and outputs, `experiment_log` runs and links, `dataset_get` downloads and checksums, `meta_analysis` studies with their `record_id` provenance, and Academic Harness cited-clause sidecars (`claims/*.yaml`) when the workspace has `ah.yaml` — using the shared `paper:`/`quote:`/`unit:`/`claim:`/`record:`/`run:`/`result:`/`review:`/`export:` id conventions from `provenance.js`. Actions: `index`, `coverage`, `unsupported` (split into `no_link`, `not_checked`, `unverified`, `stale`, `not_found`), `stale` (recorded vs current hashes, plus the state a claim held before the ripple), `unverified`, `impact` (transitive dependents of a paper, quote, file, or run), and `export` to Markdown, CSV, JSON, GraphML, and Mermaid. Support is never inferred: a `deep_review` engine verdict is recorded as unverified, a quote with no PaperMemory paper is `no_link`, a broken link is `not_found` naming the missing end, and a ledger that cannot be read is `unknown` with its reason — `scientific_result` (not persisted) and `preregister` (Phase 5A) are reported that way. Live quote re-verification through `papermemory_quote_check` is opt-in (`verify_quotes: true`); PaperMemory's SQLite store is never opened directly. Every write is funnelled through `assertAuditWrite`, which refuses any target outside `.dsh/provenance//`; source ledgers are opened with their own read functions only. Deviations from the plan: the cache is keyed by an audit name rather than a content hash (consistent with every other ledger, with the index hash recorded inside it), and AH unit authorship stays with `ah_provenance` rather than being re-derived. New skill `provenance-audit` (catalogue now 752), new guide `docs/PROVENANCE.md`, prompt section `## Provenance audit`, `yaml` added as a direct dependency for the claim-sidecar reader, and `scripts/test-provenance-audit.mjs` in CI (`test:provenance`) — it hashes every file outside the cache before and after indexing and exporting to prove no source ledger was written, and asserts that write attempts to one are refused. Live verification through the DSH Web UI (a throwaway profile linking this tree, a fixture workspace, and a real agent turn) reproduced the packaged index exactly — 22 nodes / 19 edges, claims `no_link` 3 · `not_checked` 1 · `stale` 2, with the stale AH claim showing its recorded and current source hashes — and found one usability gap that is now fixed: tool output includes node ids, so the agent can pass them straight to `impact`. - **Phase 1 end-to-end verification in the DSH Web UI.** A scripted review-plus-meta-analysis run through the browser agent found and fixed three defects: `extract_status` no longer reports every required field as missing when a rostered reviewer has not extracted yet (the row now reads `awaiting human:`, exports gained a `missing_reviewers` column, and only genuinely absent fields are listed); `meta_analysis start` accepts a `protocol` object in addition to flat arguments (flat wins on conflict); and the Markdown report and studies CSV now show per-study weights from the pooled analysis instead of an empty Weight column. The `effect_measure` description no longer mentions DOR, which is not a pooling measure. - Tests: `scripts/test-ledger-kit.mjs`, `scripts/test-review-extract.mjs`, `scripts/test-prisma-flow.mjs`, `scripts/test-meta-analysis.mjs`, and `scripts/test-grade.mjs`; `test:review` includes the review tests, `test:meta-analysis` covers the pooled tool, and `test:grade` covers GRADE. `scripts/test-appraisal.mjs` now covers all eleven instruments, PROBAST dual-axis cells, and CASP quote rules end to end. ## 0.33.0 - **Academic-search parity tools.** Added `journal_search` (OpenAlex/DOAJ/Crossref journals, APC/OA/ISSN metrics, optional Elsevier CiteScore/SJR/SNIP and WoS JCR), `paper_get` (merged OpenAlex/S2/Crossref/Europe PMC/arXiv record, OA enrichment, canonical BibTeX), `paper_context` (S2 snippets, arXiv HTML, PMC BioC/PubTator3, keyed publisher TDM, OA PDF), and `database_catalog`. Extended `literature_search` with DOAJ and keyless publisher Crossref deposits, and added OpenCitations fallback plus a `both` direction to `openalex_citations`. `citation` now accepts an `identifier` for DOI/arXiv/INSPIRE/title BibTeX. - **Free providers tested; paid tiers implemented but not tested.** OpenAlex, S2, Crossref, arXiv HTML, Europe PMC/PMC BioC/PubTator3, DOAJ, OpenCitations, publisher Crossref search, and the catalog have fixture tests and live smoke coverage. Elsevier Serial/ScienceDirect, WoS JCR, Wiley TDM, Springer API/JATS, and IEEE are implemented for use with the user's keys but are deliberately outside the automated/live suite. - **No bot-challenge bypasses.** Sources that require one are not included. ar5iv was removed after repeated timeouts; arXiv's native HTML remains the full-text tier. - Added the `academic-search` skill and reference bundle, docs, settings fields (`ELSEVIER_INSTTOKEN`, `WILEY_TDM_TOKEN`, `SPRINGER_API_KEY`, `IEEE_API_KEY`), preset rows, and golden checks. - **README and diagrams.** Added the complete Search tools table, named the source-specific search tools, and updated the search-routing, capability-map, paper-pipeline, and request-lifecycle diagrams (including `paper_get`, `paper_context`, `journal_search`, `openalex_citations`, and `database_catalog`). The capability map now reflects 747 skills. ## 0.32.7 - **README links the upstream open-deepreview repository.** The Papers and evidence section now points to [open-deepreview](https://github.com/raktim-mondol/open-deepreview) where `deep_review` is introduced. Docs-only; no behavior change. ## 0.32.6 - **Live progress for deep review.** Each review now runs as an owned background job, so the session header shows a live stage/percentage while it runs; expanding the job streams the engine log (segmentation, budgeting, per-segment verification, synthesis, citation checks, report) and every model call's route, duration, and token usage. The finished job remains under **Finished** for later inspection, and the tool result includes `job_id`. Cancelling from the job row aborts the engine and returns a cancellation error. The engine log also names the actual session model instead of a placeholder. - Tests cover job registration (kind/owner/tool call), engine stderr progress, bridge request/response logs, the result's `job_id`, and the engine's model label. Verified live in the Web UI: a LOW review streamed `3% SEGMENTATION`, `8% ADAPTIVE_BUDGETING`, `45%/80% DEEP_REVIEW`, `82% GLOBAL_SYNTHESIS`, `100% DONE`, with per-call durations and token usage. ## 0.32.5 - **No separate-cost framing for deep review.** Deep review runs on the session model, so the routing question and default workflow now describe depth and duration only. The prompt, `deep_review` tool description, and `deep-review` skill no longer mention money; `estimate` is optional and used only when the user asks for it. Docs describe it as an optional engine heuristic. Verified live: the question offered normal vs deep review without any cost or billing language. ## 0.32.4 - **Always offer deep review on paper-review requests.** Any paper or manuscript review, critique, referee report, feedback, assessment, or pre-submission request now asks the user to choose between the deep review engine and a normal session review, and explains the deep option because the user may not know it exists. The question appears every time a paper review is requested unless the user explicitly chose a mode. The routing rule is carried by the prompt, the `deep_review` tool description, and the `deep-review` skill, with a regression guard. Verified live with "Give me feedback on the paper": the agent asked with Normal, Deep review engine, and Referee report options before doing any review. ## 0.32.3 - **Ask before a generic paper review.** When the user says "review this paper", "give me feedback on this manuscript", or similar without naming the kind, the agent now asks whether to use `deep_review` (multi-pass engine, paid `estimate`, minutes) or a normal session review (fast session-model critique/referee report), then acts on the choice. It no longer silently falls back to a normal review, and it does not run `estimate` or `review` before the user chooses. The prompt, `deep_review` tool description, and `deep-review` skill carry the rule; a regression check guards the wording. ## 0.32.2 - **Documented the repeated DeepSeek model-listing lines.** A normal start mounts the DeepSeek provider once per agent-preset world, so each instance follows the official `/models` listing and prints an informational `[llm-deepseek-api-key] model listing ...` line. `docs/INSTALL.md` now explains the cause, the profile-patch catalog pin that stops the listing requests and the lines, and the discovery tradeoff. ## 0.32.1 - **DSH 0.2.1-rc.5 dependency alignment.** The plugin now pins `@deepseek-ai/dsh-tools`, `dsh-settings`, `dsh-tool-subagent`, `dsh-mcp-client`, and `schemastery` to the rc.5 line and requires dsh `>=0.2.1-rc.5 <0.3.0`. With the rc.2 pins, dsh 0.2.1-rc.5 disabled the `tool-subagent`, `subagent-model-selection-settings`, Claude Code hook/subagent, and `subagent_pro`/`subagent_vision` rows at startup as incompatible. The plugin-facing APIs (`defineTool`, `McpClient`, `agentOptions`, settings methods) are unchanged in rc.5. - The profile's own DSH packages must match too: `@deepseek-ai/dsh-hook-protocol`, `dsh-hooks-claude-code`, `dsh-subagent-claude-code`, and `dsh-working-directory` at `0.2.1-rc.5`. - Verified with a clean `academic-harness` boot (no version warnings), `test:deep-review`, `test:review`, `test:compute`, `test-academic-harness`, and a live `deep_review status` on rc.5. ## 0.32.0 - **Added `deep_review`, a stateful full-manuscript Deep Review pass.** The tool wraps the vendored [open-deepreview](https://github.com/raktim-mondol/open-deepreview) engine: cost estimate, multi-pass segmented critique, page/quote-anchored comments, a scholar-peer summary with a claim-evidence map, reproducibility checklist and author questions, and OpenAlex/Crossref reference verification. Actions are `review`, `estimate`, `pdf2md`, `verify_citations`, `status`, `show`, and `list`; `show` pages comments, questions, checklist items, claims, flagged citations, and the rendered Markdown without loading the whole report into context. - **State and cost live in the plugin, not the engine.** Artifacts are stored under `/.dsh/deep-review//`, keyed by the workspace-relative path so a stored review survives edits; `meta.json` keeps the content hash, so `status` flags a review as stale after the manuscript changes. `review` returns only a compact summary; re-runs overwrite the latest artifacts and append `runs.jsonl`. The prompt and `deep-review` skill require an `estimate` and an explicit user confirmation before any `review`. - **Runs on the session model, with no second API key.** A loopback OpenAI-compatible bridge in the plugin forwards every engine call to the host's `llm` service using the calling agent's provider/model, so `review` and `verify_citations` use the same model as the chat and switching the session model switches the reviewer. The bridge is bound to 127.0.0.1 and guarded by a per-run bearer token; ambient provider keys are not forwarded and the token is never stored or logged. - **Vendored, Node-only engine.** `vendor/open-deepreview/` pins commit `c48e294`; `runner.mjs` calls the exported CLI without relying on Bun or `import.meta.main`, and a loader hook handles npm installs under `node_modules` where Node refuses TypeScript stripping (Node ≥ 22.15). The Bun-only web viewer and the fixture command are not exposed. - New `deep-review.js`, `vendor/open-deepreview/` (SOURCE.txt, runner, loader), `skills/deep-review/SKILL.md` (746 skills), and `scripts/test-deep-review-tool.mjs` (fake-engine coverage for every action, storage, staleness, path confinement, missing keys, and redacted failures). ## 0.31.1 - **Routing is selected by task type, not by user phrasing.** `subagent_pro` is now chosen for difficulty-bound work (proofs, causal-inference/design critique, multi-stage flaw tracing, large refactors) without requiring the user to ask for it, and those categories are delegated rather than answered inline — even conceptual questions with no data, code, or files. `subagent` covers ordinary delegated work. With no custom Pro route, `subagent_pro` inherits the session model, so difficulty still decides the tool. Vision remains automatic for image reading. This supersedes 0.31.0's explicit-user-only Pro rule. - **Image routing is enforced instead of suggested.** The top-level Academic Harness agent no longer sees or can call `read_image`; a per-agent tool restriction hides it and a guard refuses any direct call with a message pointing at `subagent_vision`. Children are unaffected, so the vision child still reads the file. In live testing the agent sometimes bypassed `subagent_vision` and used its own `read_image` when no custom Vision route was configured; that path is now closed, making automatic delegation deterministic. - **Routing decisions and outcomes are logged automatically.** Each `subagent_pro`/`subagent_vision` call writes a `decision` entry to the session notebook (route used, task, prompt excerpt, tags `routing`) and an `observation` entry when the child settles (`relatesTo` the decision, stop reason, duration, final-answer excerpt). The prompt now tells the model not to duplicate these entries, removing the earlier dependence on the model remembering to call `notebook`. ## 0.31.0 - **`subagent_pro` and `subagent_vision` now take optional custom routes.** Each tool has three fields in Settings → Academic Harness API keys — endpoint URL, model, and API key. Setting all three registers an `academic-harness-pro`/`academic-harness-vision` route on the host's pi-ai adapter; the key is stored in the credential service. The endpoint URL selects Anthropic Messages when it contains `anthropic` (for example `https://api.deepseek.com/anthropic`) and OpenAI Chat Completions otherwise. With no complete route, both tools inherit the session model (DeepSeek-V4.1-Flash by default, which also accepts images); a partial set is ignored. The prompt now makes `subagent_pro` explicitly user-triggered: it is used only when the user asks for the Pro route or says a custom route is configured, and `subagent` is the default otherwise. This replaces the old fixed model defaults that pinned both tools to `deepseek-flash`. ## 0.30.1 - **Fixed the `rob-figure` layout.** The traffic-light panel clipped its title and the rotated domain labels, and long summary y-axis labels could run off the left edge. The traffic-light figure now reserves space for labels with a right-anchored rotation, both panels size from the study/domain counts, and the note stays visible. Verified in the DSH web UI. - **Reconcile provenance in exports.** `rob-table` and its CSV gain a `Reconciled by` column, the `rob-figure` note names the reconciling reviewer, and `appraisal_status` exposes each cell's reconcile event. A consensus decision no longer looks like agreement between the original appraisers. - **Clearer instrument notes.** The RoB 2 and ROBINS-I template notes now state that the overall is derived from the domain judgements and that only the official signalling-answer algorithms are unimplemented. This removes an agent-facing ambiguity found during end-to-end UI testing. - Regression assertions for the template notes and reconcile provenance. ## 0.30.0 - **Added Phase 4: risk-of-bias appraisal in the `review` ledger.** New actions `appraise`, `reconcile`, `agreement`, and `appraisal_status`; four paraphrased, versioned instruments — RoB 2 (2019, 5 domains + derived overall), ROBINS-I v1 (7 + overall), QUADAS-2 (4 domains, 7 risk-of-bias/applicability cells), and Newcastle-Ottawa (3 star-count domains). The full instrument definition is frozen into `.dsh/review//instruments.json` at `start` or first use, so a running review cannot change its rubric. - **Reviewer-keyed, backward-compatible state.** Screening and appraisal state is derived per reviewer; reviewer-less events read as `default`, so single-reviewer ledgers derive, count, and export exactly as before. Pure v0.29 ledgers keep latest-per-key behaviour even when they stored a free-text reviewer name. Identities are typed `human:` / `ai:`; with a declared roster, disagreements surface as conflicts, `override` replaces only that reviewer's earlier event, and `reconcile` records a consensus or third-reviewer decision while keeping the originals. A later disagreement reopens the key. A declared roster is also the appraisal panel: every declared reviewer must judge every domain before a record counts as complete. - **Evidence rules for appraisal.** A `low`/`high` (RoB 2, QUADAS-2) or `low`/`critical` (ROBINS-I) judgement needs a verbatim quote and location. With a PaperMemory `paper`, the quote is re-found through `papermemory_quote_check`; a quote absent from a resolved paper, or found only in the bibliography, is rejected and not stored. An unresolved paper, a failed check, or an unexpected result is stored `unverified` with the reason. Signalling answers are supporting detail only — the official answer-to-judgement algorithms are not implemented. An explicit `overall` override requires a reason; derived overalls are marked derived and never stored. - **Agreement statistics.** New `agreement.js`: percent agreement, Cohen's κ, linear/quadratic weighted κ for ordinal scales, a normal-approximation 95% CI, and documented edge cases (`no_common_units`, `single_category`, `expected_agreement_one`). More than two raters is pairwise; Fleiss' κ is not implemented. Human κ never includes AI judgements; AI-vs-human and AI-vs-AI pairs are reported separately. Caveats cover skewed marginals, small samples, rater bias, and clipped intervals. - **RoB exports.** `export` gains `rob-table` (one row per included study; domain cells, overall basis, status; NOS total stars), `rob-summary` (counts per domain, conflict, pending), and `rob-figure` (`rob-traffic-light.svg` + `rob-summary.svg` through the new `python-helpers/rob_figure.py`, matplotlib). Tables and CSVs name the reviewers behind each row and count unverified quotes; the figure note repeats both. Exports report recorded judgements and completeness, never "low risk" or a compliance verdict. New `table_format` selects Markdown or CSV; overwrite refusal is unchanged. - New `agreement.js`, `appraisal.js`, `papermemory-mcp.js` (shared PaperMemory MCP bridge), `appraisal/**`, `python-helpers/rob_figure.py`, and the `risk-of-bias-appraisal` skill (745 skills). `scripts/test-agreement.mjs` covers κ goldens and AI separation; `scripts/test-appraisal.mjs` covers template validation, derivation truth tables, dual-review reconciliation, quote rejection, exports, and figure wiring. `review` now uses the symlink-safe workspace path check for record files. ## 0.29.0 - **Added the Phase 3 reporting-guideline checker, `reporting_check`.** One action-multiplexed tool (`list`, `suggest`, `start`, `items`, `candidates`, `judge`, `status`, `export`) checks a manuscript against versioned CONSORT 2010, STROBE 2007, PRISMA 2020, PRISMA-S 2021, PRISMA-ScR 2018, ARRIVE 2.0, TRIPOD+AI 2024, CARE 2013, and STARD 2015 checklists. Item ids follow the published numbering; every prompt is a paraphrase with a link to the official source, and the official wording is not bundled. - **The model proposes, the tool verifies.** `reported`/`partial` require a verbatim quote that the tool re-finds in the manuscript after whitespace normalisation; a fabricated quote is rejected and not stored. `not_reported` requires the terms or sections searched, `not_applicable` requires a reason, and overriding a verdict requires a reason. The ledger at `.dsh/reporting//` is append-only (`check.json`, `verdicts.jsonl`, `exports/`). - **Inputs and locations.** `.tex`, `.md`, `.txt`, `.docx` (shared `python-helpers/document_text.py`), `.pdf` through the existing `pdf_to_markdown` converter, and PaperMemory papers through the PaperMemory MCP server. LaTeX is masked for matching while offsets, lines, and `%% @unit` anchors stay against the original file. `suggest` returns design-cue candidates and never selects one. - **Stale evidence is explicit.** `status` re-reads the manuscript and re-verifies every quote; a quote that no longer matches becomes stale, and absence claims are marked stale after any manuscript change. Exports (Markdown, CSV, LaTeX `longtable`) say **N of M items have verified evidence** and never contain a compliance label. - New `reporting-guidelines` skill (744 skills). `scripts/test-reporting-check.mjs` covers the acceptance criteria across Markdown, LaTeX, and DOCX fixtures, evidence rejection, stale detection, exports, item-id integrity, and workspace confinement. - `stat_audit` now shares the DOCX reader in `python-helpers/document_text.py`; `pdf_to_markdown` exposes `convertPdfMarkdown()` so `reporting_check` reuses the existing converter instead of adding a second parser. ## 0.28.0 - **Added Phase 2 domain sources.** `literature_search` now routes bioRxiv/medRxiv, INSPIRE-HEP, NASA ADS, Scopus, and Web of Science alongside OpenAlex, Semantic Scholar, PubMed, arXiv, and Crossref. The new `domain_search` tool exposes source-specific actions: bioRxiv date/category browse and published-version lookup, ACL Anthology records, INSPIRE citations/references/texkeys/BibTeX, ADS export/metrics, Scopus/WoS cited-by, and Wikidata entity resolution. ADS uses `ADS_API_TOKEN`, Scopus uses `SCOPUS_API_KEY`, and Web of Science uses `WOS_API_KEY`; without a key they return `key_missing` without making a request. Scopus/WoS are explicit-only and metadata-only. `paper_resolve` recognizes INSPIRE, ACL, bibcode, Scopus, WoS, and Wikidata identifiers and resolves them through the domain adapters. New offline coverage: `scripts/test-domain-sources.mjs`. - **DBLP was removed after live testing.** DBLP now answers its API with an Anubis anti-bot challenge (HTTP 202 plus HTML), so a keyless DBLP adapter cannot return real results from this environment. The source, DBLP identifier type, router entry, settings/docs, and tests were removed rather than shipping a source that only reports `provider_blocked`. - **Live verification tightened the remaining domain output.** OpenAlex author records no longer carry an `orcid: undefined` field that DSH rejected as non-lossless JSON; INSPIRE references render their titles, authors, and record links; `paper_resolve` returns INSPIRE/ADS/Scopus/WoS records directly instead of bouncing through Semantic Scholar; Wikidata results are filtered by the requested entity type; and INSPIRE recids plus the other source-specific identifiers are shown in rendered paper lists. - **Added the 12-skill Supervisor-Skills research-lifecycle suite** from [HKUSTDial/Supervisor-Skills](https://github.com/HKUSTDial/Supervisor-Skills) at commit `a20d76e` (v2.1.0): `deep-research`, `idea-evaluator`, `tech-paper-template`, `benchmark-paper-template`, `intro-drafter`, `paper-writer`, `paper-polish`, `figure-designer`, `drawio-reconstruction`, `pre-submission-reviewer`, `rebuttal-guidance`, and `vibe-research-workflow`. The catalogue is now **743 skills**. - Licences are mixed: six skills are CC BY 4.0, five are CC BY-NC-SA 4.0 (non-commercial, share-alike), and `drawio-reconstruction` is MIT. Attribution, the per-skill mapping, and the local changes are in [NOTICE](../NOTICE) and `vendor/supervisor-skills/SOURCE.txt`; the package's MIT licence does not relicense the non-commercial skills. - The prompt, [Writing](WRITING.md#supervisor-skills-paper-lifecycle), and [Skills](SKILLS.md) route the suite for paper work outside an `ah.yaml` project; inside one, the Academic Harness engine stays authoritative for drafting and the gate. The upstream handbook, repository READMEs, and 13 MB of drawio examples were not copied. - **Overlap resolved at the routing layer.** No Supervisor skill duplicates an existing skill name. Each of the 12 carries a `Routing in Academic Harness` section, and 15 related existing skills (`academic-harness`, `ah-write-unit`, `ah-plan-section`, `scientific-figure-making`, `diagram-design`, `manuscript-editor`, `peer-reviewer`, `literature-researcher`, `literature-review`, `surveying-prior-work`, `generating-scientific-hypotheses`, `agentic-data-science-pipeline`, `remove-ai-signs`, `abstract-writer`, `systematic-review-ledger`) carry a reciprocal note. The seeder refreshes those known-stale copies on existing installs. - **Browser-verified.** The live DSH UI listed all 12 skills in the slash menu; a real chat turn loaded `paper-writer`, `pre-submission-reviewer`, `figure-designer`, and the refreshed routing sections through the `skill` tool. ## 0.27.3 - **A project-bound collection keeps its project scope on every export.** Passing an explicit `out` used to drop the collection's project, so re-checking a correct project `.bib` after a key rekey could report `out_of_date` or expect the wrong key. The collection's project is now inferred whenever a collection is named, for every format and destination; the default `.bib` destination remains BibTeX/BibLaTeX-only. An explicit `project` still overrides, and a collection with no project still exports canonical keys. - **Prompt, skill, and tool description agree.** The reference-library rules now state the project-scope behavior; the `papermemory` skill also corrects `resolve` (`project_keys` is reported only with `project_keys: true`). The seeder refreshes existing 0.27.1 briefs that predate the rule. - **Regression coverage and a managed-install refresh.** `test_library_export.py::test_a_collections_project_scope_survives_an_explicit_out` and `scripts/test-reference-library.mjs` cover the explicit-`out` case after a rekey. `BUNDLE_REV` is now `dsh17`, so the vendored PaperMemory is reinstalled on the next DSH start. ## 0.27.2 - **Fixed turns failing with `format v4 message requires a producer-owned source kind`.** The hooks tagged the messages they inject (the RSI lesson block, the Academic Harness state card, the stop-gate hand-off) with the retired `{ kind: 'plugin', plugin }` wrapper, which DSH 0.2.1-rc.2's session format refuses. The turn that carried an injected message failed outright; a session with no lesson selected and no Academic Harness project never noticed. They now use the producer-owned kinds `plugin:academic-harness-ah` and `plugin:academic-harness-rsi`. Found by driving a real DSH web session through a browser: the third prompt of a session failed the moment a lesson was injected. - `scripts/test-phase7.mjs` now fails if either hook file goes back to the retired wrapper. ## 0.27.1 - **The reference library is now a native tool, `reference_library`, so nothing has to be told where the workspace is.** An MCP server is a single process with one fixed working directory, but every DSH session has its own workspace, and the host freezes tool arguments, so a hook cannot supply it either. The MCP version needed the model to pass `cwd` on every import, export, and doctor call. The native tool receives the session, resolves paths against its workspace, and refuses any path that leaves it (symlinks included). It runs the same `papermemory library` code as before. - The plugin hides the MCP copy (`PAPERMEMORY_HIDE_TOOLS=papermemory_library`, honoured by the PaperMemory server and its `tools` command) so the model sees one library tool, not two. The start-up check now expects 14 MCP tools. Standalone PaperMemory, Grok, and any other host still get the 15th as `papermemory_library`. - Prompt, `papermemory` skill, docs, and the seeder rule updated (existing installs get the refreshed skill). `scripts/test-reference-library.mjs` covers argument translation for every action, workspace confinement, inline imports, and an end-to-end run against the vendored PaperMemory. ## 0.27.0 - **New `power_analysis` tool** for planning a study before data exist: sample size, power, smallest detectable effect, or alpha (`power`, `sensitivity`); effect-size conversion and computation (`effect_size`); Bonferroni, Holm, Hochberg, Benjamini–Hochberg, and Benjamini–Yekutieli adjustment and per-test alpha planning (`correct`); seeded Monte-Carlo power (`simulate`); and sample size for sensitivity/specificity precision (`diagnostic_precision`). Covers t, proportion, ANOVA, chi-square, correlation, regression, log-rank, equivalence (TOST), and non-inferiority designs. - **Integrity rules built in.** An assumed effect size needs `effect_source` (pilot, literature with a `source_ref`, smallest effect of interest, or convention); a convention is labelled as one. Post-hoc power from the study's own observed effect is refused. The unrounded and rounded n are both reported, dropout and clustering inflate the enrolment target separately, and every result echoes its assumptions and method. - Runs on NumPy and SciPy through `python-helpers/power_helper.py`; no new dependency (`statsmodels` is not used). Preset-only, no key, no network. - New `power-and-sample-size` skill (731 skills). `scripts/test-power.mjs` (`npm run test:power`) checks closed-form results against published values, that every solve mode inverts the others, the refusal rules, reproducible simulation, and registration. ## 0.26.0 - **PaperMemory is now the reference manager.** The vendored PaperMemory moves to 0.3.0 (schema 2100) and adds a reference library: lossless records, unique stable citation keys, tags, collections, notes, import of BibTeX/BibLaTeX/RIS/CSL-JSON, export to BibTeX/BibLaTeX/RIS/CSL-JSON/JSON/CSV, a managed project `.bib` that never overwrites hand edits, duplicate detection, a field-level merge preview, and a library `doctor`. It is one new tool, `mcp__papermemory__papermemory_library`, so PaperMemory exposes **15 tools** (the start-up drift check now expects 15). Nothing was added for Zotero; PaperMemory replaces the need for it. - **Existing stores migrate on first start.** A schema-2000 store is backed up (`backups/` next to the database) and migrated to 2100 automatically. If another process still holds the database the migration is refused with a clear message and the store is left untouched. `papermemory backup` now also works on a store that still needs migrating. - **Harness tools use the library.** `citation` takes `library: { cited_only | project | collection | query | all }` as a record source and keeps the library's own keys; `manuscript_citations` takes `library: true` (and `project`) and reports cited keys missing from the library, keys cited under another library key, and a project's entries never cited; `literature_search` marks results already stored (`in_library`) and says nothing when the library cannot be reached; `integrity_check` takes `library` (a bounded sweep of library DOIs, recording each result on the paper; unknown and malformed-DOI results are recorded as unknown, never ok) and `record_in_library` for a single DOI. - New papers get a library record at ingest, and a key as soon as there is enough metadata to build a good one; export assigns the rest (a `--check` never writes). - The `papermemory` skill documents the library and is refreshed on existing installs. `scripts/test-library.mjs` (`npm run test:library`) covers the CLI bridge, the `citation` library source (including that both BibTeX writers agree), `manuscript_citations`, integrity sweeps, `literature_search`, and the first-run migration; `scripts/test-phase7.mjs` now tests the vendored package rather than whichever copy is installed. ## 0.25.4 - Fixed `paper_fold` failing preset validation on current DSH/Cordis: the plugin registered through `ctx.tools` without `export const inject = ['tools']`, so every new session failed with `agent-preset/invalid: paper-fold ... cannot get property "tools" without inject`. New sessions and `paper_fold` work again after restart. - `paper_fold` tests still pass (85 checks). ## 0.25.3 - The seeder now refreshes first-party briefs that predate the current guidance (Modal SDK troubleshooting, Runpod REST v2, Vast.ai pitfalls), so existing installs receive skill updates without deleting the seeded directories. It still only replaces known-old content, leaving local edits to other skills untouched. - `scripts/test-academic-harness.mjs` covers the stale-brief refresh. ## 0.25.2 - The `modal` skill now documents the managed SDK runtime (`$DSH_HOME/modal-sdk`), `ACADEMIC_HARNESS_MODAL_SKIP_INSTALL`, and the pnpm 11 `ERR_PNPM_IGNORED_BUILDS` workaround — including why `allowBuilds` belongs in the profile's `pnpm-workspace.yaml`, not in `academic-harness/package.json`. - The system prompt routes a failed manual Modal install to that skill before profile config is edited, and prefers denying the two optional native builds over `pnpm approve-builds --all`. - The docs index changelog range now tracks the current release. ## 0.25.1 - **Modal SDK install friction is gone.** `modal_run` now loads the SDK from the profile when present, otherwise installs a pinned `modal@0.11.1` into `$DSH_HOME/modal-sdk` on first use and uses it without restarting dsh. The managed install pre-configures pnpm's `allowBuilds` for its two optional native deps, and `seed.js` writes the same policy into every existing profile that depends on Academic Harness — so a manual `dsh plugin add modal` no longer fails with `ERR_PNPM_IGNORED_BUILDS` under pnpm 11. `ACADEMIC_HARNESS_MODAL_SKIP_INSTALL=1` keeps the manual-only path. ## 0.25.0 - **Vast.ai joins the remote-compute tools.** `vastai_run` searches live marketplace offers for a GPU class, rents the cheapest verified machine, uploads `files_in`, runs a command, copies `files_out` back, and destroys the instance. It supports spot bidding (`bid_price`), a pinned `offer_id`, multiple GPUs (`num_gpus`), an existing `volume_id`, and a price cap (`max_price_dph`). Its SSH key is registered per call and removed during teardown. New `vastai` skill, `VAST_API_KEY` in Settings, and [Compute](COMPUTE.md) coverage. - **Runpod moved to REST API v2** (`https://api.runpod.io/v2`); v1 retires on 2026-11-15. The client now uses v2's nested `gpu`/`cpu` request, wrapped list responses, cursor pagination for network volumes, `dataCenter` on volume creation, and RFC 9457 errors. The `cpu` instance uses the v2 `cpu5c` flavor (the old `cpu3c-2-4` id is not a v2 value) and a valid `runpod/base` tag, and a pod mounting a network volume is now pinned to that volume's data center. Pod deletion is retried; a final failure returns `runpod_teardown_failed` with the pod id instead of silently dropping it. - **`modal_run` reaches parity.** Pass `volume_name` to mount a persistent Modal Volume at `/workspace`; GPU instances default to a CUDA/PyTorch image instead of a CUDA-free `python` image; directories work in `files_in`/`files_out` via a tar fallback; the result includes `sandbox_id`; the optional SDK peer is now `^0.11.1`. Every provider now reports `*_teardown_failed` with the resource id if cleanup fails. - **Fixed the duplicate `modal` skill name.** `skills/modal-usage` now declares `name: modal-usage`, so the vendored K-Dense guide and the first-party `modal` brief both load (730 skills, 730 distinct names). NOTICE records the rename and the bundle's Apache-2.0 header. - **Transfer and teardown hardening.** Workspace paths are resolved through real paths and cannot name the workspace root or a symlink escape (`files_out: "."` previously replaced the whole workspace). Directory downloads extract to a temp dir and replace the old output only after both tar ends succeed; uploads require both ends too. `scp` downloads and stdout/stderr capture are bounded, transfers have timeouts, the main command is never retried after a dropped connection, and a create whose reply was lost is reconciled by the generated `dshrc-*` name/label. A failed teardown now preserves the run result alongside the error id. - `scripts/test-compute.mjs` and `scripts/test-compute-http.mjs` (`npm run test:compute`) cover the instance catalogs, v2/Vast request payloads, all three tool registrations, workspace-path guards (root, traversal, symlink), transfer-failure atomicity, and the clients against a mock HTTP server. ## 0.24.0 - The single-file README is now a concise landing page, with the full reference split into modular guides under `docs/`: [Install](INSTALL.md), [Architecture](ARCHITECTURE.md), [Tool index](TOOLS.md), [Configuration](CONFIGURATION.md), [Search](SEARCH.md), [Papers](PAPERS.md), [Writing](WRITING.md), [Research](RESEARCH.md), [Workspace](WORKSPACE.md), [Compute](COMPUTE.md), [Agents](AGENTS.md), [Skills](SKILLS.md), Optional tools, [Development](DEVELOPMENT.md), and this [Changelog](CHANGELOG.md). A [docs home](README.md) indexes them. - 14 diagrams (architecture, capability map, request lifecycle, install flow, search routing, paper pipeline, writing loop, review ledger, tournament loop, workspace-vs-PaperMemory, subagent routing, compute flow, RSI loop, skills map) are authored as Mermaid in `docs/diagrams/` and rendered to flattened SVG with Pele via the new `npm run docs:diagrams`. `flattenColors` is now exported from `mermaid-render.js` for reuse. - `sci_inspect` and `pdf_to_markdown` now point at the docs pages instead of the README; four tests read the docs pages rather than the old README headings. ## 0.23.0 - `fold_text` folds Markdown at five levels (Full, Brief, Points, Outline, Gist) with no model call, preserving the original words and leaving short texts whole. The Academic Harness preset now also asks for point-first replies in a separate, preset-scoped response-shape section, so a fold keeps the answer. - A completed chat reply long enough to fold shows a **Read folded:** row in the turn tail; choosing a level opens a folded panel below the untouched original. The row uses the same shared fold engine, keeps the last level in the browser, and never calls a model. **Open in sidebar** opens the same reply in a larger right-dock pane with its own level controls. - `paper_fold` adds map-first paper reading to the Academic Harness preset: a section map from the author's first sentences, then one section at a time, with no model call by default. TextFold is installed as a pinned optional Python runtime (`textfold==0.10.31`) under `$DSH_HOME/textfold`; `generate:true` asks TextFold's own provider for written levels. PaperMemory remains the evidence layer, and scanned PDFs stay on `pdf_to_markdown`/`subagent_vision`. - Removed the bundled `paper2agent-paper` reference skill and its 5.7 MB figure/table assets. The `paper2agent`, `paper2skill`, and `paper2mcp` conversion skills are unchanged, and the package is about 5 MB smaller. ## 0.22.1 - `coscientist next` no longer samples a task the ledger cannot execute: at the hypothesis cap, reflection waits for a pending review and generation/evolution are off, instead of handing back a no-op the caller has to override. - `coscientist_rank propose` completes the first ranking before repeating a pair: while any two active hypotheses have never met, a fresh pair plays first, then selection returns to uncertainty-first. The response reports `pair_matches_played` and `unplayed_pairs_remaining`. - `coscientist start` accepts `max_matches`; `next` reports `match_cap_reached`, so an explicit user stop bound (for example, one match per active pair) is enforced by the ledger rather than the model's judgment. - The `co-scientist-tournament` skill judges the two presentation orders sequentially with an identical brief, retries a failed orientation with that same brief, and tells the judge not to load skills or call tools. A child that still fails is recorded as `position_controlled: false` instead of substituting a differently worded run. ## 0.22.0 - Tournament Phase 4: `coscientist graph` (Mermaid lineage/similarity/support diagram, `export format: mermaid`), `coscientist priors` (cross-session feedback and withholdings, read from sibling ledgers), and opt-in `coscientist embed` (Gemini text embeddings that move the duplicate gate to cosine). `zvec-grep` cannot supply this — `zg query --vector` embeds a search query and never returns a vector — so embeddings go through the Gemini endpoint the plugin already uses for images. - Tournament defaults **5 hypotheses / 36 steps**, sized against the termination rules: five is the smallest pool where a review veto plus a dual-use withholding still leaves three ideas competing, and 36 is where an Elo plateau can actually be reached. A fourth stop reason, `no_competitors`, ends a pool that can no longer field a match instead of burning the budget. `coscientist_verify` (optional) adds Crossref citation integrity with the claim-support ledger, `coscientist safety` exposes the dual-use gate, and `scripts/test-coscientist-routing.mjs` checks that a problem-shaped ask still routes to a hypothesis skill. - `coscientist` + `coscientist_rank`: a persistent hypothesis tournament (Glicko-2 leaderboard, five-stage review, save-time duplicate gate, scheduling policy with step budget, dual-use withholding, Markdown/JSON export) over append-only logs in `/.dsh/coscientist//`, plus the `co-scientist-tournament` skill. Algorithms and role prompts ported from [`co-scientist`](https://github.com/raktim-mondol/co-scientist) (MIT); no new dependency. See docs/CO-SCIENTIST-INTEGRATION-PLAN.md. - `mermaid_render` — Mermaid text → SVG via [Pele](https://github.com/obsidianmd/pele) (npm dependency, MIT), in-process with no browser or mermaid-cli install; every Mermaid 12.1.0 diagram type. Written files get Pele's `--pele-*` CSS variables flattened to literal colors so Inkscape/rsvg/CairoSVG don't rasterize them blank. ## 0.21.0 - RSI architecture instrumentation: reached-action activation, injected-token cost, oracle manifests, lesson risk metadata, and explicit `rollback`. - Seven-layer plugin-RSI mapping documented; versioned skills/global promotion/executable changes are explicitly deferred or out of scope. - 96 RSI checks; corpus experiment over nine real documents (31/31 injected defects, 23/23 leave-one-project-out coverage, 56.7% routing accuracy, 0.53 held-out safety F1). - System paper in [`paper/rsi-local/`](../paper/rsi-local/). ## 0.20.0 - Local recursive self-improvement (RSI) for all research tasks: automatic capture, diagnosis, lesson admission, injection, injected-versus-control tracking, and quarantine/retirement. - `ah_rsi` in-session control surface and `academic-harness-rsi` offline CLI. - Synthetic pipeline benchmark, replay benchmark over real captured episodes, and real `ah audit` snapshot baseline. - Local-only store at `$DSH_HOME/ah-rsi`; `AH_RSI=off|observe|auto`; no author-approval step; no runtime edits to plugin, engine, project, or evaluator. - New `ah-rsi` skill and [docs/RSI.md](RSI.md). ## 0.19.0 - `remove-ai-signs` skill vendored into the plugin (style editing for machine-sounding prose; no detector-evasion or provenance-removal claims) and steered from a new `## Writing quality` prompt section. - `manuscript_citations` — manuscript ↔ `.bib` consistency for `.tex`/`.md`: missing, unused, and duplicate keys, undefined `\ref` targets, duplicate labels. - `funder_policy` — OpenAlex funder identity plus Sherpa Juliet per-version OA policy (keyed half). ## 0.18.0 Academic tooling beyond search (see docs/NON-SEARCH-TOOLS-PLAN.md): - `citation` — local APA/Vancouver/Harvard/IEEE/Chicago/MLA formatting, BibTeX/RIS export, DOI→BibTeX, and validation. - `integrity_check` — retraction / expression-of-concern / correction status from Crossref + OpenAlex (never `clean` on a failed lookup). - `review` — systematic/scoping-review ledger with protocol, screening, evidence, PRISMA-style counts, and exports (new `systematic-review-ledger` skill). - `clinicaltrials` and `openfda` — trial records/posted results and regulatory adverse-event/recall/510(k) data. - `dataset_search` and `journal_policy` — DataCite discovery with Zenodo/Figshare file listings, and DOAJ + Sherpa Romeo venue policy; new `ZENODO_API_KEY`, `FIGSHARE_API_KEY`, and `DATACITE_API_KEY` support. - Optional (`AH_ENABLE_OPTIONAL_TOOLS=1`): `author_lookup` (ORCID/ROR), `openreview`, `pdf_references` (BYO GROBID), `stat_audit`, `figure_audit`, `repo_audit`, `grant_search` (NIH/NSF/Grants.gov), and `patent_search` (PatentsView). - Europe PMC preprint routing: `pubmed_search preprints: true` and a `literature_search` `preprints` source (`SRC:PPR`).