lightcone.engine.project¶
What a project is: the convergence engine behind lc init, project
discovery, mode detection, and the one subprocess seam the whole
engine shares.
Source: src/lightcone/engine/project.py (+
engine/templates/ for the scaffold's file content).
Key symbols¶
| Symbol | Role |
|---|---|
converge(dir, *, write) |
The whole scaffold operation. write=False is check mode — the same decision path with side effects off. |
ConvergenceReport |
created / repaired / unchanged / blocked / warnings, plus .converged and .as_dict(). |
current_project() |
The cwd as a project: requires pyproject.toml, uv.lock, .venv. |
declared_project() |
The weaker question — what the repository carries, without .venv. One caller: the worker entry point, which builds the venv a moment later. |
mode(root) |
"direct" or "containerized" — presence of [tool.lightcone.image], nothing else. |
uv_prefix(root, *, sync) |
The one spelling of the project uv hop. Callers differ only in sync: a probe converges the environment, a recipe must not. |
project_name(dir) |
PEP 503-ish name from the directory name. |
_run / _check_call |
Every external tool invocation, and the suite's one monkeypatch point. |
ProjectError |
The engine's one exception; the CLI translates it once. |
What must stay true¶
- Everything routes through the converger. Every scaffold item
goes through
_Converger.item/.file/.blocked; nothing writes or records outside that mechanism..filetakes a thunk, so check mode renders no template at all. - Derived artifacts converge by correctness, not existence.
uv.lockand.venvare probed with uv's own no-write checks (uv lock --check,uv sync --locked --exact --check); drift reports asrepaired. Check mode may probe but never mutates — pinned bytest_check_mode_only_probes. - A warning is advisory; a blocked item counts. Convergence never
claims a project is converged while something it owns is absent or
unfixable — and repairs only ever append (
.gitignore/.gitattributesare converged entry-wise, order judged against the template). - Only what git can carry is converged. No
src/, no empty directories — a clone must need nothing but.venv,git annex initand the annex filter (all three local state git does not clone), andtest_a_clone_of_a_converged_project_is_convergedpins it. - The annex filter is one config key.
filter.annex.required=true, always, so agit addthat cannot reach git-annex refuses instead of silently staging raw bytes. Nothing else about how git dispatches git-annex is lc's to write: the filter drivers and hooks stay exactly asgit annex initleft them, resolved fromPATH. - There is no discovery. The invoked directory is the project or
it is a clean error; every uv call carries an explicit
--project. - Templates are files (
templates/files/*.tmpl,string.Templatewith strict substitution), and a template gets a function only when there is a value to decide or a merge policy to hold.
Tests¶
tests/test_project.py (semantics, against the stubbed _run),
tests/test_templates.py (content, substitution, repair logic),
tests/test_cli.py (the lc init surface).