Skip to content

feat(eln): scientific spine (Strain→Experiment→Hypothesis→Result) + perception→HuggingFace flywheel#77

Open
pskeshu wants to merge 21 commits into
developmentfrom
feature/eln-scientific-spine
Open

feat(eln): scientific spine (Strain→Experiment→Hypothesis→Result) + perception→HuggingFace flywheel#77
pskeshu wants to merge 21 commits into
developmentfrom
feature/eln-scientific-spine

Conversation

@pskeshu

@pskeshu pskeshu commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

ELN scientific spine — Strain → Experiment → Hypothesis → Result

Adds a co-authored electronic-lab-notebook scientific spine to gently as optional,
file-based overlays, plus the perception-annotation → HuggingFace flywheel. Turns
data-collection into claim-testing and gives the human a write-path that isn't chat.

Origin: docs/product-ideation/ENTITY-EVOLUTION.md (Fable's entity-evolution assessment)

  • Hari Shroff's Plan for Agentic Microscopy Paper. Design brainstormed → spec → plan.

Built + tested (backend, 28 tests)

  • Spine stores (FileContextStore, agent/{strains,experiments,hypotheses,results}/):
    Strain (create/get/list/resolve with bare-string back-compat), Experiment
    (arms × controls, link_session_to_arm), Hypothesis (proposed/supported/refuted/
    inconclusive), Result (accuracy/measurement/derived + experiment_ref filter).
  • gently/eln/ package: accuracy (per-timepoint predicted-vs-ground-truth →
    accuracy Result), hf_connector (approval-gated push; HF_TOKEN from env; default
    repo pskeshu/gently-perception-benchmark; lazy datasets; never auto-pushes),
    strain_resolution (non-mutating display shim), seed_loader.
  • Ground-truth authoring route POST /api/embryos/{id}/ground_truth (require_control,
    range-based → set_ground_truth) — replaces the localStorage-only Agree/Disagree
    dead-end.

The flywheel

gently-perception predicts → in-ELN ground-truth (assisted batch-confirm: agent
pre-screens ranges, human confirms uncertain frames) → accuracy Result → approval-gated
export to HuggingFace → retrain. Bidirectional: the model's calls become the human's
annotation queue; the corrections become the next training set.

Real seed programs (dogfood, not stubs)

seed/programs/: stage-classification (nuclear+BF fusion, hatching), temperature-
hatching
(Richard's K-pump thermo-paralytic → hatching-time distributions), mutant-
screen
(WT vs mutant ID), dopaminergic-outgrowth ("catching outgrowth", BZ555
dat-1p::GFP, expression-triggered high-cadence via dopaminergic_signal.py). A test
loads all four into a fresh store.

Docs

  • Spec: docs/superpowers/specs/2026-07-02-eln-scientific-spine-design.md
  • Plan: docs/superpowers/plans/2026-07-02-eln-scientific-spine.md
  • Usage eval: docs/product-ideation/ELN-USAGE.md (biologist journey over time, iterative
    stories, story-flow specs, design implications)

Remaining (in the plan, flagged for in-browser verification)

Vitals stage-strip range-authoring UI + assisted batch-confirm, experiment inspector +
export button, agent tools, and the ELN US- story flows (after #76 lands the crawler
harness on the base). Deliberately deferred because they need the running app + a Chrome
MCP UI/UX audit before shipping.

Non-goals (YAGNI)

Setpoint→Actuator, Embryo→Specimen rename, multi-tenant, re-inventing the organism
plugin, gently/dataset migration.

🤖 Generated with Claude Code — Fable 5

pskeshu and others added 11 commits July 2, 2026 11:09
…connector + 4 seed programs

On feature/eln-scientific-spine (off #72). Strain->Experiment->Hypothesis->Result as
optional co-authored file-based overlays; assisted claim-testing loop on existing
Expectation.resolve + ground_truth. Adds the approval-gated HuggingFace connector
(per-experiment export target, default pskeshu/gently-perception-benchmark, HF_TOKEN
from env, never auto) and the 4 real seed programs (stage-classification; K-pump
thermo-cycling -> hatching-time distributions; mutant screen; dopaminergic
outgrowth 'catching outgrowth'). References existing gently/dataset annotator,
dopaminergic_signal detector, test_embryo_strain. Non-goals: Actuator, rename,
multi-tenant, dataset-tool migration.

Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
FileContextStore gains create_strain/get_strain/list_strains/resolve_strain
(agent/strains/{id}.yaml) + the strains/experiments/hypotheses/results skeleton.
resolve_strain matches by id or case-insensitive name; a bare string that matches
no record returns None (back-compat — callers keep the display string). 3 tests.

Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
New gently.eln package (reads from FileStore, not the deprecated gently.dataset).
hf_connector: build_records (pure formatting of {image ref, prediction, human
ground truth, provenance, strain}) + push_dataset (approval-gated — caller confirms;
HF_TOKEN from env; lazy 'datasets' import; default repo pskeshu/gently-perception-
benchmark). Never auto-pushes; clear error when token/records absent. 5 tests.
seed/programs/: real version-controlled Campaign/Strain/Experiment/Hypothesis (not
stubs) instantiating the spine — stage-classification (RW10226 nuclear+BF fusion,
hatching), temperature-hatching (Richard K-pump thermo-paralytic -> hatching-time
distributions), mutant-screen (WT vs mutant ID), dopaminergic-outgrowth (BZ555
dat-1p::GFP 'catching outgrowth', expression-triggered high-cadence via
dopaminergic_signal.py). Dogfoods the spine + gives real notebook data.

docs/product-ideation/ELN-USAGE.md: biologist journey over TIME (accumulate datasets,
annotate incrementally, flywheel across sessions), 11 iterative stories, 23 little-
things, 8 Playwright story-flow specs, 14 design implications for the build.

Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
gently/eln/accuracy.py: pure functions comparing per-timepoint stage predictions
against range-based human ground truth -> an accuracy Result (n_scored/n_correct/
accuracy/unscored/confusion). stage_at_timepoint resolves the covering GT range
(latest-start wins on overlap; open-ended end reaches on). accuracy_result builds a
spine Result dict (draft when nothing scorable). 4 tests.
POST /api/embryos/{id}/ground_truth (require_control) -> FileStore.set_ground_truth
with annotator provenance; range-based (start/end timepoint, end null = open-ended)
so a confirmed stretch is one write. Replaces the localStorage-only Agree/Disagree
dead-end. 4 tests (persist range, open-ended end, stage required, int start).
Documents the shipped backend (Strain/HF/accuracy/GT route, tested) + the remaining
TDD tasks: Experiment/Hypothesis/Result stores (full code), strain resolution, seed
loader, export service + approval-gated export route, agent tools, and the in-browser
UI tasks (stage-strip range authoring, assisted batch-confirm, experiment inspector)
flagged for browser verification, + ELN US- story flows (after #76).
…ader

Spine data layer complete (all file-based, optional overlays):
- Experiment store (arms/controls/session-linking; strain alias); create/get/list/
  link_session_to_arm.
- Hypothesis store (proposed/supported/refuted/inconclusive); create/get/list/
  set_hypothesis_status (validates status).
- Result store; save/get/list_results (experiment_ref filter).
- strain_resolution: non-mutating display-side resolve_ref (bare-string shim) +
  enrich_imaging_spec.
- seed_loader: load_program/load_seed_dir imports seed/programs/*.yaml.
Tests (12) incl. loading the 4 REAL seed programs into a fresh store.
Fixes the inverted confirmation guards (flagged in docs/product-ideation/CONSULT.md):
the UI confirmed reversible map edits (Remove/Move embryo, Stop) but fired the two
specimen-HARMING actions unconfirmed. Now toggleLaser (OFF->ON), setTemperature, and
setLightsheetTemperature ask an explicit confirm naming the effect on live embryos.
Turning the laser OFF never confirms (safe direction). require_control unchanged.
…n flywheel UI)

Replaces the localStorage-only Agree/Disagree dead-end with a real ground-truth
stage picker: canonical-stage <select> pre-filled with the model's predicted stage +
a Confirm button that POSTs to /api/embryos/{id}/ground_truth (with currentSessionId),
shows a persisted 'GT: {stage}' badge, and a save flash. GT route made store-flexible
(agent.store -> server.gently_store -> server.store) so annotation works in
benchmark/no-agent mode too; +1 test. CSS matches the detection-card theme.
…rejected

A store-less / errored save previously only console.debug'd — now the Confirm button
briefly shows 'Save failed' (amber) so a correction never silently vanishes.
@pskeshu

pskeshu commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up commits:

Safety fix — inverted confirmation guards (39d9ff7). The UI confirmed reversible map edits (Remove/Move embryo, Stop) but fired the two specimen-harming actions unconfirmed. Now toggleLaser (OFF→ON), setTemperature, and setLightsheetTemperature ask an explicit confirm naming the effect on live embryos; turning the laser OFF stays friction-free. require_control unchanged.

Ground-truth authoring UI (b00d41f, 61fc478) — the annotation flywheel's visible write path. Replaces the localStorage-only Agree/Disagree with a canonical-stage picker pre-filled with the model's predicted stage + a Confirm that POSTs to /api/embryos/{id}/ground_truth, shows a persisted GT: {stage} badge, and a visible 'Save failed' hint on rejection. The GT route is now store-flexible (works without an agent). Verified in-browser via the real render path (screenshots attached in the session).

Still remaining (in the plan): assisted batch-confirm, live accuracy readout, experiment inspector + export button, agent tools, and the ELN US- story flows (after #76 lands the crawler on the base).

pskeshu added 3 commits July 2, 2026 14:20
…store

Makes the 4 seed research programs actually loadable into a gently FileContextStore
(they were version-controlled YAML with no loader wired). Verified: loads 4 campaigns
+ strains + experiments + hypotheses; they render in the Plans tab (screenshot).
New GET /api/campaigns/{id}/eln returns the campaign's experiments (arms x strain x
condition + controls), the strains they use (resolved), and deduped hypotheses.
campaigns.js renders an ELN section in the plan doc: Strains (chips w/ genotype),
Experiments (cards, arms with strain chip + condition, controls distinguished),
Hypotheses (status-badged). Verified in-browser against the seeded Temperature program
(2 strains, 3 experiments, 4 hypotheses). CSS in campaigns.css.
…tore empty)

launch_gently.py --seed loads the 4 bundled seed research programs into an empty
FileContextStore on boot, so a fresh instance opens with real notebook content
(Campaign/Strain/Experiment/Hypothesis). No-op if the store already has campaigns.
Verified: fresh store + --seed -> 4 campaigns render with full experiment/strain/
hypothesis detail in the Plans tab (native integrated render, screenshot).
@pskeshu

pskeshu commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Now visible in the UI (the earlier commits were backend/data; these make it show):

  • scripts/load_seed_programs.py + launch_gently --seed — load the 4 seed research programs into the store. A fresh --seed boot opens with real notebook content.
  • Plans tab now renders the ELN scientific spine per campaign: Strains (chips w/ genotype), Experiments (cards; arms with strain chip + condition; controls distinguished), Hypotheses (status-badged) — via new GET /api/campaigns/{id}/eln.
  • Verified in-browser (fresh origin, native integrated render): the Temperature program shows RK-KP1 (Richard's twk-18 K-pump strain) + WT-HIS, 3 experiments (paralysis validation, 2×2 protocol×imaging, thermo-cycling optimization) with arms/conditions/controls, and 4 proposed hypotheses.

Run it: python scripts/load_seed_programs.py then restart, or launch_gently --seed.

pskeshu added 4 commits July 2, 2026 17:21
…ion C)

Deep study of how Plan Mode and the ELN spine relate. Verdict: 3 of 4 ELN entities
(Strain/Hypothesis/Result) are genuinely additive and KEPT; only Experiment is
one-thing-modeled-twice (a controlled comparison is already a phase of imaging
PlanItems) and is the direct cause of the bolted-on render. Recommend Option C: Plan
Mode absorbs the ELN as native nodes+fields, Experiment demoted to a phase-role; ship
the model-agnostic perception->HF flywheel FIRST, then the fold. Not deprecate/absorb/
unify (A/B/E discard or rewrite the working incumbent; D keeps the redundancy).
gently/eln/export_service.py: collect_embryo_annotations pairs each prediction with the
ground-truth stage covering its timepoint (range-based, latest-start wins) into HF-ready
rows (annotated-only by default); annotation_summary gives {n_predictions, n_annotated}
for the push affordance. Robust to dict/dataclass store returns. 4 tests.
…push)

POST /api/embryos/{id}/export (require_control) — approval-gated (confirm + HF_TOKEN)
push of an embryo's annotated timelapse to HuggingFace via export_service +
hf_connector; 400 without confirm, 503 without token, 400 with no annotated timepoints,
502 on push failure. GET /api/embryos/{id}/annotation-summary -> {n_predictions,
n_annotated} for the push affordance. Extracted a shared _gt_store() resolver (agent ->
gently_store -> store), reused by the GT route. 5 route tests + GT route intact.
Each Vitals strip shows 'N/M annotated' (GET annotation-summary) + a Push-to-HF button
(disabled when nothing annotated). pushEmbryoToHf confirms, POSTs the approval-gated
/export, and surfaces the outcome inline (Pushed N / Set HF_TOKEN / Log in / Failed).
The per-embryo timelapse+annotations is the flywheel's unit; this is its terminal step.
@pskeshu

pskeshu commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Flywheel push landed (per-embryo timelapse → HuggingFace, the active project's terminal step):

  • gently/eln/export_service.py — collect_embryo_annotations pairs each prediction with the ground-truth stage covering its timepoint (range-based) into HF rows; annotation_summary gives {n_predictions, n_annotated}.
  • POST /api/embryos/{id}/export (require_control) — approval-gated (confirm + HF_TOKEN) push via export_service + hf_connector; GET /api/embryos/{id}/annotation-summary. Shared _gt_store() resolver (works agent or benchmark mode).
  • UI: each Vitals strip shows 'N/M annotated' + a Push to HF button (disabled when nothing annotated; inline outcome: Pushed N / Set HF_TOKEN / Log in / Failed). Verified in-browser.
  • 38 ELN tests green.

Per the plan-mode↔ELN fold study (committed), the flywheel is model-agnostic so it ships independent of the fold. Next: ELN agent tools, then the fold (Experiment→phase-role).

… fold

15 stories across the 4 seed programs + longitudinal/multi-user, written to SEE the
fold. Verdict: they CONFIRM Option C (fold; Experiment->phase-role; keep Strain/
Hypothesis/Result) and refine it with a 5-item punch list: (a) paired within-embryo
designs break the 1:1 arm<->session assumption (one session scored N ways); (b) no
Result->next-PlanItem authoring tool (the loop-closer); (c) Strain is lifecycle state
minted by a genetics PlanItem, not a static ref; (d) multi-annotator ground_truth +
adjudication node; (e) cross-campaign spanning Results have no home. Deepest insight:
the fold is 'one perception call wearing two hats' (live decision gate AND retrospective
ground-truth whose accuracy IS the Result).
@pskeshu pskeshu changed the base branch from feature/temperature-operations-all to development July 11, 2026 05:25
pskeshu added 2 commits July 11, 2026 10:55
- ruff format over 15 ELN files; wrap an overlong line in
  load_seed_programs.py (E501)
- export_service: only call asdict on dataclass instances, not types
- data.py ground-truth route: index start_timepoint directly and catch
  KeyError, so mypy sees no None reaching int()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant