ecarsi 0.2.8__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 (72) hide show
  1. ecarsi-0.2.8/PKG-INFO +411 -0
  2. ecarsi-0.2.8/README.md +389 -0
  3. ecarsi-0.2.8/ecarsi/__init__.py +72 -0
  4. ecarsi-0.2.8/ecarsi/__main__.py +154 -0
  5. ecarsi-0.2.8/ecarsi/agent_retry.py +41 -0
  6. ecarsi-0.2.8/ecarsi/cost.py +184 -0
  7. ecarsi-0.2.8/ecarsi/crosssample.py +439 -0
  8. ecarsi-0.2.8/ecarsi/design.py +107 -0
  9. ecarsi-0.2.8/ecarsi/downstream.py +368 -0
  10. ecarsi-0.2.8/ecarsi/execute.py +259 -0
  11. ecarsi-0.2.8/ecarsi/harness.py +59 -0
  12. ecarsi-0.2.8/ecarsi/index.py +981 -0
  13. ecarsi-0.2.8/ecarsi/layout.py +249 -0
  14. ecarsi-0.2.8/ecarsi/ledger.py +474 -0
  15. ecarsi-0.2.8/ecarsi/loop.py +470 -0
  16. ecarsi-0.2.8/ecarsi/mirror.py +164 -0
  17. ecarsi-0.2.8/ecarsi/organize.py +163 -0
  18. ecarsi-0.2.8/ecarsi/osp_contract.py +158 -0
  19. ecarsi-0.2.8/ecarsi/osp_worker.py +120 -0
  20. ecarsi-0.2.8/ecarsi/persample.py +580 -0
  21. ecarsi-0.2.8/ecarsi/plan.py +160 -0
  22. ecarsi-0.2.8/ecarsi/policies.py +263 -0
  23. ecarsi-0.2.8/ecarsi/prompts/batch_key.md +17 -0
  24. ecarsi-0.2.8/ecarsi/prompts/plan.md +52 -0
  25. ecarsi-0.2.8/ecarsi/prompts/sample_column.md +62 -0
  26. ecarsi-0.2.8/ecarsi/prompts/sample_inclusion.md +38 -0
  27. ecarsi-0.2.8/ecarsi/prune.py +158 -0
  28. ecarsi-0.2.8/ecarsi/release_state.py +132 -0
  29. ecarsi-0.2.8/ecarsi/resources.py +122 -0
  30. ecarsi-0.2.8/ecarsi/review.py +403 -0
  31. ecarsi-0.2.8/ecarsi/run_state.py +103 -0
  32. ecarsi-0.2.8/ecarsi/sample_mapping.py +172 -0
  33. ecarsi-0.2.8/ecarsi/serve.py +1339 -0
  34. ecarsi-0.2.8/ecarsi/umapdata.py +141 -0
  35. ecarsi-0.2.8/ecarsi/upstream.py +190 -0
  36. ecarsi-0.2.8/ecarsi/zoomin.py +125 -0
  37. ecarsi-0.2.8/ecarsi.egg-info/PKG-INFO +411 -0
  38. ecarsi-0.2.8/ecarsi.egg-info/SOURCES.txt +70 -0
  39. ecarsi-0.2.8/ecarsi.egg-info/dependency_links.txt +1 -0
  40. ecarsi-0.2.8/ecarsi.egg-info/entry_points.txt +2 -0
  41. ecarsi-0.2.8/ecarsi.egg-info/requires.txt +15 -0
  42. ecarsi-0.2.8/ecarsi.egg-info/top_level.txt +1 -0
  43. ecarsi-0.2.8/pyproject.toml +38 -0
  44. ecarsi-0.2.8/setup.cfg +4 -0
  45. ecarsi-0.2.8/tests/test_agent_selection.py +89 -0
  46. ecarsi-0.2.8/tests/test_bridge_logging.py +46 -0
  47. ecarsi-0.2.8/tests/test_cell_policies.py +520 -0
  48. ecarsi-0.2.8/tests/test_cost.py +97 -0
  49. ecarsi-0.2.8/tests/test_crosssample_cwd.py +47 -0
  50. ecarsi-0.2.8/tests/test_design.py +76 -0
  51. ecarsi-0.2.8/tests/test_downstream.py +136 -0
  52. ecarsi-0.2.8/tests/test_downstream_state.py +220 -0
  53. ecarsi-0.2.8/tests/test_empty_samples.py +124 -0
  54. ecarsi-0.2.8/tests/test_front_integration.py +429 -0
  55. ecarsi-0.2.8/tests/test_harness_sync.py +66 -0
  56. ecarsi-0.2.8/tests/test_identity_provenance.py +50 -0
  57. ecarsi-0.2.8/tests/test_index_light_markers.py +56 -0
  58. ecarsi-0.2.8/tests/test_index_offline.py +50 -0
  59. ecarsi-0.2.8/tests/test_index_pages.py +203 -0
  60. ecarsi-0.2.8/tests/test_ledger_conservation.py +153 -0
  61. ecarsi-0.2.8/tests/test_loop_control.py +141 -0
  62. ecarsi-0.2.8/tests/test_mirror.py +199 -0
  63. ecarsi-0.2.8/tests/test_osp_worker.py +148 -0
  64. ecarsi-0.2.8/tests/test_postflight_identity.py +89 -0
  65. ecarsi-0.2.8/tests/test_prune_receipt.py +139 -0
  66. ecarsi-0.2.8/tests/test_release_state.py +130 -0
  67. ecarsi-0.2.8/tests/test_review_agent_config.py +32 -0
  68. ecarsi-0.2.8/tests/test_sample_map_derive.py +42 -0
  69. ecarsi-0.2.8/tests/test_serve_home.py +47 -0
  70. ecarsi-0.2.8/tests/test_serve_navigator.py +30 -0
  71. ecarsi-0.2.8/tests/test_serve_state_cache.py +55 -0
  72. ecarsi-0.2.8/tests/test_upstream_skip_run_root.py +35 -0
ecarsi-0.2.8/PKG-INFO ADDED
@@ -0,0 +1,411 @@
1
+ Metadata-Version: 2.4
2
+ Name: ecarsi
3
+ Version: 0.2.8
4
+ Summary: eca-rsi main line: organize → persample (osp) → self-driving rounds of crosssample (msp) + zoomin (zmip), ledger, landing pages and serving
5
+ Author-email: chansigit <chansigit@gmail.com>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: agent-harness-bridge[all]<0.3,>=0.2.4
10
+ Requires-Dist: anndata>=0.11
11
+ Requires-Dist: scanpy
12
+ Requires-Dist: h5py
13
+ Requires-Dist: numpy
14
+ Requires-Dist: pandas
15
+ Requires-Dist: matplotlib
16
+ Provides-Extra: kernels
17
+ Requires-Dist: osp-sc[agent]<0.2,>=0.1.3; extra == "kernels"
18
+ Requires-Dist: msp-sc[agent]<0.5,>=0.4.0; extra == "kernels"
19
+ Requires-Dist: zmip<0.4,>=0.3.4; extra == "kernels"
20
+ Provides-Extra: front
21
+ Requires-Dist: osp-sc[agent]<0.2,>=0.1.3; extra == "front"
22
+
23
+ # ECA-RSI: Recursive Self-Improvement for an Ensemble Cell Atlas
24
+
25
+ **Iterative quality review and cell-type annotation, from standardized inputs
26
+ to a dataset with cell-level decision records.**
27
+
28
+ ECA-RSI coordinates sample-level QC, cross-sample integration, and lineage-level
29
+ refinement. It starts from [ECA-PP](https://github.com/chansigit/eca-pp) outputs,
30
+ runs dedicated analysis packages, and repeats integration and refinement on
31
+ the surviving cells until a numerical stopping rule is met. Each analysis
32
+ unit gets an annotated H5AD, reports, a cell ledger, and unresolved questions
33
+ for review.
34
+
35
+ The current implementation is the `ecarsi` Python package. Start with
36
+ [installation](INSTALL.md), then run:
37
+
38
+ ```bash
39
+ eca-rsi run /path/to/eca-pp-output /path/to/eca-runs/study
40
+ ```
41
+
42
+ ## Why iterate?
43
+
44
+ Feature selection and embeddings depend on the cells being analyzed. Removing
45
+ noisy populations can change which sources of variation dominate, making
46
+ previously obscured populations easier to examine in the next round.
47
+
48
+ ECA-RSI examines the data at two scales. Cross-sample integration reveals
49
+ recurring populations and quality patterns across samples. Recomputing features
50
+ within a lineage exposes finer populations that may be hidden in the global
51
+ view. The next round rebuilds the global analysis from the surviving cells'
52
+ counts. This motivates iterative review; a stable cell count alone does not
53
+ establish biological accuracy.
54
+
55
+ ## How the packages fit together
56
+
57
+ | Component | Responsibility in this workflow |
58
+ | --- | --- |
59
+ | [ECA-PP](https://github.com/chansigit/eca-pp) | Prepare counts, gene names, species information, QC measurements, and metadata evidence. Runs before ECA-RSI. |
60
+ | ECA-RSI (`ecarsi`) | Organize analysis units, identify sample columns, decide sample inclusion, drive rounds, and assemble releases and browser pages. |
61
+ | [OSP](https://github.com/chansigit/osp) (`osp-sc`) | Run QC, doublet detection, contamination estimation, clustering, and annotation proposals within each sample. |
62
+ | [MSP](https://github.com/chansigit/msp) (`msp-sc`) | Recompute the shared feature space, integrate samples, inspect populations, and apply annotations and removals. |
63
+ | [ZMIP](https://github.com/chansigit/zmip) | Plan lineages, re-embed selected lineages, refine labels, record removals and reassignments, and merge results. |
64
+ | [agent-harness-bridge](https://github.com/chansigit/agent-harness-bridge) | Provide the shared agent/tool interface, runtime adapters, and failure recovery. |
65
+ | [eca-grain](https://github.com/chansigit/eca-grain) (`eca-grain`) | After release: aggregate a unit into audited grains (metacell-style, one pass, every cell on the ledger) for downstream work such as GRN inference. Does not modify the release. |
66
+
67
+ The analysis packages implement computation and apply decisions. Agents inspect
68
+ evidence and submit structured decisions through tools, with checks in the
69
+ calling package. MSP also uses
70
+ [standissect-lite](https://github.com/chansigit/standissect-lite) to identify
71
+ smaller fragments within populations.
72
+
73
+ ## Prepare the input
74
+
75
+ The `run` and `organize` commands require ECA-PP products in this layout:
76
+
77
+ ```text
78
+ eca-pp-output/
79
+ source-A/
80
+ standardize/
81
+ standardized.h5ad
82
+ result.json
83
+ identify_columns/
84
+ result.json # optional metadata evidence
85
+ source-B/
86
+ standardize/
87
+ standardized.h5ad
88
+ result.json
89
+ ```
90
+
91
+ The input can also be a single source directory containing `standardize/`.
92
+ Source directory names must be unique. ECA-PP's step-local `.history/` archives
93
+ and any ECA-RSI run root (a directory holding `organize/manifest.json`, such as a
94
+ finished run mirrored next to `standardize/`) are ignored; other unexpected H5AD
95
+ files cause validation to fail. Keep ECA-RSI outputs outside the input tree and the source repository.
96
+
97
+ Organize checks upstream status and exit codes, opens each accepted H5AD, and
98
+ validates cell/gene IDs, dimensions, and finite nonnegative values in the
99
+ required `layers["counts"]`. Failed or inconsistent results block processing.
100
+ Rejected sources remain in the source inventory even without an H5AD;
101
+ nonblocking `needs_review` results can proceed with their reasons preserved.
102
+ Upstream results and metadata evidence are saved as snapshots for later review.
103
+
104
+ ECA-PP's `identify_columns/result.json` and derived TSV evidence are optional.
105
+ RSI aligns that evidence to the original cell IDs and identifies experiments
106
+ within each source. Two sources both using `sample=S1` remain separate OSP
107
+ inputs. A technical batch column is not automatically an experimental sample
108
+ column; explicit sample mappings are supported. See
109
+ [FRONT_INTEGRATION.md](FRONT_INTEGRATION.md) for mapping formats.
110
+
111
+ A sample map can also declare two cell policies that the host applies
112
+ deterministically and never infers (`ecarsi/policies.py`):
113
+
114
+ ```json
115
+ {
116
+ "sources": {"Lung": {"sample_column": "plate.barcode", "rationale": "Smart-seq2 plate = library"}},
117
+ "exclude_cells": [
118
+ {"blank": ["mouse.id", "subtissue", "cell_ontology_class"],
119
+ "reason": "upstream_qc_blank",
120
+ "rationale": "wells the authors' QC dropped; metadata left blank ('missing')"}
121
+ ],
122
+ "batch_key": "mouse.id"
123
+ }
124
+ ```
125
+
126
+ `exclude_cells` rules (`where`: exact string match, AND across columns;
127
+ `blank`: missing-family value in every listed column) drop cells before any
128
+ OSP subset is cut. An unknown column is an error; a rule matching no cell is
129
+ a recorded warning. Every excluded cell is listed in
130
+ `persample/excluded_cells.csv` with its reason and appears in the cell ledger
131
+ as `removed:persample-policy:<reason>`; the release's `needs_review` lists
132
+ each rule under "Cells excluded before OSP by policy". `batch_key` names the
133
+ obs column Harmony corrects by instead of the experiment (for plate = mouse x
134
+ FACS gate designs, the mouse); the host requires it to be constant within
135
+ every experiment (blank cells ignored, then filled with their experiment's
136
+ value in the OSP subset) and to take at least two values. `MSP_BATCH_COL`
137
+ still wins; a value contradicting the map is an error. Without a map, the
138
+ sample-column agent may propose exclusion rules, validated exactly like user
139
+ rules and recorded as `proposed_by: agent`; a batch-key *recommendation* from
140
+ the study design goes to `needs_review` only.
141
+
142
+ ## Run the workflow
143
+
144
+ Use Python 3.10 or newer with ECA-RSI, its three kernels, and the shared bridge
145
+ installed. Version 0.1.0 is available on
146
+ [GitHub Releases](https://github.com/chansigit/eca-rsi/releases/tag/v0.1.0).
147
+ PyPI publication is pending a new-project creation rate limit. Install the
148
+ GitHub wheel with its kernels:
149
+
150
+ ```bash
151
+ python -m pip install 'ecarsi[kernels] @ https://github.com/chansigit/eca-rsi/releases/download/v0.1.0/ecarsi-0.1.0-py3-none-any.whl'
152
+ ```
153
+
154
+ Follow [INSTALL.md](https://github.com/chansigit/eca-rsi/blob/main/INSTALL.md)
155
+ for environment checks and source installation. Installing `ecarsi` alone does
156
+ not install the kernels by default.
157
+
158
+ The default agent backend is OpenAI Agents SDK driving Doubao through
159
+ Volcengine Ark, with model `doubao-seed-2-1-turbo-260628`. Set `ARK_API_KEY` in
160
+ your environment before running. Other configured backends can be selected
161
+ with `--harness deepseek` or `--harness claude`.
162
+
163
+ ```bash
164
+ # Automatic stopping for every analysis unit.
165
+ eca-rsi run /path/to/eca-pp-output /path/to/eca-runs/study
166
+
167
+ # CLI values override HARNESS and MODEL environment variables.
168
+ eca-rsi --harness openai --model doubao-seed-2-1-turbo-260628 \
169
+ run /path/to/eca-pp-output /path/to/eca-runs/study
170
+
171
+ # A fixed total of two rounds, retaining intermediate H5ADs.
172
+ eca-rsi run /path/to/eca-pp-output /path/to/eca-runs/study \
173
+ --rounds 2 --no-prune
174
+
175
+ # Run on fast scratch, keep a browsable copy on long-term storage.
176
+ eca-rsi run /path/to/eca-pp-output $SCRATCH/eca-runs/study --mirror $OAK/eca-results/study
177
+ ```
178
+
179
+ `python -m ecarsi` is equivalent to `eca-rsi`. The repository also provides
180
+ `./run-eca-rsi.sh <input> <root>`; set `ECA_RSI_PYTHON` to select its interpreter.
181
+ Use `eca-rsi --help` and `eca-rsi run --help` for available commands.
182
+
183
+ ### Compatible packages
184
+
185
+ The 0.1.0 release uses bridge **0.2.3**, OSP **0.1.2**, and MSP/ZMIP **0.3.3**
186
+ as its minimum compatible versions, with upper version bounds in
187
+ `pyproject.toml`. These dependencies are published on PyPI. MSP uses Harmony 2
188
+ on the CPU; this workflow does not require torch. See [INSTALL.md](INSTALL.md)
189
+ for the tested package combination and configurable kernel parameters.
190
+
191
+ To validate the input and per-sample stages before starting iterative analysis:
192
+
193
+ ```bash
194
+ eca-rsi run /path/to/eca-pp-output /path/to/eca-runs/new-study --stop-after persample
195
+ ```
196
+
197
+ For explicit experiment mappings and OSP options, run `organize` and
198
+ `persample` separately. The front and downstream integration records are in
199
+ [FRONT_INTEGRATION.md](FRONT_INTEGRATION.md) and
200
+ [DOWNSTREAM_INTEGRATION.md](DOWNSTREAM_INTEGRATION.md).
201
+ `FRONT_COMPATIBILITY.json` records the earlier front-only validation snapshot;
202
+ it is not the current full-workflow dependency list.
203
+
204
+ ### Processing stages
205
+
206
+ 1. **Organize.** Profile upstream files and propose analysis units using their
207
+ metadata, then merge or split them in code. A conservation check requires
208
+ each source cell to belong to exactly one analysis unit. Cross-file barcode
209
+ overlap produces warnings; it does not establish expression identity or
210
+ automatically deduplicate cells.
211
+ 2. **Per sample, once.** Identify the experimental-run column and run OSP on
212
+ each sample. The driver sizes concurrency from available CPUs and memory.
213
+ Annotation is enabled by default and required for cross-sample review.
214
+ 3. **First round.** Decide which samples enter integration, then run MSP
215
+ integration, inspection, and annotation, followed by ZMIP lineage refinement.
216
+ With one included sample, MSP skips Harmony and sample-composition evidence.
217
+ Obs columns that are constant within every sample but differ across samples
218
+ (e.g. FACS `subtissue`, `mouse.id`) are passed to the MSP/ZMIP agents as
219
+ `--design-context`, so a sample-confined cluster is judged against the study
220
+ design rather than as a batch artefact; preview with `python -m ecarsi.design <unit>`.
221
+ 4. **Later rounds.** Take the previous ZMIP survivors, preserve prior labels
222
+ under `rNN_*` columns, and rerun MSP from counts followed by ZMIP. OSP and
223
+ the first-round sample-inclusion decision are not repeated.
224
+ 5. **Release.** Record the stopping reason, collect review items, write the
225
+ final dataset and cell ledger, and update browser pages.
226
+
227
+ OSP filters cells using its configured QC rules. Its annotation-stage
228
+ keep/flag/drop proposals remain evidence for subsequent review. MSP annotation
229
+ applies the union of preannotation candidates, inspection drop proposals, and
230
+ annotation removals. ZMIP applies local removals and label refinements; lineages
231
+ below its zoom threshold (default 800 cells) retain existing annotations.
232
+ ZMIP's output inherits MSP's global embedding; global re-embedding happens in
233
+ the next round.
234
+
235
+ Completion requires successful kernel execution and validated outputs,
236
+ including readable H5ADs, required labels, and cell conservation against removal
237
+ and reassignment ledgers. Empty placeholder files do not mark a stage complete.
238
+ Only OSP failures explicitly marked retryable receive the driver's one retry.
239
+ Stress-related expression remains evidence for review; there is no blanket
240
+ stress-population or mitochondrial top-DEG deletion switch in this release.
241
+
242
+ ### Stopping rules
243
+
244
+ In automatic mode, round 1 continues. From round 2, a unit releases when:
245
+
246
+ - the current round removed **less than 1%** of its entering cells, **or fewer
247
+ than 100 cells**; or
248
+ - the last three rounds each removed **less than 2%**;
249
+
250
+ and, on top of either path, the current round removed **fewer than 1000
251
+ cells** in absolute terms. Relative rules alone let a 400k-cell unit release
252
+ while still dropping thousands of cells per round; the floor keeps such units
253
+ going, and the round's `reason` names it (`removed 0.81% but 1,989 cells >=
254
+ 1,000 floor`). Tune it per unit with `max_removed` in `loop_control.json`.
255
+
256
+ The entering count is MSP's `integrated.h5ad` and the outgoing count is ZMIP's
257
+ `annotated_zmip.h5ad`; these round statistics exclude earlier OSP filtering
258
+ and whole-sample exclusions. The cell ledger covers the preceding stages too.
259
+ Label wording changes are not a stopping criterion. Unresolved biological
260
+ questions accumulate in `needs_review` rather than prompting for approval.
261
+ Execution failures or missing required outputs can still stop a unit.
262
+
263
+ `--cap` sets the automatic-mode round limit (default 10); reaching it without
264
+ convergence produces a forced release with a review flag. `--rounds N` overrides
265
+ automatic stopping and releases after the specified total round count, including
266
+ `--rounds 1`. Check the recorded reason before interpreting a release as converged.
267
+
268
+ ## Read the results
269
+
270
+ The unit's `index.html` is RSI's report across all rounds. MSP and ZMIP
271
+ `report.html` files describe individual analysis stages; ZMIP also produces
272
+ reports for each processed lineage. A standalone MSP/ZMIP run does not produce
273
+ an RSI final release.
274
+
275
+ Each analysis unit has its own release:
276
+
277
+ ```text
278
+ <root>/
279
+ index.html
280
+ organize/manifest.json
281
+ units/<unit>/
282
+ index.html
283
+ progress.log
284
+ input/{organized.h5ad,manifest.json}
285
+ persample/{manifest.json,<sample>/...}
286
+ rounds/roundNN/
287
+ crosssample/ # MSP outputs and report
288
+ zoomin/ # ZMIP plan, lineage outputs, and reports
289
+ ledger/ # cell ledger and Sankey plots through this round
290
+ stats.txt
291
+ decision.txt
292
+ release/
293
+ final.h5ad
294
+ summary.md
295
+ summary.json
296
+ needs_review.md
297
+ needs_review.json
298
+ cell_ledger.csv
299
+ sankey_coarse.png
300
+ umap.json
301
+ ```
302
+
303
+ `release/final.h5ad` contains surviving cells; the final broad and fine labels
304
+ are `obs["zmip_ann_coarse"]` and `obs["zmip_ann_fine"]`. Read `summary.md` for
305
+ round counts and stopping reasons, and `needs_review.md` for uncertain labels,
306
+ policy-excluded cells, excluded samples, reassignments, and other review
307
+ items. The ledger and stage-specific removal CSVs (`persample/excluded_cells.csv`,
308
+ OSP `qc_removed.csv`, MSP `annotation_removed.csv`, ZMIP `zmip_removed.csv`)
309
+ record the cell-level history. Cost summaries
310
+ include only costs reported and captured by the runtime; missing cost records
311
+ do not mean a run was free or constitute a complete bill.
312
+
313
+ Report labels and explanatory text default to English, independently of the
314
+ language used to discuss or launch the analysis. Other prose languages require
315
+ an explicit configuration override.
316
+
317
+ Open a unit's `index.html` directly in a browser to view its saved report.
318
+ The final UMAP includes its plotting data in the HTML; zoom, hover and legend
319
+ filtering work offline with JavaScript enabled. Point size adapts to the plotted
320
+ cell count, panel size, and zoom. Keep the run directory together
321
+ for links to other reports and files. To update older saved pages, run
322
+ `python -m ecarsi.index /path/to/root-or-unit` (no analysis is rerun).
323
+
324
+ To browse live progress from an unfinished run or share results over HTTP:
325
+
326
+ ```bash
327
+ eca-rsi serve scan-add /path/to/eca-runs/study
328
+ eca-rsi serve --port 8899
329
+ ```
330
+
331
+ Open `http://127.0.0.1:8899/` on the serving machine. The server reads its dataset
332
+ registry from `~/.config/ecarsi/registry.json` by default and picks up registry
333
+ changes. `eca-rsi run ... --serve 8899` starts it after processing. Optional
334
+ `--ngrok`, `--domain`, and `--auth USER:PASS` support remote access; see
335
+ [INSTALL.md](INSTALL.md).
336
+
337
+ When the run directory lives on fast, purged scratch and the server reads a
338
+ long-term directory, pass `--mirror DIR` (to `run`, or to `organize`,
339
+ `persample`, `loop`; it is remembered in `<root>/mirror.json`, so resumed steps
340
+ keep mirroring). After every landing-page update the light files of the run
341
+ root — pages, `progress.log`, manifests, `stats.txt` / `decision.txt`, markdown,
342
+ reports, figures, small tables — are copied incrementally to DIR; at release
343
+ (after cleanup) the whole root is copied, including `final.h5ad`,
344
+ `input/organized.h5ad`, and ledgers, and files that cleanup removed are deleted
345
+ from DIR's copy of that unit only. Every page's footer shows `run state updated
346
+ <time>` (the newest state file), so a viewer of DIR knows how fresh it is; a
347
+ served DIR is also labelled a mirror copy of its source. Mirroring never reads
348
+ DIR and never fails a step: a failed copy is a warning in `progress.log`.
349
+
350
+ ## Resume and storage
351
+
352
+ Repeat the same `eca-rsi run` command after an interruption to reuse validated
353
+ outputs. Organize and per-sample manifests track input, configuration, and
354
+ adapter/runtime identities. MSP and ZMIP stages also check their input content,
355
+ computation settings, runtime sources, and completed output hashes. RSI invokes
356
+ ZMIP's own resume checks even when lineage outputs already exist. Completed
357
+ rounds and releases have integrity receipts; pruned historical releases can
358
+ be checked without requiring deleted intermediate matrices.
359
+
360
+ Use a new output root when inputs or analysis code change. Legacy outputs
361
+ without the required identities or receipts remain browsable, but are not
362
+ accepted as verified completion for upgraded computation.
363
+
364
+ Recorded harness/model changes are rejected unless the stage supports an
365
+ explicit `--allow-agent-change`. That option permits an intentional mixed run;
366
+ it does not reset computation caches or override input/runtime checks.
367
+ Per-sample model/configuration changes require a new output directory.
368
+ `--force-reopen` continues beyond an existing release; with `--rounds N`, choose
369
+ a total larger than the completed round count. It does not restore pruned
370
+ matrices or forward ZMIP's `--force` option.
371
+
372
+ **Release normally triggers cleanup of intermediate H5ADs.** Use `--no-prune`
373
+ on `run` or `loop` to retain them. Cleanup keeps `input/organized.h5ad`, the
374
+ release, reports, tables, and figures. Removed H5ADs leave `.pruned` markers;
375
+ those carrying labels also leave `.obs.parquet` or `.obs.csv.gz` tables for the
376
+ ledger. `release/pruned.json` records the cleanup. This preserves the decision
377
+ history, but not every intermediate expression matrix.
378
+
379
+ ```bash
380
+ eca-rsi prune /path/to/eca-runs/study --dry-run
381
+ ```
382
+
383
+ ## Validation scope
384
+
385
+ Release checks passed **131 tests**, with **2 skipped**. Wheel and source archive
386
+ metadata checks passed, and the installed wheel's modules and prompt resources
387
+ were verified. A browser check confirmed offline UMAP rendering, legend
388
+ selection, and zoom without fetching plot data.
389
+
390
+ Real-data validation includes a two-round RSI run on **Clayton**, ending with
391
+ 850 cells and a matching cell ledger, and a separate full-size **19Liu MSP/ZMIP**
392
+ run, from 81,079 to 75,394 cells. Clayton used historical ECA-PP 0.2 inputs;
393
+ 19Liu was a downstream kernel validation, not a full RSI run. Subsequent kernel
394
+ fixes received targeted validation rather than a complete repeat of all model
395
+ decisions. These checks establish engineering behavior, not independently
396
+ validated biological accuracy. Details and remaining review items are in
397
+ [DOWNSTREAM_INTEGRATION.md](DOWNSTREAM_INTEGRATION.md).
398
+
399
+ ## Development and history
400
+
401
+ See [CLAUDE.md](CLAUDE.md) for source layout, operating conventions, and targeted
402
+ checks. See [CHANGELOG.md](CHANGELOG.md) for release changes and
403
+ [TODO.md](TODO.md) for deferred policy discussions. The [architecture diagram](diagrams/architecture.html) illustrates the
404
+ main package flow; consult this README and the source for current runtime and
405
+ resume behavior.
406
+
407
+ `run.sh` and `steps/*.md` belong to the previous six-step prompt loop, preserved
408
+ on branch `primitive`. That generation used agents to write analysis scripts
409
+ through Explore → Compute → Annotate → QC → Apply → Stop. Its commands,
410
+ governance prompts, and timing examples do not describe the current `ecarsi`
411
+ workflow. `attic-v01/` is an older archive.