dbtips-curate 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. dbtips_curate-0.2.0/PKG-INFO +303 -0
  2. dbtips_curate-0.2.0/README.md +284 -0
  3. dbtips_curate-0.2.0/pyproject.toml +49 -0
  4. dbtips_curate-0.2.0/src/dbtips_curate/__init__.py +1 -0
  5. dbtips_curate-0.2.0/src/dbtips_curate/cli/__init__.py +0 -0
  6. dbtips_curate-0.2.0/src/dbtips_curate/cli/_common.py +67 -0
  7. dbtips_curate-0.2.0/src/dbtips_curate/cli/_output.py +51 -0
  8. dbtips_curate-0.2.0/src/dbtips_curate/cli/_widget.py +207 -0
  9. dbtips_curate-0.2.0/src/dbtips_curate/cli/dev.py +358 -0
  10. dbtips_curate-0.2.0/src/dbtips_curate/cli/entity.py +723 -0
  11. dbtips_curate-0.2.0/src/dbtips_curate/cli/history.py +213 -0
  12. dbtips_curate-0.2.0/src/dbtips_curate/cli/main.py +70 -0
  13. dbtips_curate-0.2.0/src/dbtips_curate/cli/op.py +399 -0
  14. dbtips_curate-0.2.0/src/dbtips_curate/core/__init__.py +0 -0
  15. dbtips_curate-0.2.0/src/dbtips_curate/core/config.py +181 -0
  16. dbtips_curate-0.2.0/src/dbtips_curate/core/fs.py +154 -0
  17. dbtips_curate-0.2.0/src/dbtips_curate/core/git.py +80 -0
  18. dbtips_curate-0.2.0/src/dbtips_curate/core/git_history.py +183 -0
  19. dbtips_curate-0.2.0/src/dbtips_curate/core/merge.py +314 -0
  20. dbtips_curate-0.2.0/src/dbtips_curate/core/ops.py +78 -0
  21. dbtips_curate-0.2.0/src/dbtips_curate/core/schema.py +161 -0
  22. dbtips_curate-0.2.0/src/dbtips_curate/core/worklist.py +166 -0
  23. dbtips_curate-0.2.0/src/dbtips_curate/mcp_server.py +114 -0
  24. dbtips_curate-0.2.0/src/dbtips_curate/skill/_deferred/targetability-design-note.md +170 -0
  25. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate/SKILL.md +400 -0
  26. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-antibodies/SKILL.md +219 -0
  27. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-assayability/SKILL.md +175 -0
  28. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-gene-essentiality/SKILL.md +107 -0
  29. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-ontology/SKILL.md +129 -0
  30. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-orthologs/SKILL.md +123 -0
  31. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-supplementary-materials/SKILL.md +147 -0
  32. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-target-literature/SKILL.md +286 -0
  33. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-target-pathway/SKILL.md +166 -0
  34. dbtips_curate-0.2.0/src/dbtips_curate/skill/dbtips-curate-target-pipeline/SKILL.md +291 -0
@@ -0,0 +1,303 @@
1
+ Metadata-Version: 2.4
2
+ Name: dbtips-curate
3
+ Version: 0.2.0
4
+ Summary: Three-way merge curation for dbtips pipeline-generated dossier caches. Ships agentskills.io-compatible skills for Claude Code, OpenAI Codex, and Google Antigravity.
5
+ Author: Aganitha
6
+ Author-email: dev@aganitha.ai
7
+ Requires-Python: >=3.11,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: click (>=8.1,<9.0)
14
+ Requires-Dist: pydantic (>=2.6.0,<3.0.0)
15
+ Requires-Dist: rich (>=13.7.0,<14.0.0)
16
+ Requires-Dist: typer (>=0.15.0,<0.16.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # dbtips-curate
20
+
21
+ Three-way merge curation for dbtips pipeline-generated dossier JSON caches.
22
+
23
+ Scientists hand-curate cache files after pipeline generation (deleting noise,
24
+ adding records from external sources, patching status fields). When the
25
+ pipeline refreshes, those edits get wiped. `dbtips-curate` preserves scientist
26
+ intent as an append-only git-tracked operation log and replays it onto every
27
+ refresh — so the merged served file is always "fresh pipeline data + scientist
28
+ intent" without manual re-curation.
29
+
30
+ ## Scope
31
+
32
+ - Sections (section-agnostic engine; add more via config — see
33
+ [docs/adding-sections.md](docs/adding-sections.md)):
34
+ - `/evidence/target-literature/` — `target_disease`, key `PMID`
35
+ - `/target-assessment/antibodies/` — `target`, key `Antibody` + `Provider`
36
+ - `/evidence/target-pathway/` — `target`, key `image_url`
37
+ - `/target-assessment/assayability/` — `target`, key `ChEMBL ID`
38
+ - Entity-type folders: `cached_data_json/target_disease/<target>-<disease>.json`
39
+ and `cached_data_json/target/<target>.json`
40
+ - Implemented ops: `delete`, `add`, `undo`
41
+ - Deferred to v2: `patch`, `accept` CLI commands (merge engine handles both)
42
+
43
+ **Adding a new section?** Read [docs/adding-sections.md](docs/adding-sections.md)
44
+ — it has the data constraints to check, a copy-paste validator, and the
45
+ config-vs-code decision flow.
46
+
47
+ ## How it actually works (the model in one paragraph)
48
+
49
+ Every tracked entity has a `curation/<entity-type>/<id>/` folder containing:
50
+ immutable raw snapshots of the section (Base for 3-way merge), an append-only
51
+ `ops.jsonl` (scientist intent), `state.json` (mode + pointers), and per-refresh
52
+ changelogs. Each scientist action is one git commit, so blame works on every
53
+ clinical decision. On every pipeline refresh, `entity ingest` takes a fresh
54
+ snapshot (= Theirs), runs a 3-way merge (Base + Theirs + ops → Merged), and
55
+ splices the merged section back into the served file. Scientist edits survive
56
+ across refreshes; pipeline updates land on top.
57
+
58
+ ## Install
59
+
60
+ The package is published to public PyPI. Any Python tool runner works.
61
+
62
+ ### End-user install (recommended)
63
+
64
+ ```bash
65
+ # One-shot, no persistent install (great for trying it out):
66
+ uvx dbtips-curate --version
67
+
68
+ # Persistent global install (recommended for daily use):
69
+ uv tool install dbtips-curate
70
+ # or: pipx install dbtips-curate
71
+ # or: pip install dbtips-curate
72
+
73
+ dbtips-curate --version # dbtips-curate/0.2.0
74
+ ```
75
+
76
+ Then bootstrap the skills into your AI coding harnesses:
77
+
78
+ ```bash
79
+ dbtips-curate dev bootstrap
80
+ ```
81
+
82
+ Interactive menu picks Claude / Codex / Antigravity / all. Non-interactive form:
83
+
84
+ ```bash
85
+ dbtips-curate dev bootstrap --targets all
86
+ dbtips-curate dev bootstrap --targets claude,codex
87
+ dbtips-curate dev bootstrap --targets claude --project .
88
+ ```
89
+
90
+ ### Skill install matrix
91
+
92
+ Every supported harness reads the same [agentskills.io](https://agentskills.io)
93
+ `SKILL.md` format we ship — no conversion.
94
+
95
+ | Harness | Global path | Project path |
96
+ |---|---|---|
97
+ | Claude Code | `~/.claude/skills/<name>/` | `<project>/.claude/skills/<name>/` |
98
+ | OpenAI Codex | `~/.agents/skills/<name>/` | `<project>/.agents/skills/<name>/` |
99
+ | Google Antigravity | `~/.gemini/config/skills/<name>/` | `<project>/.agents/skills/<name>/` (shared with Codex) |
100
+
101
+ ### Dev install (contributors only)
102
+
103
+ ```bash
104
+ git clone https://github.com/aganitha/dbtips-curate.git
105
+ cd dbtips-curate
106
+ poetry install
107
+ poetry run dbtips-curate --version
108
+ ```
109
+
110
+ For the test suite: `poetry install --with dev && poetry run pytest`.
111
+
112
+ > **Version note:** `typer` 0.12.x is incompatible with `click` 8.4+ (released
113
+ > April 2026) — options like `--path` get misparsed as positional args and
114
+ > `--version` stops exiting eagerly. The `typer = "^0.15.0"` pin in
115
+ > `pyproject.toml` sidesteps this. If you see *"Got unexpected extra argument"*
116
+ > or *"Missing command"* on a valid flag, upgrade typer.
117
+
118
+ ## One-time setup (per platform repo)
119
+
120
+ From the platform repo root, create the curation folder + per-team config:
121
+
122
+ ```bash
123
+ dbtips-curate dev init \
124
+ --path backend/res-immunology-automation/res_immunology_automation/src/scripts/curation
125
+ ```
126
+
127
+ This drops `.dbtips-curate.toml` next to `cached_data_json/` and registers all
128
+ ten curated section endpoints (literature, antibodies, pathway, assayability,
129
+ target-pipeline, ontology, gene-essentiality, orthologs, supplementary
130
+ materials). The file is committed to git so every scientist gets identical
131
+ config.
132
+
133
+ ## Commands at a glance
134
+
135
+ ```
136
+ dbtips-curate
137
+ entity list | status | track | snapshot | diff | ingest | records | compare | finalize
138
+ op delete | add | patch | undo
139
+ history ops | report
140
+ dev init | bootstrap | install-skill
141
+ ```
142
+
143
+ Every command supports `--json` for machine-readable output, exits 0/1/2 for
144
+ success / runtime error / usage error, and follows the contract:
145
+ `{"ok": true, "data": {...}}` on success, `{"ok": false, "error": {"code", "message", "hint"}}` on error.
146
+
147
+ ### `entity` — lifecycle
148
+
149
+ | Command | Purpose |
150
+ |---|---|
151
+ | `entity list [--type T]` | All tracked entities of one type with their mode + last refresh |
152
+ | `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. |
153
+ | `entity snapshot <id> [--reason ...]` | Capture the current served file's section as an immutable raw snapshot. First snapshot becomes Base for 3-way merge. |
154
+ | `entity status <id>` | Prioritized worklist: conflicts → historical_additions → bootstrap_candidates → resurrected → new from pipeline. Includes `suggested_filters` for greenfield noise triage. |
155
+ | `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. |
156
+ | `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. |
157
+ | `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. |
158
+ | `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"`. |
159
+
160
+ ### `op` — scientist actions
161
+
162
+ | Command | Purpose |
163
+ |---|---|
164
+ | `op delete <id> --section <s> --key K=V [--key K2=V2 ...] --reason "..."` | Tombstone a record. Whole-record delete. Compound-key sections pass one `--key` flag per identity field. Per-op git commit. |
165
+ | `op add <id> --section <s> --record-file F --source "..."` | Add a record from an external source. Validates against the section's `required_fields`. Use `-` to read from stdin. |
166
+ | `op patch <id> --section <s> --key K=V [--key K2=V2 ...] --set field=value [--set ...] --reason "..."` | Change one or more fields on an existing record. Values are JSON-decoded when possible (`Phase=3` → int, `Approved=true` → bool). Identity fields cannot be `--set` (rejects with `PATCH_TOUCHES_IDENTITY`). |
167
+ | `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. |
168
+
169
+ All support `--dry-run` to preview what would be logged without writing.
170
+
171
+ ### `history` — read-only views
172
+
173
+ | Command | Purpose |
174
+ |---|---|
175
+ | `history ops <id> [--since DATE] [--actor EMAIL]` | The full operation log with optional filters. |
176
+ | `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. |
177
+
178
+ ### `dev` — setup + maintenance
179
+
180
+ | Command | Purpose |
181
+ |---|---|
182
+ | `dev init --path DIR` | Create the curation folder + write a default `.dbtips-curate.toml`. |
183
+ | `dev bootstrap [--targets claude,codex,antigravity\|all] [--project DIR]` | Install every skill into the chosen AI coding harnesses. Interactive menu if `--targets` omitted. `--project .` also drops skills into the current repo's workspace-scope skill folder. Idempotent. |
184
+ | `dev install-skill [--target claude\|codex\|antigravity] [--home DIR] [--only NAME]` | Single-target install primitive used by `bootstrap`. Prefer `bootstrap` for end-user setup. |
185
+
186
+ ## Mode state machine
187
+
188
+ | Mode | Set by | Meaning |
189
+ |---|---|---|
190
+ | `greenfield` | `track` with no git history changes | Fresh pipeline output, no prior scientist edits detected |
191
+ | `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 |
192
+ | `brownfield-detected` | `ingest` (auto-promotion) when pipeline output drops Base records that aren't tombstoned | Records preserved as bootstrap_candidates pending scientist confirmation |
193
+ | `brownfield-bootstrap` | Legacy label; behaves identically | (Backward compatible) |
194
+ | `active` | `finalize` after first review session | Steady-state operation |
195
+
196
+ ## Filesystem layout
197
+
198
+ ```
199
+ backend/.../scripts/
200
+ cached_data_json/ (pipeline writes here, served to clients)
201
+ curation/ (this tool writes here, tracked in git)
202
+ .dbtips-curate.toml (per-team config + section schema registry)
203
+ target_disease/<entity-id>/
204
+ raw/<ts>.json (immutable section snapshots)
205
+ ops.jsonl (append-only scientist operations)
206
+ state.json (mode + last_reviewed_raw + git history summary)
207
+ conflicts/<ts>.json (open conflicts after last merge)
208
+ changelog/<ts>.md (human-readable per-refresh summary)
209
+ merged.json (post-merge section, spliced back into served file)
210
+ reports/<ts>.html (HTML reports — only created on demand)
211
+ ```
212
+
213
+ ## Three-way merge semantics
214
+
215
+ Each `entity ingest` runs:
216
+
217
+ ```
218
+ Base = snapshot at state.last_reviewed_raw (what scientist last accepted)
219
+ Theirs = fresh snapshot of current served file (what pipeline just produced)
220
+ Ours = apply_ops(Base, ops.jsonl) (scientist intent layered on Base)
221
+
222
+ Merged = three_way_merge(Base, Theirs, Ours)
223
+ ```
224
+
225
+ Per identity-keyed record (PMID for literature):
226
+
227
+ | In Base | In Theirs | Has op | Result |
228
+ |---|---|---|---|
229
+ | ✓ | ✗ | tombstone | silent (both agree to remove) |
230
+ | ✓ | ✗ | none | `removed_by_pipeline` queue — in brownfield modes, **preserved** as bootstrap candidate |
231
+ | ✗ | ✓ | none | `new_from_pipeline` queue — added to merged |
232
+ | ✗ | ✓ | tombstone | dropped from merged, logged as `resurrected_tombstone` |
233
+ | ✓ | ✓ | patch | per-field merge; pipeline-changed + scientist-patched same field → `conflict` (scientist wins by default) |
234
+ | ✗ | ✗ | add | scientist-added record, included in merged |
235
+
236
+ ## Pipeline integration (build_dossier.py)
237
+
238
+ Already wired in via a CURATION HOOK block at the top of `build_dossier.py`.
239
+ Two knobs:
240
+
241
+ ```python
242
+ IS_REFRESH: bool = False # True = clear section + redis before pipeline regen
243
+ CURATE_ENABLED: bool = True # False = bypass the hook entirely (rollback)
244
+ ```
245
+
246
+ With `IS_REFRESH = True`, the hook (in order):
247
+ 1. Snapshots the current served file's section (pre-refresh Base)
248
+ 2. Clears the section key from served JSON + redis
249
+ 3. Lets the pipeline regenerate from scratch
250
+ 4. Calls `entity ingest` → 3-way merge → splices merged back into served file
251
+
252
+ With `IS_REFRESH = False` (the safe default), only step 4 runs — keeps
253
+ curation state in sync without forcing regeneration.
254
+
255
+ ## Driving from Claude (Claude Code only)
256
+
257
+ After `dev install-skill`, in any Claude Code session:
258
+
259
+ ```
260
+ curate acvr2a-no-disease
261
+ ```
262
+
263
+ Claude reads the SKILL.md, then:
264
+
265
+ 1. Runs `entity status` and presents the worklist as a markdown table
266
+ 2. Surfaces brownfield-from-git evidence (commits walked, historical adds/removes)
267
+ prominently before anything else
268
+ 3. Walks decisions one-by-one, calling `op delete` / `op add` with `--json` after explicit confirmation
269
+ 4. On `show me records`, renders `entity records --page N --page-size 20` as a paginated markdown table with delete actions per row
270
+ 5. On `diff` or `preview`, calls `entity diff` (read-only) and renders the four delta tables
271
+ 6. On session end, calls `entity finalize` and summarizes
272
+
273
+ > **Claude Code is required.** Skills work in Claude Code (terminal, desktop
274
+ > app, IDE extensions). The Claude.ai consumer Desktop app would need an MCP
275
+ > server wrapper (the `mcp_server.py` stub exists; `mcp serve` subcommand not
276
+ > built).
277
+
278
+ ## Testing
279
+
280
+ ```bash
281
+ make test # 26 tests covering merge engine + CLI end-to-end + git history walk + records pagination
282
+ ```
283
+
284
+ The fixture in `tests/fixtures/acvr2a-no-disease.json` is a real 5-record slice
285
+ of the production literature section. End-to-end tests run the full lifecycle
286
+ (track → snapshot → ingest → op delete → simulate refresh → ingest → finalize)
287
+ against a temp git repo mirroring the production layout.
288
+
289
+ ## Status
290
+
291
+ - **Production-ready for literature curation** on `target_disease` entities
292
+ - 26/26 tests passing
293
+ - Pipeline hook in `build_dossier.py` ready behind the `IS_REFRESH` / `CURATE_ENABLED` flags
294
+ - Scientist-facing skill installed via `dev install-skill`
295
+
296
+ ## Roadmap (deferred to v2)
297
+
298
+ - `op patch` (field-level updates) — merge engine handles them, no CLI yet
299
+ - `op accept` (resolve conflicts via choice)
300
+ - MCP server runtime (`mcp serve` subcommand) for Claude.ai consumer Desktop
301
+ - Field-level brownfield detection (needed when expanding to clinical trial endpoints)
302
+ - Other section schemas: `/evidence/search-patent/`, `/market-intelligence/*`, etc.
303
+
@@ -0,0 +1,284 @@
1
+ # dbtips-curate
2
+
3
+ Three-way merge curation for dbtips pipeline-generated dossier JSON caches.
4
+
5
+ Scientists hand-curate cache files after pipeline generation (deleting noise,
6
+ adding records from external sources, patching status fields). When the
7
+ pipeline refreshes, those edits get wiped. `dbtips-curate` preserves scientist
8
+ intent as an append-only git-tracked operation log and replays it onto every
9
+ refresh — so the merged served file is always "fresh pipeline data + scientist
10
+ intent" without manual re-curation.
11
+
12
+ ## Scope
13
+
14
+ - Sections (section-agnostic engine; add more via config — see
15
+ [docs/adding-sections.md](docs/adding-sections.md)):
16
+ - `/evidence/target-literature/` — `target_disease`, key `PMID`
17
+ - `/target-assessment/antibodies/` — `target`, key `Antibody` + `Provider`
18
+ - `/evidence/target-pathway/` — `target`, key `image_url`
19
+ - `/target-assessment/assayability/` — `target`, key `ChEMBL ID`
20
+ - Entity-type folders: `cached_data_json/target_disease/<target>-<disease>.json`
21
+ and `cached_data_json/target/<target>.json`
22
+ - Implemented ops: `delete`, `add`, `undo`
23
+ - Deferred to v2: `patch`, `accept` CLI commands (merge engine handles both)
24
+
25
+ **Adding a new section?** Read [docs/adding-sections.md](docs/adding-sections.md)
26
+ — it has the data constraints to check, a copy-paste validator, and the
27
+ config-vs-code decision flow.
28
+
29
+ ## How it actually works (the model in one paragraph)
30
+
31
+ Every tracked entity has a `curation/<entity-type>/<id>/` folder containing:
32
+ immutable raw snapshots of the section (Base for 3-way merge), an append-only
33
+ `ops.jsonl` (scientist intent), `state.json` (mode + pointers), and per-refresh
34
+ changelogs. Each scientist action is one git commit, so blame works on every
35
+ clinical decision. On every pipeline refresh, `entity ingest` takes a fresh
36
+ snapshot (= Theirs), runs a 3-way merge (Base + Theirs + ops → Merged), and
37
+ splices the merged section back into the served file. Scientist edits survive
38
+ across refreshes; pipeline updates land on top.
39
+
40
+ ## Install
41
+
42
+ The package is published to public PyPI. Any Python tool runner works.
43
+
44
+ ### End-user install (recommended)
45
+
46
+ ```bash
47
+ # One-shot, no persistent install (great for trying it out):
48
+ uvx dbtips-curate --version
49
+
50
+ # Persistent global install (recommended for daily use):
51
+ uv tool install dbtips-curate
52
+ # or: pipx install dbtips-curate
53
+ # or: pip install dbtips-curate
54
+
55
+ dbtips-curate --version # dbtips-curate/0.2.0
56
+ ```
57
+
58
+ Then bootstrap the skills into your AI coding harnesses:
59
+
60
+ ```bash
61
+ dbtips-curate dev bootstrap
62
+ ```
63
+
64
+ Interactive menu picks Claude / Codex / Antigravity / all. Non-interactive form:
65
+
66
+ ```bash
67
+ dbtips-curate dev bootstrap --targets all
68
+ dbtips-curate dev bootstrap --targets claude,codex
69
+ dbtips-curate dev bootstrap --targets claude --project .
70
+ ```
71
+
72
+ ### Skill install matrix
73
+
74
+ Every supported harness reads the same [agentskills.io](https://agentskills.io)
75
+ `SKILL.md` format we ship — no conversion.
76
+
77
+ | Harness | Global path | Project path |
78
+ |---|---|---|
79
+ | Claude Code | `~/.claude/skills/<name>/` | `<project>/.claude/skills/<name>/` |
80
+ | OpenAI Codex | `~/.agents/skills/<name>/` | `<project>/.agents/skills/<name>/` |
81
+ | Google Antigravity | `~/.gemini/config/skills/<name>/` | `<project>/.agents/skills/<name>/` (shared with Codex) |
82
+
83
+ ### Dev install (contributors only)
84
+
85
+ ```bash
86
+ git clone https://github.com/aganitha/dbtips-curate.git
87
+ cd dbtips-curate
88
+ poetry install
89
+ poetry run dbtips-curate --version
90
+ ```
91
+
92
+ For the test suite: `poetry install --with dev && poetry run pytest`.
93
+
94
+ > **Version note:** `typer` 0.12.x is incompatible with `click` 8.4+ (released
95
+ > April 2026) — options like `--path` get misparsed as positional args and
96
+ > `--version` stops exiting eagerly. The `typer = "^0.15.0"` pin in
97
+ > `pyproject.toml` sidesteps this. If you see *"Got unexpected extra argument"*
98
+ > or *"Missing command"* on a valid flag, upgrade typer.
99
+
100
+ ## One-time setup (per platform repo)
101
+
102
+ From the platform repo root, create the curation folder + per-team config:
103
+
104
+ ```bash
105
+ dbtips-curate dev init \
106
+ --path backend/res-immunology-automation/res_immunology_automation/src/scripts/curation
107
+ ```
108
+
109
+ This drops `.dbtips-curate.toml` next to `cached_data_json/` and registers all
110
+ ten curated section endpoints (literature, antibodies, pathway, assayability,
111
+ target-pipeline, ontology, gene-essentiality, orthologs, supplementary
112
+ materials). The file is committed to git so every scientist gets identical
113
+ config.
114
+
115
+ ## Commands at a glance
116
+
117
+ ```
118
+ dbtips-curate
119
+ entity list | status | track | snapshot | diff | ingest | records | compare | finalize
120
+ op delete | add | patch | undo
121
+ history ops | report
122
+ dev init | bootstrap | install-skill
123
+ ```
124
+
125
+ Every command supports `--json` for machine-readable output, exits 0/1/2 for
126
+ success / runtime error / usage error, and follows the contract:
127
+ `{"ok": true, "data": {...}}` on success, `{"ok": false, "error": {"code", "message", "hint"}}` on error.
128
+
129
+ ### `entity` — lifecycle
130
+
131
+ | Command | Purpose |
132
+ |---|---|
133
+ | `entity list [--type T]` | All tracked entities of one type with their mode + last refresh |
134
+ | `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. |
135
+ | `entity snapshot <id> [--reason ...]` | Capture the current served file's section as an immutable raw snapshot. First snapshot becomes Base for 3-way merge. |
136
+ | `entity status <id>` | Prioritized worklist: conflicts → historical_additions → bootstrap_candidates → resurrected → new from pipeline. Includes `suggested_filters` for greenfield noise triage. |
137
+ | `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. |
138
+ | `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. |
139
+ | `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. |
140
+ | `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"`. |
141
+
142
+ ### `op` — scientist actions
143
+
144
+ | Command | Purpose |
145
+ |---|---|
146
+ | `op delete <id> --section <s> --key K=V [--key K2=V2 ...] --reason "..."` | Tombstone a record. Whole-record delete. Compound-key sections pass one `--key` flag per identity field. Per-op git commit. |
147
+ | `op add <id> --section <s> --record-file F --source "..."` | Add a record from an external source. Validates against the section's `required_fields`. Use `-` to read from stdin. |
148
+ | `op patch <id> --section <s> --key K=V [--key K2=V2 ...] --set field=value [--set ...] --reason "..."` | Change one or more fields on an existing record. Values are JSON-decoded when possible (`Phase=3` → int, `Approved=true` → bool). Identity fields cannot be `--set` (rejects with `PATCH_TOUCHES_IDENTITY`). |
149
+ | `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. |
150
+
151
+ All support `--dry-run` to preview what would be logged without writing.
152
+
153
+ ### `history` — read-only views
154
+
155
+ | Command | Purpose |
156
+ |---|---|
157
+ | `history ops <id> [--since DATE] [--actor EMAIL]` | The full operation log with optional filters. |
158
+ | `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. |
159
+
160
+ ### `dev` — setup + maintenance
161
+
162
+ | Command | Purpose |
163
+ |---|---|
164
+ | `dev init --path DIR` | Create the curation folder + write a default `.dbtips-curate.toml`. |
165
+ | `dev bootstrap [--targets claude,codex,antigravity\|all] [--project DIR]` | Install every skill into the chosen AI coding harnesses. Interactive menu if `--targets` omitted. `--project .` also drops skills into the current repo's workspace-scope skill folder. Idempotent. |
166
+ | `dev install-skill [--target claude\|codex\|antigravity] [--home DIR] [--only NAME]` | Single-target install primitive used by `bootstrap`. Prefer `bootstrap` for end-user setup. |
167
+
168
+ ## Mode state machine
169
+
170
+ | Mode | Set by | Meaning |
171
+ |---|---|---|
172
+ | `greenfield` | `track` with no git history changes | Fresh pipeline output, no prior scientist edits detected |
173
+ | `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 |
174
+ | `brownfield-detected` | `ingest` (auto-promotion) when pipeline output drops Base records that aren't tombstoned | Records preserved as bootstrap_candidates pending scientist confirmation |
175
+ | `brownfield-bootstrap` | Legacy label; behaves identically | (Backward compatible) |
176
+ | `active` | `finalize` after first review session | Steady-state operation |
177
+
178
+ ## Filesystem layout
179
+
180
+ ```
181
+ backend/.../scripts/
182
+ cached_data_json/ (pipeline writes here, served to clients)
183
+ curation/ (this tool writes here, tracked in git)
184
+ .dbtips-curate.toml (per-team config + section schema registry)
185
+ target_disease/<entity-id>/
186
+ raw/<ts>.json (immutable section snapshots)
187
+ ops.jsonl (append-only scientist operations)
188
+ state.json (mode + last_reviewed_raw + git history summary)
189
+ conflicts/<ts>.json (open conflicts after last merge)
190
+ changelog/<ts>.md (human-readable per-refresh summary)
191
+ merged.json (post-merge section, spliced back into served file)
192
+ reports/<ts>.html (HTML reports — only created on demand)
193
+ ```
194
+
195
+ ## Three-way merge semantics
196
+
197
+ Each `entity ingest` runs:
198
+
199
+ ```
200
+ Base = snapshot at state.last_reviewed_raw (what scientist last accepted)
201
+ Theirs = fresh snapshot of current served file (what pipeline just produced)
202
+ Ours = apply_ops(Base, ops.jsonl) (scientist intent layered on Base)
203
+
204
+ Merged = three_way_merge(Base, Theirs, Ours)
205
+ ```
206
+
207
+ Per identity-keyed record (PMID for literature):
208
+
209
+ | In Base | In Theirs | Has op | Result |
210
+ |---|---|---|---|
211
+ | ✓ | ✗ | tombstone | silent (both agree to remove) |
212
+ | ✓ | ✗ | none | `removed_by_pipeline` queue — in brownfield modes, **preserved** as bootstrap candidate |
213
+ | ✗ | ✓ | none | `new_from_pipeline` queue — added to merged |
214
+ | ✗ | ✓ | tombstone | dropped from merged, logged as `resurrected_tombstone` |
215
+ | ✓ | ✓ | patch | per-field merge; pipeline-changed + scientist-patched same field → `conflict` (scientist wins by default) |
216
+ | ✗ | ✗ | add | scientist-added record, included in merged |
217
+
218
+ ## Pipeline integration (build_dossier.py)
219
+
220
+ Already wired in via a CURATION HOOK block at the top of `build_dossier.py`.
221
+ Two knobs:
222
+
223
+ ```python
224
+ IS_REFRESH: bool = False # True = clear section + redis before pipeline regen
225
+ CURATE_ENABLED: bool = True # False = bypass the hook entirely (rollback)
226
+ ```
227
+
228
+ With `IS_REFRESH = True`, the hook (in order):
229
+ 1. Snapshots the current served file's section (pre-refresh Base)
230
+ 2. Clears the section key from served JSON + redis
231
+ 3. Lets the pipeline regenerate from scratch
232
+ 4. Calls `entity ingest` → 3-way merge → splices merged back into served file
233
+
234
+ With `IS_REFRESH = False` (the safe default), only step 4 runs — keeps
235
+ curation state in sync without forcing regeneration.
236
+
237
+ ## Driving from Claude (Claude Code only)
238
+
239
+ After `dev install-skill`, in any Claude Code session:
240
+
241
+ ```
242
+ curate acvr2a-no-disease
243
+ ```
244
+
245
+ Claude reads the SKILL.md, then:
246
+
247
+ 1. Runs `entity status` and presents the worklist as a markdown table
248
+ 2. Surfaces brownfield-from-git evidence (commits walked, historical adds/removes)
249
+ prominently before anything else
250
+ 3. Walks decisions one-by-one, calling `op delete` / `op add` with `--json` after explicit confirmation
251
+ 4. On `show me records`, renders `entity records --page N --page-size 20` as a paginated markdown table with delete actions per row
252
+ 5. On `diff` or `preview`, calls `entity diff` (read-only) and renders the four delta tables
253
+ 6. On session end, calls `entity finalize` and summarizes
254
+
255
+ > **Claude Code is required.** Skills work in Claude Code (terminal, desktop
256
+ > app, IDE extensions). The Claude.ai consumer Desktop app would need an MCP
257
+ > server wrapper (the `mcp_server.py` stub exists; `mcp serve` subcommand not
258
+ > built).
259
+
260
+ ## Testing
261
+
262
+ ```bash
263
+ make test # 26 tests covering merge engine + CLI end-to-end + git history walk + records pagination
264
+ ```
265
+
266
+ The fixture in `tests/fixtures/acvr2a-no-disease.json` is a real 5-record slice
267
+ of the production literature section. End-to-end tests run the full lifecycle
268
+ (track → snapshot → ingest → op delete → simulate refresh → ingest → finalize)
269
+ against a temp git repo mirroring the production layout.
270
+
271
+ ## Status
272
+
273
+ - **Production-ready for literature curation** on `target_disease` entities
274
+ - 26/26 tests passing
275
+ - Pipeline hook in `build_dossier.py` ready behind the `IS_REFRESH` / `CURATE_ENABLED` flags
276
+ - Scientist-facing skill installed via `dev install-skill`
277
+
278
+ ## Roadmap (deferred to v2)
279
+
280
+ - `op patch` (field-level updates) — merge engine handles them, no CLI yet
281
+ - `op accept` (resolve conflicts via choice)
282
+ - MCP server runtime (`mcp serve` subcommand) for Claude.ai consumer Desktop
283
+ - Field-level brownfield detection (needed when expanding to clinical trial endpoints)
284
+ - Other section schemas: `/evidence/search-patent/`, `/market-intelligence/*`, etc.
@@ -0,0 +1,49 @@
1
+ [tool.poetry]
2
+ name = "dbtips-curate"
3
+ version = "0.2.0"
4
+ description = "Three-way merge curation for dbtips pipeline-generated dossier caches. Ships agentskills.io-compatible skills for Claude Code, OpenAI Codex, and Google Antigravity."
5
+ authors = ["Aganitha <dev@aganitha.ai>"]
6
+ readme = "README.md"
7
+ packages = [{include = "dbtips_curate", from = "src"}]
8
+ # Ensure the packaged skill/ tree (SKILL.md files) ships in the wheel so
9
+ # `dbtips-curate dev bootstrap` can copy them out post-install.
10
+ include = ["src/dbtips_curate/skill/**/*.md"]
11
+
12
+ [tool.poetry.dependencies]
13
+ python = "^3.11"
14
+ # typer 0.12 misparses options against click >= 8.4 (Apr 2026): --path is read
15
+ # as an extra positional and --version stops being an eager terminating flag.
16
+ # Pin to 0.15+ which is compatible with the current click line.
17
+ typer = "^0.15.0"
18
+ click = "^8.1"
19
+ pydantic = "^2.6.0"
20
+ rich = "^13.7.0"
21
+
22
+ [tool.poetry.group.dev.dependencies]
23
+ pytest = "^8.0.0"
24
+ pytest-cov = "^4.1.0"
25
+
26
+ [tool.poetry.scripts]
27
+ dbtips-curate = "dbtips_curate.cli.main:app"
28
+
29
+ [[tool.poetry.source]]
30
+ name = "PyPI"
31
+ priority = "primary"
32
+
33
+ [[tool.poetry.source]]
34
+ name = "prod"
35
+ url = "https://pypi.aganitha.ai/"
36
+ priority = "supplemental"
37
+
38
+ [[tool.poetry.source]]
39
+ name = "dev"
40
+ url = "https://dev-pypi.aganitha.ai/"
41
+ priority = "supplemental"
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+ addopts = "-ra -q"
46
+
47
+ [build-system]
48
+ requires = ["poetry-core"]
49
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
File without changes