diff --git a/docs/architecture.md b/docs/architecture.md index d185813..504fbb7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -264,6 +264,78 @@ warnings. --- +## Repository at a glance + +```text +src/lightcone/ # PEP 420 namespace package — NO __init__.py +├── cli/ # Click surface +│ ├── __init__.py # exposes main() +│ ├── commands.py # init, run, status, verify, build, export +│ └── plugin.py # plugin source-dir discovery +├── engine/ # execution substrate +│ ├── manifest.py # write_manifest, sha256_dir, code_version +│ ├── snakefile.py # generate .lightcone/Snakefile from astra.yaml +│ ├── container.py # docker/podman/podman-hpc build + recipe wrap +│ ├── dask_cluster.py # cluster lifecycle (local/SLURM/external) +│ ├── status.py # manifest-driven status walker (no Snakemake) +│ ├── verify.py # recompute hashes, walk the chain +│ ├── tree.py # sub-analysis tree helpers +│ ├── validation.py # post-recipe output sanity checks +│ └── site_registry.py # vestigial; not imported by active code +└── eval/ # evaluation harness for the agent loop + ├── cli.py harness.py sandbox.py graders.py build.py report.py models.py + +src/snakemake_executor_plugin_dask/ # Snakemake executor → dask.distributed + +claude/lightcone/ # Claude Code plugin (force-included into the wheel) +├── skills/ # lc-new, lc-from-code, lc-from-paper, +│ # lc-feedback, ralph (+ bundle siblings); +│ # reference skills: astra, lc-cli +├── agents/ # lc-extractor (literature subagent) +├── templates/ # project CLAUDE.md template +└── scripts/ # session hooks (bash): venv, validate-on-save, session-start primer + +tests/ # pytest, mirrors src/ +pyproject.toml # hatchling + hatch-vcs; ASTRA + Snakemake as deps +``` + +The `lightcone.*` namespace is a PEP 420 implicit namespace package. +**Do not add `src/lightcone/__init__.py`** — that would turn it into a +regular package and break coexistence with future sibling distributions +(`lightcone-ui`, etc.). Any new `lightcone-*` package must live under +`src/lightcone//` and ship only its own subpackage. + +--- + +## Execution flow + +```text +astra.yaml ── snakefile.generate() ──► .lightcone/Snakefile + .lightcone/snakefile-config.json + │ + snakemake -s … -d … --executor dask + │ + ┌──────────────────────┼──────────────────────┐ + │ │ │ + DAG resolution per-rule run: dask scheduler + (Snakemake) shell(recipe) (LocalCluster / + + write_manifest() SLURM-srun / + external) + │ + └─► results///data + results///.lightcone-manifest.json +``` + +What Snakemake owns (we don't write it): DAG construction, topological +execution, parallelism, dry-run, locking, retry, log capture, +per-rule resources, `--rerun-triggers` for staleness detection. + +What we own: a Snakefile generator, the manifest layer (write/read/verify), +a status walker, a verify routine, the Dask cluster manager, the +container-runtime layer, and a Snakemake executor plugin that submits +each rule to a Dask scheduler. + +--- + ## Configuration files | File | Scope | Purpose | diff --git a/docs/cli/dev.md b/docs/cli/dev.md deleted file mode 100644 index 222538a..0000000 --- a/docs/cli/dev.md +++ /dev/null @@ -1,17 +0,0 @@ -# lc dev (removed) - -This command no longer exists. lightcone-cli moved off the Dagster execution -backend in favor of Snakemake + Dask, and the `lc dev` Dagster-webserver -launcher was removed along with it. - -If you need a visual representation of the analysis DAG, generate a Snakemake -DAG image directly: - -```bash -lc run --dry-run | snakemake --dag | dot -Tsvg > dag.svg -``` - -(or rerun with `snakemake -s .lightcone/Snakefile --dag` after `lc run`). - -For materialization status, use [`lc status`](status.md). For provenance -verification, use [`lc verify`](verify.md). diff --git a/docs/cli/schematics.html b/docs/cli/schematics.html deleted file mode 100644 index 332bd61..0000000 --- a/docs/cli/schematics.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Schematics removed -

The interactive schematics page was removed. See the docs index.

- diff --git a/docs/cli/schematics.md b/docs/cli/schematics.md deleted file mode 100644 index 50c549e..0000000 --- a/docs/cli/schematics.md +++ /dev/null @@ -1,19 +0,0 @@ -# Command Schematics (removed) - -The interactive HTML schematics page was retired along with the legacy -Dagster-era CLI surface (`lc dev`, `lc target`, `lc update`). Several of the -commands it documented no longer exist. - -For an up-to-date map of the toolchain, see: - -- [Architecture](../architecture.md) — execution and integrity flow -- [CLI Overview](index.md) — every command currently shipped -- [Skills Overview](../skills/index.md) — the Claude Code surface - -If you want a graphical view of the analysis DAG for a specific project, -generate it from the Snakefile that `lc run` produces: - -```bash -lc run --dry-run # produces .lightcone/Snakefile -snakemake -s .lightcone/Snakefile --dag | dot -Tsvg > dag.svg -``` diff --git a/docs/cli/setup.md b/docs/cli/setup.md deleted file mode 100644 index f826c07..0000000 --- a/docs/cli/setup.md +++ /dev/null @@ -1,34 +0,0 @@ -# lc setup (removed) - -The `lc setup` command no longer exists. The global configuration file -(`~/.lightcone/config.yaml`) is now created automatically the first time -you run any `lc` command — there is nothing to run manually. - -## Global config file - -`~/.lightcone/config.yaml` is created with these defaults on first use: - -```yaml -container: - runtime: auto -``` - -Edit the file by hand to change the container runtime: - -```bash -$EDITOR ~/.lightcone/config.yaml -``` - -## `container.runtime` values - -| Value | Behavior | -|-------|----------| -| `auto` (default) | Pick the first usable runtime in `(podman, docker, podman-hpc)`. Skips docker if its daemon isn't reachable. Falls back to `none` if nothing's available. | -| `docker` | Pin to docker. Errors at run time if the binary isn't on PATH. | -| `podman` | Pin to podman. | -| `podman-hpc` | Pin to podman-hpc — typical on NERSC Perlmutter login nodes. | -| `none` | Explicit opt-out from containers. Recipes run on the host. | - -Setting `runtime: none` explicitly silences the provenance warning -that `lc run` shows when `auto` falls back to `none`. (See -[`lc run`](run.md#provenance-gotcha).) diff --git a/docs/cli/target.md b/docs/cli/target.md deleted file mode 100644 index 0b71205..0000000 --- a/docs/cli/target.md +++ /dev/null @@ -1,19 +0,0 @@ -# lc target (removed) - -The target/options subsystem (per-machine targets with QoS, constraint, and -time-limit options) was removed. lightcone-cli now resolves the container -runtime and the cluster shape from a much smaller surface: - -- **Container runtime** — `~/.lightcone/config.yaml` carries a single - `container.runtime` key (`auto | docker | podman | podman-hpc | none`). - The file is created automatically on first use; edit it by hand to change - runtimes. See [Global config](setup.md) and [api/container](../api/container.md). -- **Cluster shape** — derived at runtime from the environment. - `lc run` always dispatches through a Dask cluster; the cluster manager - picks `LocalCluster` on a workstation, `srun`-launched workers when - `SLURM_JOB_ID` is set, or whatever scheduler is on - `DASK_SCHEDULER_ADDRESS` if you are connecting to one. See - [api/dask_cluster](../api/dask_cluster.md). - -There are no project-level target files, no `.lightcone/lightcone.yaml` -shape beyond an opaque scratchpad, and no `lc target` command. diff --git a/docs/cli/update.md b/docs/cli/update.md deleted file mode 100644 index 64df74f..0000000 --- a/docs/cli/update.md +++ /dev/null @@ -1,32 +0,0 @@ -# lc update (removed) - -This command no longer exists. Upgrade lightcone-cli with normal Python -tooling: - -```bash -pip install --upgrade lightcone-cli # or: uv pip install -U lightcone-cli -``` - -To pull updated plugin files into an existing project (`lc init` refuses to -run if `astra.yaml` already exists, so we copy by hand): - -```bash -python - <<'PY' -import shutil -from pathlib import Path -from lightcone.cli.plugin import get_plugin_source_dir - -src = get_plugin_source_dir() -dst = Path(".claude") -for sub in ("skills", "agents", "scripts", "guides", "templates"): - s, d = src / sub, dst / sub - if d.exists(): - shutil.rmtree(d) - if s.exists(): - shutil.copytree(s, d) -print("synced from", src) -PY -``` - -A short `bin/lc-sync` helper or a `lc init --sync` flag would make this -nicer — see the issue tracker if interested. diff --git a/docs/index.md b/docs/index.md index 683a130..f2d6400 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,74 +48,6 @@ nav. spec itself (validation, paper management, evidence verification); the `lc` CLI handles execution and the agent surface. -## Repository at a glance - -```text -src/lightcone/ # PEP 420 namespace package — NO __init__.py -├── cli/ # Click surface -│ ├── __init__.py # exposes main() -│ ├── commands.py # init, run, status, verify, build, export -│ └── plugin.py # plugin source-dir discovery -├── engine/ # execution substrate -│ ├── manifest.py # write_manifest, sha256_dir, code_version -│ ├── snakefile.py # generate .lightcone/Snakefile from astra.yaml -│ ├── container.py # docker/podman/podman-hpc build + recipe wrap -│ ├── dask_cluster.py # cluster lifecycle (local/SLURM/external) -│ ├── status.py # manifest-driven status walker (no Snakemake) -│ ├── verify.py # recompute hashes, walk the chain -│ ├── tree.py # sub-analysis tree helpers -│ ├── validation.py # post-recipe output sanity checks -│ └── site_registry.py # vestigial; not imported by active code -└── eval/ # evaluation harness for the agent loop - ├── cli.py harness.py sandbox.py graders.py build.py report.py models.py - -src/snakemake_executor_plugin_dask/ # Snakemake executor → dask.distributed - -claude/lightcone/ # Claude Code plugin (force-included into the wheel) -├── skills/ # lc-new, lc-from-code, lc-from-paper, -│ # lc-feedback, ralph (+ bundle siblings); -│ # reference skills: astra, lc-cli -├── agents/ # lc-extractor (literature subagent) -├── templates/ # project CLAUDE.md template -└── scripts/ # session hooks (bash): venv, validate-on-save, session-start primer - -tests/ # pytest, mirrors src/ -pyproject.toml # hatchling + hatch-vcs; ASTRA + Snakemake as deps -``` - -The `lightcone.*` namespace is a PEP 420 implicit namespace package. -**Do not add `src/lightcone/__init__.py`** — that would turn it into a -regular package and break coexistence with future sibling distributions -(`lightcone-ui`, etc.). Any new `lightcone-*` package must live under -`src/lightcone//` and ship only its own subpackage. - -## Execution flow - -```text -astra.yaml ── snakefile.generate() ──► .lightcone/Snakefile + .lightcone/snakefile-config.json - │ - snakemake -s … -d … --executor dask - │ - ┌──────────────────────┼──────────────────────┐ - │ │ │ - DAG resolution per-rule run: dask scheduler - (Snakemake) shell(recipe) (LocalCluster / - + write_manifest() SLURM-srun / - external) - │ - └─► results///data - results///.lightcone-manifest.json -``` - -What Snakemake owns (we don't write it): DAG construction, topological -execution, parallelism, dry-run, locking, retry, log capture, -per-rule resources, `--rerun-triggers` for staleness detection. - -What we own: a Snakefile generator, the manifest layer (write/read/verify), -a status walker, a verify routine, the Dask cluster manager, the -container-runtime layer, and a Snakemake executor plugin that submits -each rule to a Dask scheduler. - ## What every materialized output gets A sidecar `.lightcone-manifest.json` next to its data, recording: diff --git a/docs/user/cluster.md b/docs/user/cluster.md index 29aa557..ffa73f1 100644 --- a/docs/user/cluster.md +++ b/docs/user/cluster.md @@ -5,11 +5,6 @@ a SLURM HPC system. There's no separate configuration to learn — the same `lc run` command works inside an allocation, just with more hardware to spread across. -> On NERSC Perlmutter, the filesystem layout (DVS-mounted home, Lustre -> scratch) and the `module load conda` workflow add a few site-specific -> considerations. See [NERSC (Perlmutter)](nersc.md) for a focused -> walkthrough. - ## The big picture `lc run` always dispatches through a Dask cluster. Three branches: @@ -27,8 +22,8 @@ you do differently on a cluster is request the nodes. ## Pre-flight: pick the right container runtime On most HPC sites, docker isn't available on compute nodes. Most -allocations of NERSC-style systems support `podman-hpc`. On a login -node: +SLURM systems (including NERSC Perlmutter) provide `podman-hpc`. On a +login node: ```bash $EDITOR ~/.lightcone/config.yaml @@ -69,16 +64,35 @@ sbatch run.sbatch # batch `run.sbatch` looks like: -```bash -#!/bin/bash -#SBATCH -N 4 -#SBATCH -t 02:00:00 -#SBATCH -C gpu - -cd $HOME/my-analysis -source .venv/bin/activate -lc run -j 16 -``` +=== "Generic" + ```bash + #!/bin/bash + #SBATCH -N 4 + #SBATCH -t 02:00:00 + #SBATCH -C gpu + + cd $HOME/my-analysis + source .venv/bin/activate + lc run -j 16 + ``` + +=== "NERSC Perlmutter" + ```bash + #!/bin/bash + #SBATCH -A + #SBATCH -q regular + #SBATCH -C gpu + #SBATCH -N 4 + #SBATCH -t 04:00:00 + + cd $SCRATCH/your-analysis + + # make `lc` available — pick the line that matches your install: + export PATH=$HOME/.local/bin:$PATH # uv tool install + # source ~/.conda/envs/your-env-name/bin/activate # conda env + + lc run -j 16 + ``` ### 2. `lc run` inside the allocation @@ -111,6 +125,25 @@ The Snakemake-via-Dask executor maps these to per-task resource requests, so a rule that needs a GPU only schedules on nodes that advertise one. +## Interactive: agent-driven runs + +During development you're usually iterating — ask the agent to build +something, check the result, adjust the spec, repeat. For that loop +you want to run the agent itself from inside a SLURM allocation so +that `lc run` executes on the compute node rather than the login node. + +```bash +salloc -A -q interactive -C gpu --nodes=1 -t 00:30:00 +# salloc drops you onto a compute node; from there: +cd /path/to/your-analysis +claude # or whichever agent CLI you prefer +``` + +Everything the agent triggers (`lc run`, scripts, etc.) now executes +on the allocated node. When you're done iterating and want a +hands-off sweep of all universes, submit `lc run` as a batch job +instead (the sbatch template above). + ## What about login-node-only operations? Build images, dry-run, look at status — all fine on a login node @@ -137,6 +170,49 @@ lc run `lc run` notices the env var and connects rather than starting its own scheduler. It does *not* tear the scheduler down on exit. +## NERSC Perlmutter: site-specific notes + +!!! note "Setting up on Perlmutter for the first time?" + The [Install](install.md) page has NERSC-specific tabs for Python + (uv vs `module load python`, conda env storage), lightcone-cli, and + the agent CLI. Come back here once `lc --version` works. + +### Storage: keep Snakemake state on `$SCRATCH` + +!!! danger "DVS silently ignores `flock()`" + `$HOME` and `/global/cfs/` are mounted on compute nodes via DVS, + which silently ignores `flock()`. Snakemake relies on `flock` for + locking, so its `.snakemake/` directory and Dask spill files + **must** live on Lustre (`$SCRATCH`), which honors `flock`. + Otherwise you get intermittent silent rule-rerun loops or hangs. + +`lc` redirects state automatically when it detects Perlmutter, so +this usually just works. To pin explicitly at project creation: + +```bash +lc init your-analysis --scratch '$SCRATCH' # kept verbatim, expanded at run time +``` + +Or, after the fact, edit `/.lightcone/lightcone.yaml`: + +```yaml +scratch_root: $SCRATCH +``` + +!!! warning "12-week purge on `$SCRATCH`" + Perlmutter purges `$SCRATCH` on a rolling 12-week window. For + outputs you need to keep, copy or symlink to + `/global/cfs/cdirs//`. + +### Further reading + +- [NERSC interactive jobs](https://docs.nersc.gov/jobs/interactive/) + — `salloc` patterns and reservation queues +- [Perlmutter system overview](https://docs.nersc.gov/systems/perlmutter/) + — node types and partitions +- [NERSC queue policy](https://docs.nersc.gov/jobs/policy/) + — QoS options for GPU and CPU partitions + ## Troubleshooting - `dask CLI is not on PATH inside the SLURM allocation`. Install @@ -148,6 +224,14 @@ own scheduler. It does *not* tear the scheduler down on exit. - Image not found on compute nodes. Re-run `lc build` on the login node — the migrate step is the one that actually publishes the image to the per-node cache. +- Snakemake locking errors or silent rule-rerun loops on Perlmutter. + `.snakemake/` ended up on DVS-mounted storage — set + `scratch_root: $SCRATCH` in the project's `.lightcone/lightcone.yaml`. +- `pip install` hangs or times out. Compute nodes have no public + internet — always install from a login node. +- `PermissionError` reading another user's symlinked `results/`. + Cross-user scratch path without group ACLs — request access from + the data owner, or copy the manifests into your own scratch. For the wiring detail, see [engine/dask_cluster](../api/dask_cluster.md) in the maintainer docs. diff --git a/docs/user/getting-started.md b/docs/user/getting-started.md index e2968e9..abbbd25 100644 --- a/docs/user/getting-started.md +++ b/docs/user/getting-started.md @@ -1,65 +1,69 @@ # Getting Started -This page walks you from "nothing on my disk" to a project that's ready -for the agent to fill in. The actual *doing science* part is in the -[tutorial](tutorial.md); this page is the orientation. +Let's go from nothing on your disk to a working, reproducible analysis. +You can read this top to bottom without running anything, or follow along — +every command is copy-paste ready. + +**What you'll build:** a small two-output analysis that fits a linear model on +a public dataset and sweeps one methodological decision (whether to standardize +features). The result is two universes, `baseline` and `raw`, each with its +own `r2` metric and `fit_plot` figure — a clean comparison ready for a paper +figure. Make sure you've finished the [install](install.md) first. ## 1. Create a project - lc init my-analysis - cd my-analysis - -`lc init` is a one-shot setup. It creates a small, opinionated -directory layout and stops; it doesn't ask any questions. +```bash +lc init r2-decision-demo +cd r2-decision-demo +``` -## 2. What you got +`lc init` is a one-shot setup. It creates a small, opinionated directory +layout and stops; it doesn't ask any questions. - my-analysis/ - ├── astra.yaml # the spec — this is where everything lives - ├── CLAUDE.md # short note for the agent (resumes context across sessions) - ├── .gitignore - ├── .git # initialized git repository (skip with --no-git) - ├── .venv/ # Python virtual env (skip with --no-venv) - ├── .claude/ # Claude Code plugin — skills, agents, hooks - ├── .lightcone/ # internal scratchpad — don't edit by hand - ├── Containerfile # build instructions for a local testing container — don't edit by hand - ├── requirements.txt # software dependencies — don't edit by hand - ├── universes/ # - ├── src/ # placeholder directories for now - └── results/ # +``` +r2-decision-demo/ +├── astra.yaml # the spec — this is where everything lives +├── CLAUDE.md # short note for the agent (resumes context across sessions) +├── .gitignore +├── .git # initialized git repository (skip with --no-git) +├── .venv/ # Python virtual env (skip with --no-venv) +├── .claude/ # Claude Code plugin — skills, agents, hooks +├── .lightcone/ # internal scratchpad — don't edit by hand +├── Containerfile # build instructions for a local testing container +├── requirements.txt # software dependencies +├── universes/ +├── src/ +└── results/ +``` The two files you'll actually look at: -### `astra.yaml` - -The single source of truth for your analysis. Inputs, outputs, -methodological decisions, recipes. Everything else lightcone-cli does -is downstream of this file. - -The boilerplate written by `lc init` is one example output and an -empty decisions block — enough to run an `lc run` and see something +**`astra.yaml`** — the single source of truth for your analysis. Inputs, +outputs, methodological decisions, recipes. Everything else lightcone-cli does +is downstream of this file. The boilerplate from `lc init` has one example +output and an empty decisions block — enough to run `lc run` and see something materialize, but not yet a real analysis. -### `CLAUDE.md` +**`CLAUDE.md`** — a short note that tells Claude Code about the project. The +skills will update this as you go (filling in working notes, design context). +You can edit it by hand whenever you want. -A short note that tells Claude Code about the project. The skills will -update this as you go (filling in working notes, design context). You -can edit it by hand whenever you want. +## 2. Open Claude Code -## 3. Open Claude Code - - claude +```bash +claude +``` -That opens an interactive session inside `my-analysis/`. Claude Code -reads `astra.yaml` and `CLAUDE.md` so it has context. +This opens an interactive session inside the project directory. Claude Code +reads `astra.yaml` and `CLAUDE.md` so it has context from the start. -## 4. The slash commands +## 3. The slash commands -Inside Claude Code. The `/lc-from-*` family is parallel by what you -start from — a question, code, or a paper — and `/lc-feedback` handles -bug reports without leaving the session. +Inside Claude Code, the `/lc-from-*` family is organized by what you're +starting from. We'll use `/lc-new` in this guide; the others work the same +way. | Command | Use it when… | |---------|--------------| @@ -68,35 +72,154 @@ bug reports without leaving the session. | `/lc-from-paper` | You have a published paper (DOI / arXiv ID) you want to reproduce. | | `/lc-feedback` | Something broke and you want to file a GitHub issue without leaving the session. | -These are structured entry points for common starting situations. You -don't have to use them — once you're inside a project, you can also -just describe what you're trying to do to Claude. `astra.yaml`, -`lc run`, and `lc verify` keep things tracked regardless of how you -got there. +These are structured entry points for common starting situations. Once inside a +project you can also just describe what you're trying to do to Claude — +`astra.yaml`, `lc run`, and `lc verify` keep things tracked regardless of how +you got there. + +## 4. Scope the analysis with `/lc-new` + +Type: + +```text +/lc-new +``` + +The agent banner switches to **RESEARCH QUESTION** and asks something like +"What are you trying to learn?" Reply in plain prose: + + I want to know how much R² changes on the diabetes dataset depending + on whether I standardize features before fitting a linear regression. + +A few follow-ups will sharpen this. After Phase 1 your `astra.yaml` already +has a `name`, `description`, and `version` — open it in another window if +you're curious; it's <30 lines. + +In Phase 2 (**ANALYSIS STRUCTURE**) the agent asks about inputs, outputs, and +whether this should be one analysis or split into stages. For our case, one +analysis is right: + +- Input: `diabetes` (sklearn's bundled toy dataset). +- Output 1: `r2`, type `metric`. +- Output 2: `fit_plot`, type `figure`. + +In Phase 3 (**DEEP DIVE**), say "skip the literature pass" to keep this a +quick demo. The agent will still walk you through identifying the decision: +does it preprocess? what options? what's the default? + +You'll end up with something like this in `astra.yaml`: + +```yaml +version: "1.0" +name: "R² with and without feature standardization" +description: "Linear regression on the diabetes dataset, sweeping the standardization choice." + +inputs: [] + +decisions: + standardize: + label: "Feature standardization" + rationale: "Standardizing changes coefficient scales and can shift R² for ridge-like models." + default: standardized + options: + standardized: { label: "StandardScaler before fit" } + raw: { label: "No preprocessing" } + +outputs: + - id: r2 + type: metric + description: "Coefficient of determination on the test split." + recipe: + command: python scripts/fit.py --standardize {standardize} --output {output[0]} + - id: fit_plot + type: figure + description: "Predicted vs true scatter." + recipe: + command: python scripts/plot.py --r2_dir {input.r2} --output {output[0]} + inputs: [r2] + +container: Containerfile +``` + +Phase 4 (**FINALIZE**) runs `astra validate astra.yaml`, writes +`universes/baseline.yaml`, and fills in the `narrative:` block. You're handed +back a short summary table — two outputs, one decision, zero prior insights. + +The agent may suggest `/clear` to free up context. Take its advice. -The next page, [The Agentic Workflow](agent-workflow.md), -explains each of these in more detail. +## 5. Implement the spec -## 5. The four CLI commands you'll actually type +```text +/clear +Implement this analysis from astra.yaml. Write the scripts, run the baseline universe, and verify the result. +``` + +The agent reads the spec, the universe file, and the empty `scripts/` dir, +then makes an implementation checklist: + +```text +1. Add Python deps (scikit-learn, matplotlib) to requirements.txt +2. Write Containerfile if missing +3. scripts/fit.py — accepts --standardize {standardized,raw}, writes r2.json +4. scripts/plot.py — reads r2_dir, writes fit_plot.png +5. lc run --universe baseline +6. lc status +7. astra validate astra.yaml +8. lc verify +``` -You can mostly stay inside Claude Code, but the durable workhorse is -the `lc` CLI. The four commands you'll touch by hand: +It works through the checklist one item at a time. You'll see commands like: ```bash -lc run # produce all outputs declared in astra.yaml -lc status # what's done, stale, or missing — fast and offline -lc verify # heavier audit: recomputes hashes, walks the input chain -lc build # build container images declared in astra.yaml +lc run --universe baseline +lc status ``` -Each of these has a one-page reference in the -[CLI section](../cli/index.md) of the maintainer docs if you want -exact flags. +Expected `lc status` output: -## 6. Read on +``` +Universe baseline + ✓ ok r2 + ✓ ok fit_plot +``` + +`lc verify` and `astra validate` should exit cleanly — no tampering, no broken +chains. If anything fails, ask the agent to fix the concrete error and rerun. + +The agent commits after each successful output, so your `git log` is a clean +record of the build. + +## 6. Verify integrity + +```bash +lc verify +``` -- [The Agentic Workflow](agent-workflow.md) — how each slash - command actually flows. -- [Tutorial: Your First Analysis](tutorial.md) — end-to-end, with the - agent doing most of the typing. -- [Glossary](glossary.md) — terminology in plain language. +This recomputes data hashes for every output and walks the input chain back to +declare whether anything has been tampered with since materialization. Useful +pre-publication, when archiving a project, or any time you want a stronger +guarantee than `lc status`. + +## What just happened + +- `astra.yaml` was the only file you "wrote" — and the agent did most of the + typing. +- The agent wrote `scripts/fit.py` and `scripts/plot.py` with argparse-driven + decision injection. +- `lc run` generated `.lightcone/Snakefile` from your spec, dispatched each + rule through Snakemake, and wrote a per-output sidecar manifest recording the + recipe, container image, decisions, input hashes, and output hash. +- `lc status` and `lc verify` rely on those manifests — they don't re-execute + anything; they just check. + +If your laptop dies tomorrow and you `git clone` the repo on a fresh machine +and run `lc run`, you'll get bit-identical results. + +## Where to next + +- [The Agentic Workflow](agent-workflow.md) — what each slash command does in + detail. +- [Running on a Cluster](cluster.md) — take the same project to SLURM. +- [Troubleshooting](troubleshooting.md) — when something goes sideways. +- [Glossary](glossary.md) — terms like universe, decision, and manifest in + plain language. diff --git a/docs/user/index.md b/docs/user/index.md index 24751b7..0f56a41 100644 --- a/docs/user/index.md +++ b/docs/user/index.md @@ -13,15 +13,13 @@ implementation; **you stay in charge of the scientific choices**. - [Install](install.md) — get `lc` and Claude Code running on your machine. -- [Getting Started](getting-started.md) — your first `lc init` and - what every directory means. +- [Getting Started](getting-started.md) — create your first project, + run it end-to-end, and understand what each piece does. - [The Agentic Workflow](agent-workflow.md) — `/lc-new`, `/lc-from-code`, `/lc-from-paper`, and `/lc-feedback` — what each one does and when to reach for it. -- [Tutorial: Your First Analysis](tutorial.md) — an end-to-end worked - example, written so you can read it without running anything. - [Running on a Cluster](cluster.md) — taking your analysis to a SLURM - HPC system. + HPC system, including Perlmutter-specific notes. - [Troubleshooting](troubleshooting.md) — common issues and how to unstick them. - [Glossary](glossary.md) — the terms that show up everywhere @@ -53,7 +51,7 @@ unhurried version. things itself. - **A workflow language.** Recipes in `astra.yaml` are short shell or Python commands, not a DSL. There's no learning curve beyond what's - in the [tutorial](tutorial.md). + in [Getting Started](getting-started.md). - **An IDE.** `lc` is a command-line tool; the agent surface lives inside the agent harness (Claude Code for now). diff --git a/docs/user/install.md b/docs/user/install.md index 6682074..9ea81ff 100644 --- a/docs/user/install.md +++ b/docs/user/install.md @@ -20,6 +20,50 @@ If you don't already have a recent Python === "Windows" [python.org](https://www.python.org/downloads/) or WSL +=== "NERSC Perlmutter" + NERSC doesn't ship `uv`, but it installs into your home dir with a + single curl: + + ```bash + curl -LsSf https://astral.sh/uv/install.sh | sh + uv python install 3.12 + ``` + + Both `uv` and an isolated Python 3.12 land under `~/.local/`. + Make sure `~/.local/bin` is on your `PATH`. + + ??? note "Alternative: NERSC's `python` module" + `module load python` gives you a ready-to-use distribution with + `conda`, `pip`, and many scientific packages already installed: + + ```bash + module load python # NERSC Python (3.11+) + ``` + + Convenient, but the module is shared and read-only. For custom + packages, build a conda env on top: + + ```bash + conda create -n your-env-name python=3.11 -y + conda activate your-env-name + ``` + + This is NERSC's [recommended path for `pip install`](https://docs.nersc.gov/development/languages/python/nersc-python/) + when you need custom packages. + + !!! warning "Storage: 40 GB home quota" + Conda envs land under `~/.conda/envs/` by default. The + Perlmutter home quota is **40 GB**, which gets eaten quickly. + NERSC recommends `/global/common/software//` for + larger envs. If you want them on `$SCRATCH` (note: 12-week + purge), move and symlink: + + ```bash + conda deactivate + mv ~/.conda/envs/your-env-name $SCRATCH/conda-envs/ + ln -s $SCRATCH/conda-envs/your-env-name ~/.conda/envs/your-env-name + ``` + !!! tip "Recommendation" We highly recommend the use of [uv](https://docs.astral.sh/uv/) to manage Python installation and virtual environments. @@ -46,6 +90,40 @@ is `lc`. python -m pip install lightcone-cli ``` +=== "NERSC Perlmutter" + With `uv` (recommended — isolates `lc` under `~/.local/share/uv/tools/`): + + ```bash + uv tool install lightcone-cli + ``` + + With pip, the exact command depends on which Python you're using: + + ```bash + # NERSC python module + module load python + python -m pip install --user lightcone-cli # lands in ~/.local/bin/ + + # Conda env + conda activate your-env-name + python -m pip install lightcone-cli + ``` + + `astra-tools` is a transitive dependency — pulled in automatically. + + ??? note "From source (contributors only)" + ```bash + git clone https://github.com/LightconeResearch/lightcone-cli.git + uv pip install -e ./lightcone-cli + ``` + + To also hack on `astra-tools`: + + ```bash + git clone https://github.com/LightconeResearch/ASTRA.git + uv pip install -e ./ASTRA + ``` + Get a confirmation of the proper installation by running lc --version # → lightcone-cli, version ... @@ -68,17 +146,36 @@ your PATH (and skips docker if its daemon isn't running). Feel free to pin the r ## 4. Agentic CLI -Most of your interactions with a lightcone project happen *through* an agent-based CLI, for now we are supporting Claude Code. - -Install Claude Code +Most of your interactions with a lightcone project happen *through* an +agent-based CLI. Any agent that can drive a project shell works — the +choice is yours. +=== "Claude Code" + ```bash curl -fsSL https://claude.ai/install.sh | bash + ``` + + Make sure `~/.local/bin` is on your `PATH`, then verify and + authenticate: + + ```bash + claude --version + claude # first run prompts for login (claude.ai or API key) + ``` + + Other install routes (npm, native package managers) are documented + in the [Claude Code installation docs](https://docs.claude.com/en/docs/claude-code/setup). -Open a project in your terminal or editor (see [Getting Started](getting-started.md)) and run +=== "OpenAI Codex" + See the [openai/codex](https://github.com/openai/codex) repo README + for install options. - claude +=== "opencode" + ```bash + curl -fsSL https://opencode.ai/install | bash + ``` -Inside Claude Code you'll type slash commands like `/lc-new`, +Open a project in your terminal or editor (see [Getting Started](getting-started.md)) and run your agent CLI from inside it. Inside Claude Code you'll type slash commands like `/lc-new`, `/lc-from-code`, and `/lc-from-paper` — see [The Agentic Workflow](agent-workflow.md). @@ -105,3 +202,42 @@ isolation. Both should print help text. If `lc` is shadowed by an `ls` alias, unset it (`unalias lc`) or use the full path (`$(which lc) --version`). + +## Updating + +=== "uv tool" + ```bash + uv tool upgrade lightcone-cli + ``` + +=== "pip" + ```bash + pip install -U lightcone-cli astra-tools + ``` + +=== "Source" + ```bash + cd path/to/lightcone-cli + git pull + uv pip install -e . # only needed if pyproject.toml changed + ``` + + Editable installs auto-follow source edits — switching branches or + pulling new commits is reflected immediately in `lc`. Re-install + only when `pyproject.toml` adds a new dependency. + +## Uninstalling + +=== "uv tool" + ```bash + uv tool uninstall lightcone-cli + ``` + +=== "pip" + ```bash + pip uninstall lightcone-cli + ``` + +!!! note "Keep your config?" + `~/.lightcone/config.yaml` survives the uninstall. Delete it too + if you want a clean slate. diff --git a/docs/user/nersc.md b/docs/user/nersc.md deleted file mode 100644 index 60331c6..0000000 --- a/docs/user/nersc.md +++ /dev/null @@ -1,315 +0,0 @@ -# lightcone-cli on NERSC (Perlmutter) - -A practical guide for running [`lightcone-cli`](https://github.com/LightconeResearch/lightcone-cli) on **Perlmutter**. The CLI itself behaves the same as on a laptop — the wrinkles are in the filesystem layout (DVS-mounted home, Lustre scratch), the container runtime (`podman-hpc`), and SLURM submission. This page covers all three. - -!!! tip "Already familiar with the basics?" - The generic [Install](install.md) and [Running on a Cluster](cluster.md) pages cover the cross-platform story. This page is the NERSC-specific overlay — read it first if Perlmutter is your home base. - ---- - -## 0. Agentic CLI - -`lightcone-cli` is the execution layer of the `lightcone` project — it harnesses an **agent-based CLI** to follow the `astra` standard while building and running an analysis. The choice of agent is open: anything that can drive a project shell works. This guide uses Claude Code as the running example — substitute your preferred agent CLI throughout if you use a different one. - -Installing Claude Code: - -```bash -curl -fsSL https://claude.ai/install.sh | bash # installs to ~/.local/bin/claude -``` - -Make sure `~/.local/bin` is on your `PATH`, then verify and authenticate: - -```bash -claude --version -claude # first run prompts for login (claude.ai or API key) -``` - -Other install routes (npm, native package managers) are documented in the [Claude Code installation docs](https://docs.claude.com/en/docs/claude-code/setup). - -!!! note "Other agent CLIs" - Other agentic CLIs work too — for example: - - - [OpenAI Codex](https://github.com/openai/codex) — see the repo README for install options. - - [opencode](https://opencode.ai/docs/#install) — install via `curl -fsSL https://opencode.ai/install | bash`. - - Pick whichever you prefer; the rest of this guide writes `claude` in concrete commands, but the workflow is the same with any agent CLI. - ---- - -## 1. Python - -Like the generic [Install](install.md#1-python) page, we recommend [`uv`](https://docs.astral.sh/uv/) for managing Python on Perlmutter — it's faster than pip and gives you a Python independent of NERSC's `module` system. NERSC doesn't ship it, but it installs into your home dir with a single curl: - -```bash -curl -LsSf https://astral.sh/uv/install.sh | sh -uv python install 3.12 -``` - -This drops both `uv` and an isolated Python 3.12 under `~/.local/`. Make sure `~/.local/bin` is on your `PATH`. - -!!! note "Alternative: NERSC's `python` module" - If you'd rather use NERSC's pre-built environment, `module load python` gives you a ready-to-use distribution with `conda`, `pip`, and many scientific packages already installed: - - ```bash - module load python # NERSC Python (3.11+); brings conda and pip onto PATH - ``` - - Convenient, but the module is shared and read-only — you can't pin a different Python version or guarantee dependency isolation. For that, build a conda env on top: - - ```bash - conda create -n your-env-name python=3.11 -y - conda activate your-env-name - ``` - - This is also NERSC's [recommended path for `pip install`](https://docs.nersc.gov/development/languages/python/nersc-python/) when you need custom packages. - -!!! warning "Storage note: 40 GB home quota" - Conda envs land under `~/.conda/envs/` by default. The Perlmutter home quota is **40 GB**, which gets eaten quickly. NERSC recommends `/global/common/software//` for larger envs. If you really want them on `$SCRATCH` (note: 12-week purge!), move and symlink: - - ```bash - conda deactivate - mv ~/.conda/envs/your-env-name $SCRATCH/conda-envs/ - ln -s $SCRATCH/conda-envs/your-env-name ~/.conda/envs/your-env-name - ``` - - See [NERSC's Python guide](https://docs.nersc.gov/development/languages/python/nersc-python/) for the full storage strategy. - ---- - -## 2. Install lightcone-cli - -The package on PyPI is `lightcone-cli`; the command it provides is `lc`. The recommended install uses `uv tool`, which isolates `lc` in its own venv under `~/.local/share/uv/tools/` and exposes a wrapper at `~/.local/bin/lc`: - -```bash -uv tool install lightcone-cli -``` - -`astra-tools` is a transitive dependency — pulled in automatically. - -!!! note "Alternative: pip" - If you'd rather not use uv, install with pip. The exact command depends on which Python you're using: - - === "NERSC python module" - ```bash - module load python - python -m pip install --user lightcone-cli # --user lands in ~/.local/bin/ - ``` - - === "Conda env" - ```bash - conda activate your-env-name - python -m pip install lightcone-cli - ``` - -### From source (contributors only) - -If you want to track the latest commits or contribute back, clone the repo and install editably. **Most users should stick with PyPI.** - -```bash -cd ~/.lightcone # or wherever you keep clones -git clone https://github.com/LightconeResearch/lightcone-cli.git -uv pip install -e ./lightcone-cli # or: pip install -e ./lightcone-cli -``` - -To hack on `astra-tools` itself (PyPI name `astra-tools`, GitHub repo `ASTRA`): - -```bash -git clone https://github.com/LightconeResearch/ASTRA.git -uv pip install -e ./ASTRA -``` - -For development tooling (pytest, ruff, mypy): - -```bash -uv pip install -e "./lightcone-cli[dev]" -``` - -### Sanity check - -```bash -which lc # should resolve under ~/.local/bin/ or your active env -lc --version -lc --help -``` - -!!! note "Global config is auto-created" - The first `lc` invocation writes `~/.lightcone/config.yaml` with `runtime: auto` — no manual setup step needed. You'll pin it to `podman-hpc` for compute nodes in [§5](#5-running-on-compute-nodes). - ---- - -## 3. Initialize a new project - -Scaffold a project directory and drop into it with the agent: - -```bash -lc init your-analysis # scaffolds a fresh project tree -cd your-analysis -claude # launch your agent CLI (Claude Code shown here) -``` - ---- - -## 4. Start your research - -Once your agent CLI is open (Claude Code in this guide's examples), drive everything from there. The `lc-*` skills are how you tell the agent what to build: - -=== "Start fresh" - ```text - /lc-new Please sample a standard Gaussian distribution using numpy. - ``` - -=== "Port existing code" - ```text - /lc-from-code I have code that samples a standard Gaussian distribution using numpy at @../gaussian_sampling. Please create an analysis based on it. - ``` - -After that, just keep talking to the agent in plain English about what you want to build next. - -!!! warning "You're still on a login node" - Everything from `lc init` through your first `/lc-new` runs on a Perlmutter **login node**. That's fine for scaffolding and small recipes, but anything heavyweight needs a compute node — see [§5](#5-running-on-compute-nodes). - ---- - -## 5. Running on compute nodes - -Login nodes are shared and rate-limited — fine for `lc init`, `lc status`, and small `lc build` calls, but anything heavyweight belongs on a compute node. - -### Pre-flight: pin the container runtime and build images - -Perlmutter compute nodes ship `podman-hpc`. Pin it once globally: - -```yaml -# ~/.lightcone/config.yaml -container: - runtime: podman-hpc -``` - -Then, on a login node, build and migrate your project's images: - -```bash -cd /path/to/your-analysis -lc build -``` - -`lc build` runs `podman-hpc build` followed by `podman-hpc migrate`, which copies the image into each compute node's local container cache. See [Running on a Cluster → Pre-flight](cluster.md#pre-flight-pick-the-right-container-runtime) for the underlying mechanics. - -### Interactive runs (agent-driven) - -The agent calls `lc run` for you whenever a recipe needs to materialize — you never call it directly. What you *do* control is **where the agent is running**: it inherits the shell environment you launched it from. To put the agent's recipes onto a compute node, simply launch it from inside a SLURM allocation: - -```bash -salloc -A -q interactive -C gpu --nodes=1 -t 00:30:00 -# salloc drops you onto a compute node; from there: -cd /path/to/your-analysis -claude # or whichever agent CLI you use -``` - -Now everything the agent triggers (`lc run`, scripts, etc.) executes on the allocated node. - -!!! note "Picking a QoS" - The `interactive` QoS on the GPU partition is right for development. For longer or larger sessions, see [NERSC's queue policy reference](https://docs.nersc.gov/jobs/policy/). - -### Unattended batch runs (no agent in the loop) - -For production sweeps where the recipes are already nailed down, you can submit `lc run` directly as a batch job — no agent CLI involved. See [Running on a Cluster → A typical SLURM workflow](cluster.md#a-typical-slurm-workflow) for the generic template; on Perlmutter, the only addition is the `-A` / `-q` directives: - -```bash -#!/bin/bash -#SBATCH -A -#SBATCH -q regular -#SBATCH -C gpu -#SBATCH -N 4 -#SBATCH -t 04:00:00 - -cd $SCRATCH/your-analysis - -# Make `lc` available — pick the line that matches your install: -export PATH=$HOME/.local/bin:$PATH # uv tool install (default) -# source ~/.conda/envs/your-env-name/bin/activate # conda env - -lc run -j 16 -``` - -!!! note "When to use this path" - The agent-driven flow above is the right tool during development. Reach for batch submission when you've finished iterating and want a hands-off sweep. - -### Storage gotcha: Snakemake state must live on `$SCRATCH` - -!!! danger "DVS silently ignores `flock()`" - `$HOME` and `/global/cfs/` are mounted on compute nodes via DVS, which silently ignores `flock()`. Snakemake (and any sane locking system) relies on `flock`, so its `.snakemake/` directory and Dask spill files **must** live on Lustre (`$SCRATCH`), which honors `flock`. Otherwise you get intermittent silent rule-rerun loops or hangs. - -`lc` redirects state automatically when it detects Perlmutter, so this usually just works. To pin explicitly at project creation: - -```bash -lc init your-analysis --scratch '$SCRATCH' # kept verbatim, expanded at run time -``` - -Or, after the fact, edit `/.lightcone/lightcone.yaml`: - -```yaml -scratch_root: $SCRATCH -``` - -!!! warning "12-week purge on `$SCRATCH`" - Perlmutter purges `$SCRATCH` on a rolling 12-week window. For outputs you need to keep, copy or symlink to `/global/cfs/cdirs//`. - -### Further reading - -- [NERSC interactive jobs](https://docs.nersc.gov/jobs/interactive/) — `salloc` patterns and reservation queues -- [Perlmutter system overview](https://docs.nersc.gov/systems/perlmutter/) — node types and partitions -- [NERSC Python guide](https://docs.nersc.gov/development/languages/python/nersc-python/) — module, conda, and pip layering - ---- - -## 6. Common troubleshooting - -| Symptom | Likely cause | Fix | -|---|---|---| -| `lc: command not found` | Wrong env active, or `~/.local/bin` not on `PATH` | `which lc`; reinstall in the active env, or fix `PATH` | -| `lc` runs but uses unexpected code | Two installs across two envs shadowing each other on `PATH` | `which lc` and uninstall the stale one | -| `ModuleNotFoundError: lightcone.cli.__main__` | Tried `python -m lightcone.cli` (the package isn't directly executable) | Use the `lc` console script instead | -| Snakemake locking errors / silent rule rerun loops | `.snakemake/` ended up on DVS-mounted storage | Set `scratch_root: $SCRATCH` in the project's `.lightcone/lightcone.yaml` | -| `ImportError: cannot import name 'resolve_analysis_tree' from 'astra.helpers'` | Stale `astra-tools` (pre-0.2.5) | `pip install -U astra-tools` | -| `PermissionError` reading another user's symlinked `results/` | Cross-user scratch path without group ACLs | Request access from the data owner, or copy the manifests into your own scratch | -| `pip install` hangs or times out on a compute node | Compute nodes have no public internet | Always install from a login node | - ---- - -## 7. Updating - -=== "uv tool" - ```bash - uv tool upgrade lightcone-cli - ``` - -=== "pip" - ```bash - pip install -U lightcone-cli astra-tools - ``` - -=== "Source" - ```bash - cd ~/.lightcone/lightcone-cli - git pull - uv pip install -e . # only needed if pyproject.toml changed - ``` - - Editable installs auto-follow source edits — switching branches or pulling new commits is reflected immediately in `lc`. Re-install only when `pyproject.toml` adds a new dependency or changes the `[project.scripts]` table. - ---- - -## 8. Uninstalling - -=== "uv tool" - ```bash - uv tool uninstall lightcone-cli - ``` - -=== "pip" - ```bash - pip uninstall lightcone-cli - rm -rf ~/.lightcone/lightcone-cli # only for source installs - ``` - -!!! note "Keep your config?" - `~/.lightcone/config.yaml` survives the uninstall. Delete it too if you want to start fresh. diff --git a/docs/user/tutorial.md b/docs/user/tutorial.md deleted file mode 100644 index c5e8d67..0000000 --- a/docs/user/tutorial.md +++ /dev/null @@ -1,221 +0,0 @@ -# Tutorial: Your First Analysis - -Let's build a tiny but real analysis from scratch. We'll fit a linear -model on a small public dataset, sweep one methodological decision, and -get to publishable, reproducible results. - -You can read this top to bottom without typing anything; if you want to -follow along, every command is plain copy-paste. - -## What we'll build - -A two-output analysis: - -- **`r2`** — the coefficient of determination of a linear model on a - toy dataset. -- **`fit_plot`** — a scatter plot of predictions vs. truth. - -We'll declare one **decision**: whether to standardize the features -before fitting. That gives us two universes, `standardized` and -`raw`, each with its own results. - -## 1. Make the project - -```bash -lc init r2-decision-demo -cd r2-decision-demo -claude -``` - -You're now in Claude Code, sitting in a fresh project. The first line -on the screen is the session start banner, which probably says "no -recipes yet." - -## 2. Scope the analysis with `/lc-new` - -Type: - -```text -/lc-new -``` - -The agent banner switches to **RESEARCH QUESTION** and asks something -like "What are you trying to learn?" Reply in plain prose: - - I want to know how much R² changes on the diabetes dataset depending on whether I standardize features before fitting a linear regression. - -A few follow-ups will sharpen this. After Phase 1 your `astra.yaml` -already has a `name`, `description`, and `version` — open it in another -window if you're curious; it's <30 lines. - -In Phase 2 (**ANALYSIS STRUCTURE**) the agent will ask about inputs, -outputs, and whether this should be one analysis or split into stages. -For our case, one analysis is the right answer: - -- Input: `diabetes` (sklearn's bundled toy dataset). -- Output 1: `r2`, type `metric`. -- Output 2: `fit_plot`, type `figure`. - -In Phase 3 (**DEEP DIVE**), if you want to skip literature for a tiny -demo, just say "skip the literature pass." The agent will still walk -you through identifying the decision: does it preprocess? what -options does it have? what's the default? - -You'll end up with something like this in `astra.yaml`: - -```yaml -version: "1.0" -name: "R² with and without feature standardization" -description: "Linear regression on the diabetes dataset, sweeping the standardization choice." - -inputs: [] - -decisions: - standardize: - label: "Feature standardization" - rationale: "Standardizing changes coefficient scales and can shift R² for ridge-like models." - default: standardized - options: - standardized: { label: "StandardScaler before fit" } - raw: { label: "No preprocessing" } - -outputs: - - id: r2 - type: metric - description: "Coefficient of determination on the test split." - recipe: - command: python scripts/fit.py --standardize {standardize} --output {output[0]} - - id: fit_plot - type: figure - description: "Predicted vs true scatter." - recipe: - command: python scripts/plot.py --r2_dir {input.r2} --output {output[0]} - inputs: [r2] - -container: Containerfile -``` - -Phase 4 (**FINALIZE**) runs `astra validate astra.yaml`, writes -`universes/baseline.yaml`, and fills in the `narrative:` block in -`astra.yaml` (`summary`, `methods`, `inputs`, `outputs`). You're handed -back a short summary table — two outputs, one decision, zero prior -insights. - -The agent may suggest `/clear` to free up context. Take its advice, -then ask Claude Code to implement the spec. - -## 3. Build it - -```text -/clear -Implement this analysis from astra.yaml. Write the scripts, run the baseline universe, and verify the result. -``` - -The agent reads everything (spec, universe file, empty `scripts/` dir, -plus the `/astra` and `/lc-cli` reference skills primed at session -start) and makes an implementation checklist. It might look like this: - -```text -1. Add Python deps (scikit-learn, matplotlib) to requirements.txt -2. Write Containerfile if missing -3. scripts/fit.py — accepts --standardize {standardized,raw}, writes r2.json -4. scripts/plot.py — reads r2_dir, writes fit_plot.png -5. lc run --universe baseline -6. lc status -7. astra validate astra.yaml -8. lc verify -``` - -It works through the checklist one item at a time. You'll see commands -like: - -```bash -lc run --universe baseline -lc status -astra validate astra.yaml -lc verify -``` - -Expected `lc status` output: - -``` -Universe baseline - ✓ ok r2 - ✓ ok fit_plot -``` - -Expected validation and verification output is boring in the best way: -`astra validate astra.yaml` exits cleanly, and `lc verify` reports no -tampering, broken provenance chain, or missing manifests. If anything -fails, ask the agent to fix the concrete error and rerun the same -commands. - -The agent commits after each successful output, so your `git log` is a -clean record of the build. - -## 4. Add the second universe - -The whole point of decisions is to sweep them. Drop out of Claude -Code (`Ctrl+D` or `/exit`) and create the second universe: - -```bash -astra universe generate -n raw -d standardize=raw -``` - -That writes `universes/raw.yaml` selecting `standardize: raw`. Now -materialize it: - -```bash -lc run --universe raw -lc status -``` - -You should see: - -```text -Universe baseline - ✓ ok r2 - ✓ ok fit_plot -Universe raw - ✓ ok r2 - ✓ ok fit_plot -``` - -Each universe has its own `results//` tree. The two `r2.json` -files are the comparison your paper figure needs. - -## 5. Verify integrity - -```bash -lc verify -``` - -This recomputes data hashes for every output and walks the input chain -back to declare whether anything has been tampered with since -materialization. Useful pre-publication, useful when archiving a -project, useful any time you want a stronger guarantee than `lc -status`. - -## What just happened - -Concretely: - -- `astra.yaml` was the only file you "wrote" — and the agent did most - of the typing. -- The agent wrote `scripts/fit.py` and `scripts/plot.py` with - argparse-driven decision injection. -- `lc run` generated `.lightcone/Snakefile` from your spec, dispatched - each rule through Snakemake, and wrote a per-output sidecar manifest - (`.lightcone-manifest.json`) recording the recipe, container image, - decisions, input hashes, and output hash. -- `lc status` and `lc verify` rely on those manifests — they don't - re-execute anything; they just check. - -If your laptop dies tomorrow and you `git clone` the repo on a fresh -machine and `lc run` it, you'll get bit-identical results (modulo -floating-point nondeterminism in your numerical libraries). - -## Where to next - -- [Running on a Cluster](cluster.md) — take the same project to SLURM. -- [Troubleshooting](troubleshooting.md) — when something goes sideways. diff --git a/zensical.toml b/zensical.toml index 97b2091..23be2cb 100644 --- a/zensical.toml +++ b/zensical.toml @@ -13,15 +13,16 @@ nav = [ {"Install" = "user/install.md"}, {"Getting Started" = "user/getting-started.md"}, {"The Agent Workflow" = "user/agent-workflow.md"}, - {"Tutorial: Your First Analysis" = "user/tutorial.md"}, - {"Multiverse Analyses" = "user/multiverse.md"}, {"Running on a Cluster" = "user/cluster.md"}, - {"NERSC (Perlmutter)" = "user/nersc.md"}, {"Troubleshooting" = "user/troubleshooting.md"}, {"Glossary" = "user/glossary.md"}, ]}, {"Maintainer Docs" = [ {"Architecture" = "architecture.md"}, + {"Contributing" = [ + {"Development Setup" = "contributing/setup.md"}, + {"Testing" = "contributing/testing.md"}, + ]}, {"CLI Reference" = [ {"Overview" = "cli/index.md"}, {"lc init" = "cli/init.md"}, @@ -57,10 +58,6 @@ nav = [ {"check-sentence-by-sentence" = "skills/check-sentence-by-sentence.md"}, {"Authoring Skills" = "skills/authoring.md"}, ]}, - {"Contributing" = [ - {"Development Setup" = "contributing/setup.md"}, - {"Testing" = "contributing/testing.md"}, - ]}, ]}, ]