Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1f1a82d
reset on clean basis
EiffL Aug 17, 2026
16d4fb2
streamline pyproject
EiffL Aug 17, 2026
43b8032
minor changes
EiffL Aug 17, 2026
c0bf65b
minor clean-up
EiffL Aug 18, 2026
9921c76
fixing minor surprise
EiffL Aug 18, 2026
e391564
Layer 5: the exec boundary and `lc run` (#173)
EiffL Aug 18, 2026
45ad94a
Say "not a Lightcone project" and point elsewhere, not at `lc init`
EiffL Aug 18, 2026
0f0cf61
Make the project tree writable, and fix the outside-a-project message…
EiffL Aug 18, 2026
9395dcf
Layers 2 and 4: identity, provenance, and `lc materialize` (#175)
EiffL Aug 19, 2026
19986bb
Point the uv.toml limitation at the issue that owns it
EiffL Aug 19, 2026
e7cea62
The engine is the host's uv tool, not a project dependency (#178)
EiffL Aug 20, 2026
a67ddf2
Layer 6: the container hatch — OCI runtimes as the exec boundary (#180)
EiffL Aug 20, 2026
735f4a7
Layer 7: venues — SLURM in-allocation execution and podman-hpc (#181)
EiffL Aug 20, 2026
efbbbf3
Layer 8: the publication view — the RO-Crate converged, integrity as …
EiffL Aug 20, 2026
8b4e038
Reactivate the agentic eval against the rebuilt CLI (#183)
EiffL Aug 20, 2026
bd68cff
Docs rewrite 1/5: README, landing page, install, getting started (#185)
EiffL Aug 21, 2026
8ee52ee
Docs rewrite 2/5: core concepts, cluster, troubleshooting, glossary (…
EiffL Aug 21, 2026
86e75f7
Hardening: close the residues recorded through layer 8 (#184)
EiffL Aug 21, 2026
bbe6d85
Docs rewrite 3/5: the CLI reference (#187)
EiffL Aug 21, 2026
8099d85
Docs rewrite 4/5: the developer corner (#188)
EiffL Aug 21, 2026
5d47485
Docs rewrite 5/5: reconcile CLAUDE.md and the doc-drift check (#189)
EiffL Aug 21, 2026
960de67
Catch the docs up to the hardening pass: network is uncontrolled ever…
EiffL Aug 21, 2026
6e49d04
Merge main into clean_rebuild: carry the mike docs-versioning integra…
EiffL Aug 21, 2026
13495f9
Eval trace follow-up: lc run syntax note, clean harness env (#191)
EiffL Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions .github/workflows/check-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,30 @@ jobs:
Your task: check whether the project documentation is still accurate
and complete given the code changes introduced by this PR.

The documentation is structured, and each kind of change has a home:
- A verb's flags, output, JSON shape, or exit codes → its page in docs/cli/
(one page per verb, plus the overview's exit-code contract).
- An engine module's responsibility, key symbols, or invariants → its page
in docs/api/ (hand-written module tours) and, for cross-cutting shifts,
docs/architecture.md.
- User-visible behavior (scaffold contents, states, refusal messages,
environment model, SLURM, publication) → docs/user/ (getting-started
quotes real console output; troubleshooting quotes real refusals) and
README.md's quick start.
- Test structure, dev workflow, or conventions → docs/contributing/.

Steps to follow:
1. Run: git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}
to get the list of changed files.
2. Read the changed source files (focus on .py, .ts, .js, .go, .rs files).
3. Read existing documentation files (README*, docs/**/*.md, CHANGELOG*, CONTRIBUTING*).
4. SKIP any files under skills/ or named SKILL.md — these are not user-facing docs.
5. Identify any documentation that is now inaccurate, incomplete, or missing
given the code changes.
2. Read the changed source files (focus on src/**/*.py and the workflows).
3. Read the documentation pages the map above points at for those changes.
4. SKIP CLAUDE.md and evals/ — agent instructions and the eval harness are
maintained separately, not user-facing docs.
5. Identify documentation that is now inaccurate, incomplete, or missing.
Two failure modes matter most here: a quoted console output or refusal
message that no longer matches what the CLI prints, and a documented
flag, verb, state, or file that no longer exists (the docs must never
describe more than the code delivers — no foreshadowing).

Then:
- Post a comment on PR #${{ github.event.pull_request.number }} summarising
Expand Down
64 changes: 46 additions & 18 deletions .github/workflows/eval.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ name: Eval
# Minimal agentic eval: scaffold a project from the eval seed on the
# runner, let Claude Code (with the astra plugin) build it using the
# branch's lc/astra, then check the results with astra validate and
# lc status.
# lc materialize --check.

on:
pull_request:
workflow_dispatch:
pull_request:
# `ready_for_review` beside the defaults, so flipping a draft to
# ready triggers the run the draft guard below skips.
types: [opened, synchronize, reopened, ready_for_review]

# Only one eval per PR at a time — cancel in-progress runs
concurrency:
Expand All @@ -28,20 +31,35 @@ jobs:
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
# Full history so hatch-vcs sees the tags and stamps a truthful
# dev version into the installed engine (and every manifest's
# lc_version) instead of a 0.1.dev fallback
fetch-depth: 0

- name: Set up uv
# No python-version input: it would export an ambient UV_PYTHON,
# which lc's install-settings scrub then rightly warns about on
# every single invocation the agent makes. The interpreter is
# pinned per tool install below instead.
uses: astral-sh/setup-uv@v6
with:
python-version: "3.12"
enable-cache: true
activate-environment: true

- name: Install lightcone-cli (branch under test)
- name: Install lightcone-cli + astra (uv tools)
# The engine is the host's uv tool — install the branch under
# test the way a user installs a release: `uv tool install`,
# which links `lc` and (via the mirrored entry points) the
# git-annex executables into ~/.local/bin. The commit under test
# is the checkout itself, stamped into the version by hatch-vcs.
# astra's executable belongs to astra-tools, so it is its own
# tool install, pinned to the version the branch depends on. No
# venv is activated: the agent's shell sees the tools exactly as
# an end user's would.
run: |
uv sync
# Scientific deps the task's analysis scripts will need — the
# agent can add more with `uv pip install`
uv pip install numpy scipy matplotlib
uv tool install --python 3.12 "$GITHUB_WORKSPACE"
uv tool install --python 3.12 "astra-tools==$(grep -oP 'astra-tools==\K[0-9][0-9.]*' pyproject.toml)"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

- name: Install Claude Code + astra plugin
run: |
Expand Down Expand Up @@ -70,6 +88,12 @@ jobs:
rm -f universes/baseline.yaml
astra universe generate -n baseline \
-d 'Default configuration using standard practices'
# No scientific deps are pre-seeded — declaring what the
# recipes import (`uv add`) is part of what the eval measures
# lc materialize refuses a dirty tree — commit the seed so the
# agent starts from a clean one
git add -A
git commit -q -m 'Seed the eval task'

- name: Run Claude Code
id: agent
Expand All @@ -92,11 +116,14 @@ jobs:
working-directory: ${{ env.PROJECT }}
run: |
astra validate astra.yaml
lc status --universe baseline
# Fail unless every materializable output is ok
lc status --json | jq -e \
'[.universes[].outputs[] | select(.status != "alias")]
| length > 0 and all(.status == "ok")'
lc status
# The gate: nothing failed, nothing left to make — and at least
# one output actually got materialized
lc materialize --check --json | jq -e \
'.up_to_date and ((.current | length) > 0)'
# The publication view: declaring a license and materializing
# once more is part of the task
test -f ro-crate-metadata.json

- name: Build report
if: always()
Expand Down Expand Up @@ -136,7 +163,7 @@ jobs:
echo '<details><summary>lc status</summary>'
echo ""
echo '```'
(cd "$PROJECT" && lc status --universe baseline) || echo "unavailable"
(cd "$PROJECT" && lc status) || echo "unavailable"
echo '```'
echo ""
echo '</details>'
Expand Down Expand Up @@ -199,11 +226,12 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: eval-project
# include-hidden-files keeps the .lightcone-manifest.json
# provenance files; the venv and snakemake scratch are excluded
# include-hidden-files keeps the manifests, the run records in
# .git, and the annexed results; the venv and lc's per-run
# machine state are excluded
path: |
${{ env.PROJECT }}
!${{ env.PROJECT }}/.venv/**
!${{ env.PROJECT }}/.snakemake/**
!${{ env.PROJECT }}/.lightcone/**
include-hidden-files: true
if-no-files-found: warn
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ on:
push:
branches: [main]
pull_request:
# `ready_for_review` is not in the default set, and this job is gated
# on `draft == false` — so without it a PR opened as a draft and then
# marked ready never runs at all until someone happens to push again.
types: [opened, synchronize, reopened, ready_for_review]

jobs:
lint:
Expand Down
58 changes: 57 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,42 @@ on:
push:
branches: [main]
pull_request:
# `ready_for_review` is not in the default set, and this job is gated
# on `draft == false` — so without it a PR opened as a draft and then
# marked ready never runs at all until someone happens to push again.
types: [opened, synchronize, reopened, ready_for_review]

jobs:
test:
if: github.event_name == 'push' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
# Both mechanisms are enforced by the same suite, so a failure on one
# OS is exactly the information the other run is there to provide.
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.11", "3.12", "3.13"]
include:
# macOS is where Seatbelt is the mechanism, and the only place
# the generated SBPL is ever executed. One interpreter is
# enough: what differs here is the kernel, not the python.
- os: macos-latest
python-version: "3.13"

env:
# Enforcement tests skip themselves where no mechanism exists. On CI
# that must be a failure instead: a runner that quietly loses
# Landlock or Seatbelt would otherwise report green while testing
# nothing at all.
LC_SANDBOX_TESTS_REQUIRED: "1"
# Same rule for the container smoke tests, on the runners that carry
# the runtimes (ubuntu ships podman and a running docker daemon).
# macOS runners have no podman machine, so there the suite skips.
LC_CONTAINER_TESTS_REQUIRED: ${{ matrix.os == 'ubuntu-latest' && '1' || '' }}
# And for the crate validation: rocrate-validator is in the dev
# group, so every runner has it and none may skip.
LC_CRATE_TESTS_REQUIRED: "1"

steps:
- uses: actions/checkout@v4
Expand All @@ -26,5 +54,33 @@ jobs:
- name: Install dependencies
run: uv sync --group dev

- name: Report the sandbox this runner can build
# Printed before the suite so a failure downstream can be read
# against what this host could actually enforce, and against the
# exec set it resolved. The allowlist resolves off a fixed search
# path, so where a tool lives differs per OS and a missing one is
# otherwise a very confusing "Operation not permitted".
run: |
python -c "
import shutil, tempfile
from pathlib import Path
from lightcone.engine.sandbox import detect
from lightcone.engine.sandbox.policy import (
EXEC_ALLOWLIST, _UTILITY_PATH, exec_policy,
)
c = detect().capability
print(f'mechanism: {c.kind} abi: {c.landlock_abi} {c.detail}')
print(f'utility search path: {_UTILITY_PATH}')
policy = exec_policy(Path(tempfile.mkdtemp()) / 'proj')
granted = set(policy.execute)
for name in EXEC_ALLOWLIST:
found = shutil.which(name, path=_UTILITY_PATH)
if found is None:
print(f' {name:10} not on this host')
elif Path(found).resolve() not in granted:
print(f' {name:10} {found} -> {Path(found).resolve()} NOT GRANTED')
print(f'exec set: {len(policy.execute)} paths, write: {len(policy.write)}')
"

- name: Run tests
run: pytest
Empty file removed .gitmodules
Empty file.
Loading
Loading