Three-way merge curation for dbtips pipeline-generated dossier caches. Ships agentskills.io-compatible skills for Claude Code, OpenAI Codex, and Google Antigravity.
Project description
dbtips-curate
Three-way merge curation for dbtips pipeline-generated dossier JSON caches.
Scientists hand-curate cache files after pipeline generation (deleting noise,
adding records from external sources, patching status fields). When the
pipeline refreshes, those edits get wiped. dbtips-curate preserves scientist
intent as an append-only git-tracked operation log and replays it onto every
refresh — so the merged served file is always "fresh pipeline data + scientist
intent" without manual re-curation.
Scope
- Sections (section-agnostic engine; add more via config — see
docs/adding-sections.md):
/evidence/target-literature/—target_disease, keyPMID/target-assessment/antibodies/—target, keyAntibody+Provider/evidence/target-pathway/—target, keyimage_url/target-assessment/assayability/—target, keyChEMBL ID
- Entity-type folders:
cached_data_json/target_disease/<target>-<disease>.jsonandcached_data_json/target/<target>.json - Implemented ops:
delete,add,undo - Deferred to v2:
patch,acceptCLI commands (merge engine handles both)
Adding a new section? Read docs/adding-sections.md — it has the data constraints to check, a copy-paste validator, and the config-vs-code decision flow.
How it actually works (the model in one paragraph)
Every tracked entity has a curation/<entity-type>/<id>/ folder containing:
immutable raw snapshots of the section (Base for 3-way merge), an append-only
ops.jsonl (scientist intent), state.json (mode + pointers), and per-refresh
changelogs. Each scientist action is one git commit, so blame works on every
clinical decision. On every pipeline refresh, entity ingest takes a fresh
snapshot (= Theirs), runs a 3-way merge (Base + Theirs + ops → Merged), and
splices the merged section back into the served file. Scientist edits survive
across refreshes; pipeline updates land on top.
Install
With Poetry (recommended — resolves against pyproject.toml):
cd dbtips-curate
poetry install
poetry run dbtips-curate --version # should print dbtips-curate/0.1.0
Without Poetry (pip + pinned requirements.txt):
cd dbtips-curate
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install -e .
dbtips-curate --version # should print dbtips-curate/0.1.0
For the test suite, use requirements-dev.txt on top of either flow.
Version note:
typer0.12.x is incompatible withclick8.4+ (released April 2026) — options like--pathget misparsed as positional args and--versionstops exiting eagerly. The pins inrequirements.txtand thetyper = "^0.15.0"line inpyproject.tomlsidestep this. If you see "Got unexpected extra argument" or "Missing command" on a valid flag, upgrade typer.
One-time setup
From the platform repo root, create the curation folder + per-team config:
poetry run dbtips-curate dev init \
--path backend/res-immunology-automation/res_immunology_automation/src/scripts/curation
This drops .dbtips-curate.toml next to cached_data_json/ and registers the
literature section's identity key (PMID). The file is committed to git so
every scientist gets identical config.
To install the Claude Code skill (so Claude can drive the CLI conversationally):
poetry run dbtips-curate dev install-skill
Drops SKILL.md into ~/.claude/skills/dbtips-curate/. Restart Claude Code
(terminal, desktop app, or IDE extension) and say curate <entity-id>.
Commands at a glance
dbtips-curate
entity list | status | track | snapshot | diff | ingest | records | finalize
op delete | add
history ops | report
dev init | install-skill
Every command supports --json for machine-readable output, exits 0/1/2 for
success / runtime error / usage error, and follows the contract:
{"ok": true, "data": {...}} on success, {"ok": false, "error": {"code", "message", "hint"}} on error.
entity — lifecycle
| Command | Purpose |
|---|---|
entity list [--type T] |
All tracked entities of one type with their mode + last refresh |
entity track <id> |
Bootstrap. Walks git history of the served file → synthesizes delete ops for historical removals; flags historical additions for scientist review; sets mode based on what was found (see modes below). --skip-history opts out. |
entity snapshot <id> [--reason ...] |
Capture the current served file's section as an immutable raw snapshot. First snapshot becomes Base for 3-way merge. |
entity status <id> |
Prioritized worklist: conflicts → historical_additions → bootstrap_candidates → resurrected → new from pipeline. Includes suggested_filters for greenfield noise triage. |
entity records <id> [--page N --page-size 20] [--full] [--fields F1,F2] |
List current records, paginated. For tabular review in chat — Claude renders as markdown table with delete action per row. |
entity diff <id> |
Read-only preview of the 3-way merge vs the current served file. Writes nothing. Use before ingest if scientist wants to see the table first. |
entity ingest <id> |
Take fresh snapshot → 3-way merge → splice merged section back into served file. Writes changelog + conflicts file. This is what the pipeline calls. |
entity finalize <id> [--no-push] |
Mark current review complete: advance last_reviewed_raw, clear bootstrap_candidates, prune old snapshots, git commit. Pushes to remote if auto_push_on = "finalize". |
op — scientist actions
| Command | Purpose |
|---|---|
op delete <id> --section <s> --key K=V --reason "..." |
Tombstone a record. Whole-record delete. Per-op git commit. |
op add <id> --section <s> --record-file F --source "..." |
Add a record from an external source. Validates against schema (PMID/Title/Year required for literature). Use - to read from stdin. |
op undo <id> --undoes <op_id> --reason "..." |
Cancel a prior op. Endpoint inferred from the target op. Rejects unknown op_ids, undo-of-undo, and double-undo. Original op stays in the log for audit. |
All support --dry-run to preview what would be logged without writing.
history — read-only views
| Command | Purpose |
|---|---|
history ops <id> [--since DATE] [--actor EMAIL] |
The full operation log with optional filters. |
history report <id> [--output FILE] [--open] |
Generate standalone HTML report (all queues + ops table). --open launches it in your browser. Good for review meetings or async sharing. |
dev — setup + maintenance
| Command | Purpose |
|---|---|
dev init --path DIR |
Create the curation folder + write a default .dbtips-curate.toml. |
dev install-skill [--home DIR] |
Drop SKILL.md into ~/.claude/skills/dbtips-curate/. |
Mode state machine
| Mode | Set by | Meaning |
|---|---|---|
greenfield |
track with no git history changes |
Fresh pipeline output, no prior scientist edits detected |
brownfield-from-git |
track when git history shows record-level adds/removes in past commits |
Prior scientist edits detected via git history; synthesized delete ops + historical_additions queue surfaced |
brownfield-detected |
ingest (auto-promotion) when pipeline output drops Base records that aren't tombstoned |
Records preserved as bootstrap_candidates pending scientist confirmation |
brownfield-bootstrap |
Legacy label; behaves identically | (Backward compatible) |
active |
finalize after first review session |
Steady-state operation |
Filesystem layout
backend/.../scripts/
cached_data_json/ (pipeline writes here, served to clients)
curation/ (this tool writes here, tracked in git)
.dbtips-curate.toml (per-team config + section schema registry)
target_disease/<entity-id>/
raw/<ts>.json (immutable section snapshots)
ops.jsonl (append-only scientist operations)
state.json (mode + last_reviewed_raw + git history summary)
conflicts/<ts>.json (open conflicts after last merge)
changelog/<ts>.md (human-readable per-refresh summary)
merged.json (post-merge section, spliced back into served file)
reports/<ts>.html (HTML reports — only created on demand)
Three-way merge semantics
Each entity ingest runs:
Base = snapshot at state.last_reviewed_raw (what scientist last accepted)
Theirs = fresh snapshot of current served file (what pipeline just produced)
Ours = apply_ops(Base, ops.jsonl) (scientist intent layered on Base)
Merged = three_way_merge(Base, Theirs, Ours)
Per identity-keyed record (PMID for literature):
| In Base | In Theirs | Has op | Result |
|---|---|---|---|
| ✓ | ✗ | tombstone | silent (both agree to remove) |
| ✓ | ✗ | none | removed_by_pipeline queue — in brownfield modes, preserved as bootstrap candidate |
| ✗ | ✓ | none | new_from_pipeline queue — added to merged |
| ✗ | ✓ | tombstone | dropped from merged, logged as resurrected_tombstone |
| ✓ | ✓ | patch | per-field merge; pipeline-changed + scientist-patched same field → conflict (scientist wins by default) |
| ✗ | ✗ | add | scientist-added record, included in merged |
Pipeline integration (build_dossier.py)
Already wired in via a CURATION HOOK block at the top of build_dossier.py.
Two knobs:
IS_REFRESH: bool = False # True = clear section + redis before pipeline regen
CURATE_ENABLED: bool = True # False = bypass the hook entirely (rollback)
With IS_REFRESH = True, the hook (in order):
- Snapshots the current served file's section (pre-refresh Base)
- Clears the section key from served JSON + redis
- Lets the pipeline regenerate from scratch
- Calls
entity ingest→ 3-way merge → splices merged back into served file
With IS_REFRESH = False (the safe default), only step 4 runs — keeps
curation state in sync without forcing regeneration.
Driving from Claude (Claude Code only)
After dev install-skill, in any Claude Code session:
curate acvr2a-no-disease
Claude reads the SKILL.md, then:
- Runs
entity statusand presents the worklist as a markdown table - Surfaces brownfield-from-git evidence (commits walked, historical adds/removes) prominently before anything else
- Walks decisions one-by-one, calling
op delete/op addwith--jsonafter explicit confirmation - On
show me records, rendersentity records --page N --page-size 20as a paginated markdown table with delete actions per row - On
difforpreview, callsentity diff(read-only) and renders the four delta tables - On session end, calls
entity finalizeand summarizes
Claude Code is required. Skills work in Claude Code (terminal, desktop app, IDE extensions). The Claude.ai consumer Desktop app would need an MCP server wrapper (the
mcp_server.pystub exists;mcp servesubcommand not built).
Testing
make test # 26 tests covering merge engine + CLI end-to-end + git history walk + records pagination
The fixture in tests/fixtures/acvr2a-no-disease.json is a real 5-record slice
of the production literature section. End-to-end tests run the full lifecycle
(track → snapshot → ingest → op delete → simulate refresh → ingest → finalize)
against a temp git repo mirroring the production layout.
Status
- Production-ready for literature curation on
target_diseaseentities - 26/26 tests passing
- Pipeline hook in
build_dossier.pyready behind theIS_REFRESH/CURATE_ENABLEDflags - Scientist-facing skill installed via
dev install-skill
Roadmap (deferred to v2)
op patch(field-level updates) — merge engine handles them, no CLI yetop accept(resolve conflicts via choice)- MCP server runtime (
mcp servesubcommand) for Claude.ai consumer Desktop - Field-level brownfield detection (needed when expanding to clinical trial endpoints)
- Other section schemas:
/evidence/search-patent/,/market-intelligence/*, etc.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbtips_curate-0.2.0.tar.gz.
File metadata
- Download URL: dbtips_curate-0.2.0.tar.gz
- Upload date:
- Size: 75.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
850464005a79dce08b09cf519d271b646b9d42a41250f1e68f0f027be162e524
|
|
| MD5 |
7b44a958948e8a2f3035b1e0a789f6c1
|
|
| BLAKE2b-256 |
6aea94daae7557437e96024cd58744984dfa9eaa854543c78030f2874564bd3f
|
File details
Details for the file dbtips_curate-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dbtips_curate-0.2.0-py3-none-any.whl
- Upload date:
- Size: 92.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68dde5bde4374759d6c080faef720a75354b1d46be478785e3d4095e4e7ecc27
|
|
| MD5 |
a79bad5f46b1bf852b538313a30417a7
|
|
| BLAKE2b-256 |
c51e054fbbd69e17e3ecde9080393977edffce6fb3b6387d6dba323de25ad90f
|