Diaspore

All documents · source file docs/reports/2026-11-10-science-eval-report.md · changed 2026-11-10 · sha256 9eef8a7b3fdb

A specialist science agent lost to one careful read. Here is what I built instead, and what it measurably does.

2026-11-10. Plan: docs/plans/2026-11-10-science-eval-plan.md. Everything ran on this machine, in-session, at no API cost. Nothing was fetched and nothing was pushed.

1. The short version

2. What I decided and why

QuestionDecisionReason
What is the reader's job that a single read cannot do?Not reading better. Its job is set by its inputs and contract: it gets retrieved literature, it must answer per claim with two checkable quotes, and code does everything that is not reading.M13 showed one pass reads as well as the specialist. I did not contest that.
Separate agent, or merge with the one-pass baseline?Merge. One reader contract for every arm.The only open question was one read or several, and the test settled it: one.
Which Lens outputs?search_labs, lab_science, company_science, provenance, and the abstracts behind the papers they return.I call lens.py; I did not edit it.
How does it reach them and stay sealed?Code calls the Lens, gates every dated row again, and writes a pack. The reader gets Read on listed files and Write on one output. A read of any other file voids it.191 reader transcripts in the test, none left its files.

Routes I cut: reshaping the proofreading test again (spent); giving the agent search tools (nothing sealed, nothing reproducible); AI-judged "usefulness" (AI grading AI prose); an agent that grades each paper's design in prose (unmeasured opinion, when the Lens field is already measured at 95%).

Success and the change-course rules were written before any build step. None of the stop rules fired.

3. What I built

  1. Three fixes the handoff named.
    • A correction round can only rewrite a refused finding. An added finding is refused with a reason.
    • dd-verifier2: a claim that a source lacks something is judged against the whole block, not a quote.
    • An unseeded pack is no longer described to a judge as changed.
  2. The pack builder (src/diaspore/dd/scieval.py). Topic mode: the top labs' claims, at most 24, and up to 60 abstracts. Target mode: a lab's own claims, or a company's possible founding labs' claims, plus other labs' papers. The claims sheet never says which paper a claim came from. The key stays outside the pack.
  3. The reader contract (prompts/dd/scieval-v1.md). Per claim and paper: supported, contradicted, doubt or not found, with two quotes. No number of its own.
  4. Code that does the rest (scibrief.py): independence by lab and author overlap, tier from the Lens model-system field, counts, and a published sort rule (scirank-v1). A second prompt checks each record; a third writes a framed read (the claim that carries the most weight, what must hold, data-room questions) from validated records only. Its numbers are checked by code.
  5. The test (mutate_claims.py, sx1.py): code plants one false detail in a claim; code scores against a key. No AI judge anywhere.
  6. The command. /sci-brief topic "<words>" <date>, or lab <id>, or company <id>; underneath, scripts/dd_sci_run.py. An MCP tool, science_brief, serves built briefs.

Try it:

PYTHONPATH=src ~/.venvs/diaspore-sk122/bin/python scripts/dd_sci_run.py brief topic "KRAS inhibitor" --as-of 2025-12-31

Then follow .claude/commands/sci-brief.md. The agents run in a Claude Code session, so a brief is not one shell command: it is a build step, one to four agent reads, and a render step.

4. Results against baselines

Pre-registered in docs/dd/sx1-prereg.md, audited, sealed and committed before any test read. Run once. 24 topics no earlier step had used, 121 hard planted errors, 423 unaltered claims. Detail: docs/dd/sx1-results.md.

ArmHard errors found, with the right paperHard errors flagged at allUnaltered claims flagged
One pass with retrieval (ships)110 of 121 (91%, 85 to 96)1122 of 423
Several reads over chunks108 of 121 (89%, 82 to 96)1107 of 423
One pass, no retrieval0 (it has no paper to quote)63 of 121 (52%)7 of 423
Lens plus a string rule, no agent3 of 121 (2%)314 of 423

The other direction, graded in session. The test above measures catching a misstatement. Most of a real brief is the opposite: records that say a claim is supported. So 20 of those were drawn at random from the five real briefs and re-read by two blind agents, with a negative control built by code from each one's own paper (a direction word flipped, a number changed, or the claim paired with another paper). Both graders had to agree.

GroupCorrect95% interval
Real supported records (should hold)20 of 2083% to 100%
Controls (should be rejected)20 of 2083% to 100%

All 80 grader runs were unanimous. The controls are what make the first row mean anything: a grader that accepts everything scores 20 of 20 on real records and 0 of 20 here. What it does not show: the controls are plain contradictions, so a subtly wrong record (the same result in a different population) is not represented; 20 items give a lower bound of 83%, not a guarantee; and the graders are the same model family as the reader, so this is agreement, not independence. Method and limits: docs/methodology/scibrief-grading.md, rerunnable with scripts/dd_sci_grade.py.

On real data (five briefs, 99 claims, as of 2025-12-31): 96 claims verified against their own paper with quotes, 0 contradictions, 0 independent supports, 1 same-group support. Subjects: KRAS inhibitor, in vivo CAR-T, antibody-drug conjugate, one lab target, one company target. Outputs are in data/processed/scibrief/ and are not committed: they pair named PIs with name-matched companies.

5. How a skeptical investor should attack this

  1. "You planted the errors and hid the answer in the pack." Correct. 91% is an upper bound. A real misstatement is messier, and the paper that contradicts it may not be in the corpus.
  2. "The claim and the paper are the same text." Each claim is a model's summary of the paper it is checked against. The test shows the reader can find a source among 24 to 60 abstracts and notice one changed detail. It does not show it can judge a company's own wording.
  3. "So your agent is one prompt." Yes. The test says a second architecture adds nothing at this size. The value is the retrieval, the contract and the code around the read.
  4. "It found nothing on real topics." Correct, on five. The corpus is three recent abstracts per lab; two labs rarely report the same result in it. The rule for "supported" is strict: same intervention, system, direction and numbers.
  5. "The ranking is the Lens field sorted." Nearly. With no independent support and no contradictions, the order is evidence tier, then ties. It is a sort rule and the brief says it predicts nothing.
  6. "Company links are name matches." Correct. The company brief for one target drew claims from six labs joined by officer-name matches. It prints "the company link is weak" at the top.
  7. "AI graded AI." The reader and the extractor are Claude models, and so are the two graders of the supported records. The planted-error scoring is code against a key, with no model in the loop. The grading check runs negative controls so that at least a grader which accepts everything is detectable. No person has checked any of it, and the briefs say so.

6. What failed

7. Checks by independent agents

8. What is left, and what needs you

9. Acceptance

A fresh agent, given only .claude/commands/sci-brief.md, ran the acceptance test fixed in the plan and returned ACCEPTANCE: PASS.