Skip to main content

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, key PMID
    • /target-assessment/antibodies/target, key Antibody + Provider
    • /evidence/target-pathway/target, key image_url
    • /target-assessment/assayability/target, key ChEMBL ID
  • Entity-type folders: cached_data_json/target_disease/<target>-<disease>.json and cached_data_json/target/<target>.json
  • Implemented ops: delete, add, undo
  • Deferred to v2: patch, accept CLI 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: typer 0.12.x is incompatible with click 8.4+ (released April 2026) — options like --path get misparsed as positional args and --version stops exiting eagerly. The pins in requirements.txt and the typer = "^0.15.0" line in pyproject.toml sidestep 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):

  1. Snapshots the current served file's section (pre-refresh Base)
  2. Clears the section key from served JSON + redis
  3. Lets the pipeline regenerate from scratch
  4. 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:

  1. Runs entity status and presents the worklist as a markdown table
  2. Surfaces brownfield-from-git evidence (commits walked, historical adds/removes) prominently before anything else
  3. Walks decisions one-by-one, calling op delete / op add with --json after explicit confirmation
  4. On show me records, renders entity records --page N --page-size 20 as a paginated markdown table with delete actions per row
  5. On diff or preview, calls entity diff (read-only) and renders the four delta tables
  6. On session end, calls entity finalize and 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.py stub exists; mcp serve subcommand 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_disease entities
  • 26/26 tests passing
  • Pipeline hook in build_dossier.py ready behind the IS_REFRESH / CURATE_ENABLED flags
  • Scientist-facing skill installed via dev install-skill

Roadmap (deferred to v2)

  • op patch (field-level updates) — merge engine handles them, no CLI yet
  • op accept (resolve conflicts via choice)
  • MCP server runtime (mcp serve subcommand) 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

dbtips_curate-0.2.0.tar.gz (75.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dbtips_curate-0.2.0-py3-none-any.whl (92.8 kB view details)

Uploaded Python 3

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

Hashes for dbtips_curate-0.2.0.tar.gz
Algorithm Hash digest
SHA256 850464005a79dce08b09cf519d271b646b9d42a41250f1e68f0f027be162e524
MD5 7b44a958948e8a2f3035b1e0a789f6c1
BLAKE2b-256 6aea94daae7557437e96024cd58744984dfa9eaa854543c78030f2874564bd3f

See more details on using hashes here.

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

Hashes for dbtips_curate-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 68dde5bde4374759d6c080faef720a75354b1d46be478785e3d4095e4e7ecc27
MD5 a79bad5f46b1bf852b538313a30417a7
BLAKE2b-256 c51e054fbbd69e17e3ecde9080393977edffce6fb3b6387d6dba323de25ad90f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page