lightcone-cli 0.2.0__tar.gz → 0.3.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.
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/.gitignore +1 -1
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/PKG-INFO +8 -5
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/guides/astra-reference.md +66 -18
- lightcone_cli-0.3.0/claude/lightcone/guides/lightcone-cli-reference.md +61 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-build/assets/loop-prompt.md +13 -14
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-feedback/SKILL.md +1 -1
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-migrate/SKILL.md +20 -3
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-new/SKILL.md +1 -1
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-verify/SKILL.md +7 -2
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/templates/CLAUDE.md +1 -3
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/pyproject.toml +24 -6
- lightcone_cli-0.3.0/src/lightcone/cli/commands.py +780 -0
- lightcone_cli-0.3.0/src/lightcone/engine/__init__.py +6 -0
- lightcone_cli-0.3.0/src/lightcone/engine/container.py +781 -0
- lightcone_cli-0.3.0/src/lightcone/engine/dask_cluster.py +252 -0
- lightcone_cli-0.3.0/src/lightcone/engine/manifest.py +205 -0
- lightcone_cli-0.3.0/src/lightcone/engine/runner.py +110 -0
- lightcone_cli-0.3.0/src/lightcone/engine/scratch.py +212 -0
- lightcone_cli-0.3.0/src/lightcone/engine/site_registry.py +167 -0
- lightcone_cli-0.3.0/src/lightcone/engine/snakefile.py +444 -0
- lightcone_cli-0.3.0/src/lightcone/engine/status.py +155 -0
- lightcone_cli-0.3.0/src/lightcone/engine/tree.py +375 -0
- lightcone_cli-0.3.0/src/lightcone/engine/validation.py +180 -0
- lightcone_cli-0.3.0/src/lightcone/engine/verify.py +140 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/graders.py +27 -22
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/sandbox.py +11 -5
- lightcone_cli-0.3.0/src/snakemake_executor_plugin_dask/__init__.py +25 -0
- lightcone_cli-0.3.0/src/snakemake_executor_plugin_dask/executor.py +173 -0
- lightcone_cli-0.2.0/claude/lightcone/guides/lightcone-cli-reference.md +0 -75
- lightcone_cli-0.2.0/src/lightcone/cli/commands.py +0 -2327
- lightcone_cli-0.2.0/src/lightcone/engine/__init__.py +0 -42
- lightcone_cli-0.2.0/src/lightcone/engine/assets.py +0 -418
- lightcone_cli-0.2.0/src/lightcone/engine/container.py +0 -370
- lightcone_cli-0.2.0/src/lightcone/engine/io_manager.py +0 -27
- lightcone_cli-0.2.0/src/lightcone/engine/runner.py +0 -1017
- lightcone_cli-0.2.0/src/lightcone/engine/site_registry.py +0 -142
- lightcone_cli-0.2.0/src/lightcone/engine/status.py +0 -135
- lightcone_cli-0.2.0/src/lightcone/engine/targets.py +0 -68
- lightcone_cli-0.2.0/src/lightcone/engine/tree.py +0 -245
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/LICENSE +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/agents/lc-extractor.md +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/guides/ui-brand.md +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/hooks/langfuse_git_commit_hook.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/hooks/langfuse_hook.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/hooks/langfuse_prepare_commit_msg.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/hooks/langfuse_session_init_hook.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/hooks/langfuse_utils.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/scripts/activate-venv.sh +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/scripts/check-lc-run.sh +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/scripts/session-start.sh +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/scripts/validate-on-save.sh +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-build/SKILL.md +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-build/scripts/setup-lc-build.sh +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/cli/__init__.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/cli/plugin.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/__init__.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/build.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/cli.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/harness.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/models.py +0 -0
- {lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/src/lightcone/eval/report.py +0 -0
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lightcone-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Lightcone command-line toolchain
|
|
5
5
|
Author: Lightcone Research
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: >=3.11
|
|
9
|
-
Requires-Dist: astra-tools>=0.2.
|
|
9
|
+
Requires-Dist: astra-tools>=0.2.5
|
|
10
10
|
Requires-Dist: click>=8.0
|
|
11
|
-
Requires-Dist:
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist:
|
|
11
|
+
Requires-Dist: dask>=2024.1
|
|
12
|
+
Requires-Dist: distributed>=2024.1
|
|
13
|
+
Requires-Dist: jinja2>=3.0
|
|
14
14
|
Requires-Dist: langfuse>=2.0
|
|
15
15
|
Requires-Dist: pyyaml>=6.0
|
|
16
16
|
Requires-Dist: rich>=13.0
|
|
17
|
+
Requires-Dist: snakemake-interface-common>=1.14
|
|
18
|
+
Requires-Dist: snakemake-interface-executor-plugins>=9.0
|
|
19
|
+
Requires-Dist: snakemake>=9.0
|
|
@@ -14,7 +14,8 @@ Fields: `name`, `description`, `version`, `authors`, `tags`, `inputs`, `outputs`
|
|
|
14
14
|
# Simple analysis -- everything at top level
|
|
15
15
|
version: "1.0"
|
|
16
16
|
name: "My Analysis"
|
|
17
|
-
|
|
17
|
+
narrative:
|
|
18
|
+
summary: "What this analysis investigates."
|
|
18
19
|
inputs:
|
|
19
20
|
- id: training_data
|
|
20
21
|
type: data
|
|
@@ -43,8 +44,16 @@ decisions:
|
|
|
43
44
|
outputs:
|
|
44
45
|
- id: accuracy
|
|
45
46
|
type: metric
|
|
47
|
+
inputs: [training_data] # upstream artifacts (Input or sibling Output)
|
|
48
|
+
decisions: [scaling, use_pca, n_components] # decisions that parameterize this output
|
|
46
49
|
recipe:
|
|
47
|
-
command:
|
|
50
|
+
command: >
|
|
51
|
+
python scripts/evaluate.py
|
|
52
|
+
--data {inputs.training_data}
|
|
53
|
+
--scaling {decisions.scaling}
|
|
54
|
+
--use_pca {decisions.use_pca}
|
|
55
|
+
--n_components {decisions.n_components}
|
|
56
|
+
--output {output}
|
|
48
57
|
container: Containerfile
|
|
49
58
|
```
|
|
50
59
|
|
|
@@ -68,9 +77,9 @@ A decision is a methodological choice where a different defensible option could
|
|
|
68
77
|
|
|
69
78
|
### Parameterization
|
|
70
79
|
|
|
71
|
-
**Every decision must be parameterized in code** -- never hardcode a decision value.
|
|
80
|
+
**Every decision must be parameterized in code** -- never hardcode a decision value. Decisions reach the script via the recipe template's `{decisions.<id>}` placeholders (see [Recipe Format](#recipe-format)). The recipe author chooses how to pass them — typically as CLI args (`--scaling {decisions.scaling}` paired with `parser.add_argument("--scaling")` in the script), but env vars or sidecar files work too. There is no magic auto-injection: if a decision isn't referenced in the recipe template, the script never sees it.
|
|
72
81
|
|
|
73
|
-
**
|
|
82
|
+
**Decision provenance contract:** list every decision a script consumes under `Output.decisions: [...]`. Re-running the output with a different option for any listed decision must be expected to change the result. The validator enforces that every `{decisions.<id>}` placeholder appears in `Output.decisions`, and `code_version` (the cache key) hashes only those decisions — so changes to unrelated decisions don't invalidate cached results.
|
|
74
83
|
|
|
75
84
|
### Constraints
|
|
76
85
|
|
|
@@ -91,21 +100,44 @@ Convention path: `results/<universe_id>/<output_id>.<ext>` -- no `path` field ne
|
|
|
91
100
|
|
|
92
101
|
## Recipe Format
|
|
93
102
|
|
|
94
|
-
|
|
103
|
+
In v0.0.7, **`inputs` and `decisions` live on the Output, not on the Recipe.** The recipe is pure *how*: a `command` template plus its execution context (`container`, `resources`).
|
|
95
104
|
|
|
96
105
|
```yaml
|
|
97
106
|
outputs:
|
|
98
107
|
- id: accuracy
|
|
99
108
|
type: metric
|
|
109
|
+
inputs: [trained_model] # upstream artifacts
|
|
110
|
+
decisions: [scaling, n_components] # decisions that parameterize this output
|
|
100
111
|
recipe:
|
|
101
|
-
command:
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
command: >
|
|
113
|
+
python scripts/evaluate.py
|
|
114
|
+
--model {inputs.trained_model}
|
|
115
|
+
--scaling {decisions.scaling}
|
|
116
|
+
--n_components {decisions.n_components}
|
|
117
|
+
--output {output}
|
|
118
|
+
container: ghcr.io/proj/ml:latest # Overrides analysis-level default
|
|
104
119
|
resources: { cpus: 4, memory: "32GB", gpus: 1, time_limit: "2h" }
|
|
120
|
+
# `gpus` is per-node. Multi-node recipes get nodes × gpus total GPUs.
|
|
105
121
|
```
|
|
106
122
|
|
|
123
|
+
`Output.inputs` references resolve to either a sibling Output's directory or an analysis-level Input's source string (e.g. a path or `sklearn.datasets.load_iris`). The runner walks any `from:` aliases in the surrounding scope to find the source.
|
|
124
|
+
|
|
107
125
|
Set `container:` at analysis level (all recipes inherit); per-recipe `container:` overrides. Pass either a container image name (e.g., `python:3.12-slim`, `ghcr.io/org/img:latest`) or a path to a Containerfile (e.g., `Containerfile`, `containers/Dockerfile`). The runtime figures out whether to pull or build.
|
|
108
126
|
|
|
127
|
+
### Recipe Command Template
|
|
128
|
+
|
|
129
|
+
The `command` is a template with these placeholders:
|
|
130
|
+
|
|
131
|
+
| Placeholder | Substitutes to |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `{output}` | The directory the artifact is written to (e.g. `results/baseline/accuracy/`) |
|
|
134
|
+
| `{inputs.<id>}` | The named upstream input's resolved path or source string. `<id>` must be in `Output.inputs`. |
|
|
135
|
+
| `{inputs}` | Space-joined values of every entry in `Output.inputs` (declaration order). |
|
|
136
|
+
| `{decisions.<id>}` | The active option ID for the named decision in this universe. `<id>` must be in `Output.decisions`. |
|
|
137
|
+
| `{{` / `}}` | Literal `{` / `}` (e.g. `awk '{{print $1}}'`). |
|
|
138
|
+
|
|
139
|
+
Static constants belong inline in the command (`--max-iter 1000`); only varying values are decisions, only path/source values are inputs. Any other placeholder is rejected by `astra validate` and by the runner.
|
|
140
|
+
|
|
109
141
|
### Conditional Outputs
|
|
110
142
|
|
|
111
143
|
Outputs can have `when` conditions -- the output only exists when the condition is met for a given universe. Uses the same syntax as decision `when` (negation with `~`, lists AND'd).
|
|
@@ -242,21 +274,29 @@ analyses:
|
|
|
242
274
|
path: ./analyses/train_network
|
|
243
275
|
```
|
|
244
276
|
|
|
245
|
-
Inside each sub-analysis's own `astra.yaml`, `from:` wires inputs and decisions to the parent or siblings
|
|
277
|
+
Inside each sub-analysis's own `astra.yaml`, `from:` wires inputs and decisions to the parent or siblings using the **unified `../` path grammar**:
|
|
246
278
|
|
|
247
279
|
```yaml
|
|
248
280
|
# analyses/train_network/astra.yaml
|
|
249
281
|
inputs:
|
|
250
282
|
- id: training_data
|
|
251
|
-
|
|
252
|
-
from: build_mocks.mock_catalog # Sibling output
|
|
283
|
+
from: ../build_mocks.mock_catalog # Sibling sub-analysis's output (escape one scope, descend)
|
|
253
284
|
outputs:
|
|
254
285
|
- id: trained_model
|
|
255
286
|
type: data
|
|
256
|
-
|
|
287
|
+
inputs: [training_data]
|
|
288
|
+
decisions: [cosmology_model, noise_model]
|
|
289
|
+
recipe:
|
|
290
|
+
command: >
|
|
291
|
+
python src/train.py
|
|
292
|
+
--data {inputs.training_data}
|
|
293
|
+
--cosmology {decisions.cosmology_model}
|
|
294
|
+
--noise {decisions.noise_model}
|
|
295
|
+
--output {output}
|
|
296
|
+
resources: { gpus: 1, memory: "32GB" }
|
|
257
297
|
decisions:
|
|
258
298
|
cosmology_model:
|
|
259
|
-
from: ../cosmology_model
|
|
299
|
+
from: ../cosmology_model # Inherit parent decision (one scope up)
|
|
260
300
|
noise_model:
|
|
261
301
|
label: "Noise Model"
|
|
262
302
|
default: heteroscedastic
|
|
@@ -265,11 +305,19 @@ decisions:
|
|
|
265
305
|
heteroscedastic: { label: "Heteroscedastic" }
|
|
266
306
|
```
|
|
267
307
|
|
|
268
|
-
**Wiring patterns:**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
308
|
+
**Wiring patterns (v0.0.7 unified `from:` grammar):**
|
|
309
|
+
|
|
310
|
+
| Where | Form | Meaning |
|
|
311
|
+
|---|---|---|
|
|
312
|
+
| Input | `from: ../id` | An ancestor input |
|
|
313
|
+
| Input | `from: ../../id` | A grandparent input |
|
|
314
|
+
| Input | `from: ../sibling.out_id` | A sibling sub-analysis's output |
|
|
315
|
+
| Output | `from: child.out_id` | Re-export of an own child sub's output (no `../` — outputs only flow *up* via re-export, never reach laterally) |
|
|
316
|
+
| Decision | `from: ../id`, `../../id` | An ancestor decision (downward and lateral references aren't allowed; lift shared decisions to a common ancestor) |
|
|
317
|
+
|
|
318
|
+
An aliased node carries only `id`, `from`, and (where applicable) `when` — type, description, recipe, etc. are inherited from the source.
|
|
319
|
+
|
|
320
|
+
The `universe:` field in universe files selects which sub-analysis universe to load: `build_mocks: { universe: baseline }` loads `./analyses/build_mocks/universes/baseline.yaml`. Decisions inherited via `from: ../...` use the ancestor's value automatically; do not set them in the sub-analysis universe file.
|
|
273
321
|
|
|
274
322
|
## CLI Reference (astra)
|
|
275
323
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# lightcone-cli Reference
|
|
2
|
+
|
|
3
|
+
Reference for lightcone-cli execution: CLI commands, development workflow, status interpretation, and failure diagnosis. For `astra.yaml` spec syntax, see `astra-reference.md`.
|
|
4
|
+
|
|
5
|
+
## CLI Reference
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
lc init [DIR] # Scaffold a new ASTRA project
|
|
9
|
+
lc run [OUTPUT] [--universe NAME] # Materialize outputs
|
|
10
|
+
lc build [--force] [--runtime docker] # Build container images from specs
|
|
11
|
+
lc status [--universe NAME] [--json] # Materialization status (text or JSON)
|
|
12
|
+
lc verify [--universe NAME] # Recompute hashes and walk the provenance chain
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The first `lc` invocation auto-creates `~/.lightcone/config.yaml` with defaults; edit it directly to pin a container runtime or set the extraction model.
|
|
16
|
+
|
|
17
|
+
**Always run via `lc`.** Recipes must execute through `lc run` so that container builds, option resolution, resource limits, and result paths are applied. Treat the underlying execution engine as a black box — never invoke schedulers or container runtimes directly, that will bypass reproducibility guarantees.
|
|
18
|
+
|
|
19
|
+
## Creating Sub-Analyses
|
|
20
|
+
|
|
21
|
+
Sub-analyses are scaffolded by hand, since each one is just another `astra.yaml` nested in a directory. To add one:
|
|
22
|
+
|
|
23
|
+
1. Create `analyses/<name>/` with its own `astra.yaml` (and optionally `scripts/`, `universes/baseline.yaml`, `results/`).
|
|
24
|
+
2. Add a `path:` entry to the parent `astra.yaml` under `analyses:` (e.g. `analyses: { my_sub: { path: ./analyses/my_sub } }`).
|
|
25
|
+
3. Add a `<name>: { universe: baseline }` entry to each existing parent universe file.
|
|
26
|
+
|
|
27
|
+
Populate the sub-analysis's `astra.yaml` with inputs, outputs, and decisions. Use `from:` references to wire inputs and decisions to the parent or siblings — see `astra-reference.md` under "Composition Mechanics."
|
|
28
|
+
|
|
29
|
+
## Development Workflow
|
|
30
|
+
|
|
31
|
+
Three overlapping phases:
|
|
32
|
+
|
|
33
|
+
1. **Write & Debug** — Run scripts directly (`python scripts/compute.py`) to iterate. Write them recipe-ready from the start: parameterize decisions, write to convention paths, one script per output.
|
|
34
|
+
2. **Integrate** — Add `recipe:` blocks to outputs in `astra.yaml`. Track with `lc status` (`alias` / `missing` / `stale` / `ok`). Set `container:` at analysis level or per-recipe — pass an image name (e.g., `python:3.12-slim`) or a path to a Containerfile (e.g., `Containerfile`).
|
|
35
|
+
3. **Materialize** — `lc run` executes recipes inside their declared containers and writes a content-addressed manifest next to each output. Done when `lc status` shows all `ok`.
|
|
36
|
+
|
|
37
|
+
**An output is not done until `lc run` produces it.** Running scripts directly is for debugging only — final results must always come from `lc run` so they are reproducible.
|
|
38
|
+
|
|
39
|
+
### Spec-Code Invariant
|
|
40
|
+
|
|
41
|
+
**`astra.yaml` must always reflect the code and vice versa.** When you change one, update the other immediately:
|
|
42
|
+
- Add a decision to code? Add it to `astra.yaml` and all universe files.
|
|
43
|
+
- Add an output or change a script? Update the `recipe:` block in `astra.yaml`.
|
|
44
|
+
- Remove or rename something? Update both sides and run `astra validate astra.yaml`.
|
|
45
|
+
|
|
46
|
+
## Status Interpretation
|
|
47
|
+
|
|
48
|
+
`lc status` shows each declared output's materialization state per universe. Pass `--json` for machine-readable output.
|
|
49
|
+
|
|
50
|
+
- `ok` — Recipe exists, results on disk, manifest matches the current spec. Done.
|
|
51
|
+
- `stale` — Recipe or decisions changed since the last run. Re-run `lc run`.
|
|
52
|
+
- `missing` — Recipe exists but no manifest (never run, or output deleted). Run `lc run`.
|
|
53
|
+
- `alias` — Output has no recipe of its own; produced as a side effect of an upstream output (or a `from:` reference into a sub-analysis). Not independently materializable.
|
|
54
|
+
|
|
55
|
+
## Failure Diagnosis
|
|
56
|
+
|
|
57
|
+
- **Script arg not recognized** — The recipe's `command` template controls how decisions reach the script. Make sure each `{decisions.<id>}` is paired with a flag the script's argparse defines (e.g. `--<id> {decisions.<id>}` ↔ `parser.add_argument('--<id>')`).
|
|
58
|
+
- **Recipe input not found** — Materialize upstream outputs first.
|
|
59
|
+
- **Undeclared placeholder error** — A `{decisions.<id>}` or `{inputs.<id>}` in the recipe references something not listed in `Output.decisions` / `Output.inputs`. Add it to the Output's declaration, or remove the placeholder.
|
|
60
|
+
|
|
61
|
+
After failure: fix, then `lc run <output_id> --universe <name>`.
|
{lightcone_cli-0.2.0 → lightcone_cli-0.3.0}/claude/lightcone/skills/lc-build/assets/loop-prompt.md
RENAMED
|
@@ -4,7 +4,7 @@ You are inside a lc-build loop (universe: {{UNIVERSE}}). Each iteration: survey,
|
|
|
4
4
|
|
|
5
5
|
Run these commands and read their output:
|
|
6
6
|
|
|
7
|
-
1. `lc status --universe {{UNIVERSE}}` -- what's materialized,
|
|
7
|
+
1. `lc status --universe {{UNIVERSE}}` -- what's materialized (`ok`), missing, stale, or an alias of an upstream output
|
|
8
8
|
2. `git log --oneline -10` -- what happened recently
|
|
9
9
|
3. `astra validate astra.yaml` -- is the spec valid
|
|
10
10
|
4. Read `.lightcone/plans/build-plan-{{UNIVERSE}}.md` -- your implementation plan (cross off completed items as you go)
|
|
@@ -24,9 +24,9 @@ Always fix validation errors before doing anything else. Commit. Exit.
|
|
|
24
24
|
All outputs are materialized. Time to verify.
|
|
25
25
|
|
|
26
26
|
1. **Inline checks:**
|
|
27
|
-
- `astra validate astra.yaml` passes
|
|
27
|
+
- `astra validate astra.yaml` passes (this also catches undeclared `{decisions.X}` / `{inputs.X}` placeholders in recipes)
|
|
28
28
|
- `lc status --universe {{UNIVERSE}}` shows all `ok`
|
|
29
|
-
- Decision-code alignment: `grep -r "add_argument" scripts/`
|
|
29
|
+
- Decision-code alignment: every decision listed in any `Output.decisions` must be (a) referenced by a `{decisions.<id>}` placeholder in the same Output's recipe command, AND (b) accepted as a parameter by the script the recipe invokes. `astra info --decisions` lists the decisions; `grep -r "add_argument" scripts/` (or whatever the script's parsing convention is) confirms the script side. No hardcoded values.
|
|
30
30
|
2. **If any issues found:** fix them, re-materialize if needed, commit. Exit (loop continues).
|
|
31
31
|
3. **If all clean:** Spawn a verification sub-agent with explicit steps (do not rely on skill dispatch — the sub-agent cannot invoke `/lc-verify` directly):
|
|
32
32
|
```
|
|
@@ -34,7 +34,7 @@ All outputs are materialized. Time to verify.
|
|
|
34
34
|
Prompt: "Verify the spec, code, and results all agree for universe {{UNIVERSE}}. Run these checks in order:
|
|
35
35
|
1. Spec validation: run `astra validate astra.yaml` — must pass with no errors.
|
|
36
36
|
2. Materialization status: run `lc status --universe {{UNIVERSE}}` — every output must show `ok`.
|
|
37
|
-
3. Decision-code alignment (most important): run `astra info --decisions
|
|
37
|
+
3. Decision-code alignment (most important): run `astra info --decisions`. For every output that declares decisions in `Output.decisions`, confirm (a) the recipe's `command` references each one via a `{decisions.<id>}` placeholder, and (b) the invoked script accepts each as a parameter (typically via `add_argument`). No hardcoded option values. `astra validate` flags placeholder/declaration mismatches; this check covers the script side.
|
|
38
38
|
4. Results match spec: for every output in astra.yaml, confirm `results/{{UNIVERSE}}/<output_id>.<ext>` exists and looks well-formed. For `type: metric` outputs, check for valid `{'value': ...}` JSON.
|
|
39
39
|
Report all findings with file paths and line numbers. If all checks pass, end your report with exactly: VERIFIED"
|
|
40
40
|
```
|
|
@@ -47,27 +47,26 @@ These are the kinds of work you'll do, guided by the plan. Not a rigid sequence
|
|
|
47
47
|
|
|
48
48
|
### Writing scripts
|
|
49
49
|
|
|
50
|
-
1. **Write the script.**
|
|
50
|
+
1. **Write the script.** Make every decision the recipe will pass into it a real CLI parameter (typically `argparse`). The recipe's `command` template is what wires decisions to the script — e.g. `python scripts/fit.py --stellar_mass_cut {decisions.stellar_mass_cut} --output {output}` paired with `parser.add_argument('--stellar_mass_cut')`. Pick the script-side flag names so they match the recipe template; the spec doesn't dictate them.
|
|
51
51
|
The script must contain real, functional logic that produces genuine results from actual input data. No `# TODO` stubs, no hardcoded dummy values standing in for computation, no `pass` in place of real logic, no synthetic/mock data generation when real data is specified. If you cannot implement the full logic (e.g., missing a library or unclear algorithm), document the blocker in the build plan and move on — do not ship a fake version.
|
|
52
|
-
2. **Test locally:** `python scripts/<name>.py --decision1 value1 --decision2 value2
|
|
52
|
+
2. **Test locally:** invoke the script with concrete values, e.g. `python scripts/<name>.py --decision1 value1 --decision2 value2 --output /tmp/check`, using values from `universes/{{UNIVERSE}}.yaml`.
|
|
53
53
|
Note: manual script runs may write to `results/` but do NOT register as materialized.
|
|
54
|
-
Only `lc run`
|
|
55
|
-
3. **Debug until it works.** Read tracebacks, check imports (`python -c "import module"`), verify
|
|
54
|
+
Only `lc run` produces the per-output manifests that `lc status` reads.
|
|
55
|
+
3. **Debug until it works.** Read tracebacks, check imports (`python -c "import module"`), verify the recipe template's `{decisions.<id>}` / `{inputs.<id>}` references match `Output.decisions` / `Output.inputs` (decision/input id, not script flag name).
|
|
56
56
|
4. **Commit** with a message describing what the script does.
|
|
57
57
|
|
|
58
58
|
### Adding recipes & materializing
|
|
59
59
|
|
|
60
|
-
1. **
|
|
60
|
+
1. **Wire the output.** On the `Output` itself, list its dependencies in `inputs: [...]` and the decisions it consumes in `decisions: [...]`. Then add the `recipe:` block with a `command` template referencing each via `{inputs.<id>}` and `{decisions.<id>}` (and `{output}` for the artifact directory). `astra validate` rejects placeholders that don't match what the Output declares.
|
|
61
61
|
2. **Validate:** `astra validate astra.yaml`
|
|
62
|
-
3. **
|
|
63
|
-
4. **
|
|
64
|
-
5. **If it fails:** Read the error output carefully and diagnose the root cause before retrying. Never re-run the same command without changing something first. Common causes:
|
|
62
|
+
3. **Run it:** `lc run <OUTPUT> --universe {{UNIVERSE}}`. `lc run` is the only supported way to execute recipes — it handles container resolution, scheduling, and provenance. Run the loop on a machine that can actually execute recipes (the user's laptop with a container runtime, or a compute session they've already opened).
|
|
63
|
+
4. **If it fails:** Read the error output carefully and diagnose the root cause before retrying. Never re-run the same command without changing something first. Common causes:
|
|
65
64
|
- Container not built → `lc build`
|
|
66
65
|
- Upstream not materialized → materialize dependency first
|
|
67
66
|
- Script error inside container → fix the script, then re-run
|
|
68
67
|
If a second attempt also fails, note the failure in your commit message and in the build plan, then move on to other work. Come back to it in a later iteration with fresh context.
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
5. **If it succeeds:** Verify the result file exists at `results/{{UNIVERSE}}/<output_id>/` and looks well-formed.
|
|
69
|
+
6. **Commit** with a message noting what was materialized.
|
|
71
70
|
|
|
72
71
|
## Rules
|
|
73
72
|
|
|
@@ -35,7 +35,7 @@ The user should have provided a description inline (e.g., `/lc-feedback pipeline
|
|
|
35
35
|
|
|
36
36
|
Triage the repo from context:
|
|
37
37
|
- **ASTRA** — `astra` CLI, schema validation, YAML parsing, helpers
|
|
38
|
-
- **lightcone-cli** — `lc` CLI,
|
|
38
|
+
- **lightcone-cli** — `lc` CLI, recipe execution, container builds, scaffolding, skills, telemetry hooks
|
|
39
39
|
|
|
40
40
|
Default to **lightcone-cli** if ambiguous.
|
|
41
41
|
|
|
@@ -53,7 +53,24 @@ For reference, here are the decision criteria for classifying candidates:
|
|
|
53
53
|
|
|
54
54
|
Write the scan results to `CLAUDE.md` under Analysis Context as a script inventory, then draft `astra.yaml` from the scan results following the spec structure documented in `.claude/guides/astra-reference.md`. Use the decision criteria from [ASTRA Reference](../../guides/astra-reference.md) to filter the subagent's candidate decisions down to only true analytical choices — most hardcoded values are implementation details, not decisions. Use current hardcoded values as defaults.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
For each output, list the upstream artifacts it depends on under `Output.inputs: [...]` and the decisions it consumes under `Output.decisions: [...]`. Then add a `recipe.command` template that references each via `{inputs.<id>}` / `{decisions.<id>}` and writes to `{output}`. Example:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
outputs:
|
|
60
|
+
- id: galaxy_catalog
|
|
61
|
+
type: data
|
|
62
|
+
inputs: [survey_data]
|
|
63
|
+
decisions: [magnitude_cut, redshift_min]
|
|
64
|
+
recipe:
|
|
65
|
+
command: >
|
|
66
|
+
python scripts/build_catalog.py
|
|
67
|
+
--survey {inputs.survey_data}
|
|
68
|
+
--magnitude_cut {decisions.magnitude_cut}
|
|
69
|
+
--redshift_min {decisions.redshift_min}
|
|
70
|
+
--output {output}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Also generate `universes/baseline.yaml` with all defaults matching the current hardcoded values (so the first run reproduces existing behavior).
|
|
57
74
|
|
|
58
75
|
Write to `astra.yaml` and `universes/baseline.yaml`, then validate: `astra validate astra.yaml`. Fix any errors.
|
|
59
76
|
|
|
@@ -74,8 +91,8 @@ Parameterize the code so decisions can be varied across universes. The goal is m
|
|
|
74
91
|
Whatever approach you use:
|
|
75
92
|
|
|
76
93
|
- **Don't refactor, restructure, or improve the code.** Just add the parameter plumbing.
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
94
|
+
- **The recipe template is what wires decisions to scripts.** Each `{decisions.<id>}` placeholder in `recipe.command` substitutes the active option ID at runtime, and the recipe author chooses the script-side flag name. Underscore IDs in the spec → match-them-yourself flags in the script (e.g. `--outlier_sigma {decisions.outlier_sigma}` paired with `parser.add_argument('--outlier_sigma')`). There is no auto-injection.
|
|
95
|
+
- **Output paths.** The recipe receives the output directory as `{output}` — pass that through to the script (`--output {output}`) and have the script write its artifact inside that directory (e.g. `{output}/data.parquet`).
|
|
79
96
|
- **Update recipes** in `astra.yaml` if the entry point or command changed.
|
|
80
97
|
|
|
81
98
|
## Phase 3: Run & Debug
|
|
@@ -68,7 +68,7 @@ For each approved paper: `astra paper add <doi>`, `astra paper path <doi>`, then
|
|
|
68
68
|
- **Target decisions**: each decision ID, label, and options with descriptions
|
|
69
69
|
- **Timestamp**: current time in ISO 8601
|
|
70
70
|
|
|
71
|
-
The agent type is pre-configured with the user's preferred extraction model (
|
|
71
|
+
The agent type is pre-configured with the user's preferred extraction model (the `extraction_model` field in `~/.lightcone/config.yaml`). Spawn all in a single message (parallel). Show progress as results come in:
|
|
72
72
|
|
|
73
73
|
```
|
|
74
74
|
✓ Ba et al. 2016 -- 3 prior insights
|
|
@@ -22,11 +22,16 @@ astra validate astra.yaml
|
|
|
22
22
|
lc status --universe <universe_id>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Every output should show `ok`. Flag anything
|
|
25
|
+
Every output should show `ok`. Flag anything `missing`, `stale`, or only present as an `alias`.
|
|
26
26
|
|
|
27
27
|
### 3. Decision-code alignment
|
|
28
28
|
|
|
29
|
-
**The most important check.** For every decision in `
|
|
29
|
+
**The most important check.** For every output, every decision listed in `Output.decisions` must:
|
|
30
|
+
|
|
31
|
+
1. Appear as a `{decisions.<id>}` placeholder in the same Output's recipe `command`. (`astra validate` enforces this; rerun if the spec changed.)
|
|
32
|
+
2. Be accepted as a parameter by the script the recipe invokes, with no hardcoded value.
|
|
33
|
+
|
|
34
|
+
Compare `astra info --decisions` against `grep -r "add_argument" scripts/` (or whatever the script's parsing convention is). Cross-check the recipe text to see how each decision is passed.
|
|
30
35
|
|
|
31
36
|
### 4. Results match spec
|
|
32
37
|
|
|
@@ -18,9 +18,7 @@ astra.yaml # Specification: decisions, inputs, outputs
|
|
|
18
18
|
CLAUDE.md # This file
|
|
19
19
|
Containerfile # Container image for execution
|
|
20
20
|
requirements.txt # Python deps (keep in sync with scripts)
|
|
21
|
-
.lightcone/
|
|
22
|
-
lightcone.yaml # lightcone-cli config (default target, etc.)
|
|
23
|
-
dagster.yaml # Dagster instance config
|
|
21
|
+
.lightcone/ # lightcone-cli internals (managed by `lc`; do not edit)
|
|
24
22
|
universes/
|
|
25
23
|
baseline.yaml # Default decision selections
|
|
26
24
|
scripts/ # Implementation scripts
|
|
@@ -10,13 +10,16 @@ authors = [
|
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
dependencies = [
|
|
13
|
-
"astra-tools>=0.2.
|
|
13
|
+
"astra-tools>=0.2.5",
|
|
14
14
|
"click>=8.0",
|
|
15
15
|
"pyyaml>=6.0",
|
|
16
16
|
"rich>=13.0",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
17
|
+
"jinja2>=3.0",
|
|
18
|
+
"snakemake>=9.0",
|
|
19
|
+
"snakemake-interface-executor-plugins>=9.0",
|
|
20
|
+
"snakemake-interface-common>=1.14",
|
|
21
|
+
"dask>=2024.1",
|
|
22
|
+
"distributed>=2024.1",
|
|
20
23
|
"langfuse>=2.0",
|
|
21
24
|
]
|
|
22
25
|
|
|
@@ -51,13 +54,20 @@ build-backend = "hatchling.build"
|
|
|
51
54
|
source = "vcs"
|
|
52
55
|
|
|
53
56
|
[tool.hatch.build.targets.wheel]
|
|
54
|
-
packages = [
|
|
57
|
+
packages = [
|
|
58
|
+
"src/lightcone",
|
|
59
|
+
"src/snakemake_executor_plugin_dask",
|
|
60
|
+
]
|
|
55
61
|
|
|
56
62
|
[tool.hatch.build.targets.wheel.force-include]
|
|
57
63
|
"claude/lightcone" = "lightcone/cli/claude/lightcone"
|
|
58
64
|
|
|
59
65
|
[tool.hatch.build.targets.sdist]
|
|
60
|
-
include = [
|
|
66
|
+
include = [
|
|
67
|
+
"src/lightcone",
|
|
68
|
+
"src/snakemake_executor_plugin_dask",
|
|
69
|
+
"claude/lightcone",
|
|
70
|
+
]
|
|
61
71
|
|
|
62
72
|
[tool.ruff]
|
|
63
73
|
target-version = "py311"
|
|
@@ -73,5 +83,13 @@ namespace_packages = true
|
|
|
73
83
|
explicit_package_bases = true
|
|
74
84
|
mypy_path = "src"
|
|
75
85
|
|
|
86
|
+
[[tool.mypy.overrides]]
|
|
87
|
+
module = ["dask.*", "distributed.*"]
|
|
88
|
+
ignore_missing_imports = true
|
|
89
|
+
follow_untyped_imports = true
|
|
90
|
+
|
|
76
91
|
[tool.pytest.ini_options]
|
|
77
92
|
testpaths = ["tests"]
|
|
93
|
+
markers = [
|
|
94
|
+
"slow: tests that spin up real subsystems (dask cluster, etc.) — opt-in via -m slow",
|
|
95
|
+
]
|