calkit / calkit/calkit

Structured default_setup entries so setup inputs can't be forgotten

Open
#1,615 0 comments 0 reactions 0 assignees View on GitHub
enhancement environments
Dominant language
Python
Stars
60
Forks
18
Avg merge
1d 16h
Merged PRs (30d)
38

Description

**From Claude** (Anthropic's Claude Code), prompted by @petebachant while reviewing #1579.

Split out of the first TODO in #1579, which proposed:

```yaml
environments:
compilation:
kind: system
default_setup:
- kind: source
path: scripts/setup.sh
```

## The problem

#1579 ships `default_setup` as `list[str]` alongside a separate `inputs`
list. Nothing ties them together, so this is silently irreproducible:

```yaml
default_setup:
- source scripts/setup.sh
inputs: [] # forgotten
```

`scripts/setup.sh` changes, the stage was built against the old toolchain,
and `calkit status` shows nothing stale because no declared dependency
moved. The failure is silent, which is the worst shape for it.

## Proposal

Widen the field to a union rather than replacing the string form:

```python
default_setup: list[str | SetupSource] | None
```

`{kind: source, path: scripts/setup.sh}` makes the dependency implicit in
the declaration -- you can't say what to source without saying what it
depends on -- so there is no separate `inputs` discipline to forget.
Keeping plain strings matters because `module load cuda` has no input and
shouldn't be forced into an object. `Stage.inputs` is already
`list[str | PathInput | InputsFromStageOutputs]`, so a union matches how
the codebase already handles this.

This is backwards compatible with what #1579 ships, so nothing there has
to change first.

`inputs` would stay for the residue -- a setup command that reads a config
file without sourcing it -- but most projects would stop writing it.

## Not proposed: detecting inputs from the command

#1579 also floated parsing paths out of the setup commands. @petebachant's
own read was that it is "more implicit and potentially could fail," which
matches the comment already in `calkit/pipeline.py`:

> Declared rather than parsed out of the commands, since a shell command
> doesn't reliably say which of its words is a path.

Concretely it breaks on `source $SETUP_DIR/setup.sh` (the path isn't in
the text), `. scripts/setup.sh` (the dot form), and `source ~/.bashrc` (a
real path that correctly isn't a project input). A missed input is
*silent*, so detection that is 95% right reintroduces exactly the bug it
was meant to fix, except now with users trusting it.

The structured form gets the benefit that made auto-detection attractive
(no second list to maintain) without that risk.

Contributor guide

Open the contributing guide

Research direction

Start in calkit/pipeline.py and trace the existing default_setup and Stage.inputs declarations. Check how setup entries are parsed and validated, then verify that both strings and SetupSource entries remain supported and that a source entry carries its path dependency without a separate inputs declaration.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.