Skip to content

Add generic probe data model and private storage - #131

Open
Ely-S wants to merge 3 commits into
study/psychedelicsfrom
pr/1-data-model-storage
Open

Add generic probe data model and private storage#131
Ely-S wants to merge 3 commits into
study/psychedelicsfrom
pr/1-data-model-storage

Conversation

@Ely-S

@Ely-S Ely-S commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Why

The second-pass extraction prototype combines probe-specific pharmacology logic, execution state, and persistence in one large script. That makes the next engine difficult to reuse and risks leaking quote-bearing source material into shared analysis artifacts.

Approach taken

  • Add probe-agnostic Pydantic contracts for run identity, ordered cohort members, bounded units, private source windows, provider attempts, usage, evidence anchors, and generic claims.
  • Reject undeclared fields and enforce model-level invariants that can be checked without knowing a probe's domain.
  • Add a transactional per-probe SQLite store with run, cohort, unit, source-window, attempt, and claim tables.
  • Keep raw responses, source text, source IDs, quotes, and hashed author IDs inside the private data/probes/ boundary.
  • Include indexes for unit resume, response-cache lookup, source-window membership, and claim queries.

User-facing changes

This PR adds the reusable data and persistence layer only. It does not change the existing psychedelic study, invoke an LLM, or add CLI behavior; the planning/execution engine and probe migration are intentionally deferred to later stacked PRs.

Detailed test plan

  1. From the repository root, run uv run python -m compileall -q probes and verify success.
  2. Run a temporary-database smoke script that constructs ProbeRun, CohortMember, Unit, Attempt, and Claim objects; persists them through ProbeStore; verifies accepted-response cache lookup; verifies unfinished-unit discovery; then marks the unit complete and verifies resume state is empty.
  3. Run LLM_PROVIDER=anthropic uv run pytest variable_extraction/tests -q; the current repository package suite reports 316 passing tests.
  4. Confirm git diff --name-only study/psychedelics...HEAD contains only probes/__init__.py, probes/models.py, and probes/store.py for this PR.

The full repository test command is currently blocked during collection by an unrelated environment mismatch: an existing test imports a utility that rejects the pre-existing LLM_PROVIDER=openai environment value.

Made with Cursor

Ely-S and others added 3 commits August 9, 2026 13:18
Introduce the shared, probe-agnostic contracts for runs, cohort members, source
windows, units, provider attempts, evidence anchors, and claims. Add a
transactional per-probe SQLite store that keeps raw responses, source text, and
quotes private while preserving the identity, cache, accounting, and resume
fields required by the second-pass engine.

Co-authored-by: Cursor <cursoragent@cursor.com>
Record what the shipped data model and private schema actually contain, and
where they deliberately diverge from the full design: probe_run stays in the
private database, source_window is a table so claim membership is a foreign
key rather than an engine check, and attempt carries no variant column. State
plainly what V1 leaves out so a reader does not mistake the design for the
build.

Remove unfinished_unit_keys, which no caller reaches -- resume derives pending
work from unit status. Correct the cohort-order docstring: the store persists
whatever order it is handed and does not impose one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eight review findings against the storage layer, none of which the
existing callers would have surfaced on their own.

Whitespace stripping applied to every string field, so a stored
response body no longer matched the checksum written beside it and a
stored source window no longer matched the corpus text it was selected
from. Both are audit records, so add a non-stripping base for the
models that carry private payloads.

The default database path was relative to the working directory, and
the WAL sidecars were not ignored. A probe run from a subdirectory
wrote quote-bearing uncommitted pages to a tracked location. Anchor
the default to the repository root and ignore the sidecars, so the
L11 boundary holds structurally rather than by convention.

Cache lookup ordered on ISO-8601 text, which is not a time ordering
across offsets: an older attempt could win and serve a stale body.
Order on a normalized UTC column instead.

Also: run inserts now roll back instead of leaving the connection in
an open write transaction; status updates on an unknown key raise
rather than silently no-op, which would strand a unit as running and
re-dispatch it on every resume; the connection is shared across
threads under a lock, ahead of the engine's concurrency; and two
indexes that restated their table's primary key are dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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