Skip to content

feat(slurm): local-disk tiering for confined targets - #10

Merged
ligon merged 4 commits into
mainfrom
feat/local-tier-confined
Sep 9, 2026
Merged

ligon merged 4 commits into
mainfrom
feat/local-tier-confined

Conversation

@ligon

@ligon ligon commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Step (2) of docs/local-disk-tiering.org, for confined (sbatch) targets. Unconfined (salloc) targets keep the legacy all-on-/local layout for now (README says so); that is the next PR.

What it does. With slurm.local_disk (true = /local, or a path) on a confined target, the batch body runs a staged prepare script (sucoder/local_tier.py) that clones the shared mirror to <root>/job$SLURM_JOB_ID/mirrors/<name>, installs a post-commit hook publishing every commit back to the shared mirror (never forced), restores the last WIP snapshot when its parent is the branch tip, and starts the agent in the clone with UV_CACHE_DIR/PIP_CACHE_DIR/npm_config_cache/TMPDIR on local disk. The deadline timer snapshots the clone (runtime path via a new snapshot_dir_shell). The mirror root stays shared: laptop push/pull, prelude/batch staging, and scaffolding are untouched. SLURM's epilog cleans up.

Config. slurm.local_disk now accepts true/false too (the README already showed true; the parser rejected it). RemoteExecutor.local_disk_root carries the resolved choice for confined targets.

Smoke test on n0036.savio4 under the batch job's environment: prepare 4.7 s; a commit in the clone landed on the shared mirror via the hook without moving its checkout; the dirty-mirror warning fired correctly; the timer put refs/sucoder/wip/<name> on the shared mirror with the dirty files. A fresh sbatch launch cannot be exercised from inside a job — first real run is sucoder -T carleton-htc collaborate SuCoder with local_disk: true in the target.

Tests. tests/test_local_tier.py drives the prepare script under bash against a real temporary shared mirror (7 scenarios); batch-script, confined-launch, config, timer, and executor-kwargs tests added. 707 passed CI-style; mypy unchanged.

Impact analysis by hand (gitnexus cannot load here): _build_batch_script <- _launch_confined (+ tests); build_timer_script <- _start_slurm_timer, _launch_confined; RemoteExecutor <- _build_executor.

🤖 Generated with Claude Code

https://claude.ai/code/session_01StcQgXQDE4F6eJer1sEXRb

Sue the Coder and others added 4 commits September 9, 2026 11:29
Step (2) of docs/local-disk-tiering.org, for confined (sbatch) targets.
With slurm.local_disk set (true = /local, or a path), the batch body runs
a staged prepare script that clones the shared mirror to
<root>/job$SLURM_JOB_ID/mirrors/<token>, installs a post-commit hook that
publishes every commit back to the shared mirror (never forced; a push
refused because the laptop pushed first is reported for the agent to
git pull --ff-only), restores the last WIP snapshot when its parent is
the branch tip and the tree is clean, and starts the agent in the clone
with UV_CACHE_DIR / PIP_CACHE_DIR / npm_config_cache / TMPDIR under
<root>/job$SLURM_JOB_ID.  The deadline timer snapshots the clone (runtime
path, via a new snapshot_dir_shell parameter) instead of the shared
mirror.  The mirror root stays on the shared filesystem throughout: it is
the laptop's push/pull target and the staging area for prelude and batch
script, so _sync_remote / _pull_from_remote / scaffolding are untouched.
Slurm's epilog wipes <root>/job<ID>; nothing is orphaned and --node
pinning is unnecessary.

New module sucoder/local_tier.py: path helpers with literal and runtime
(${SLURM_JOB_ID}) flavours so the batch body, timer, and prepare script
agree on paths without passing them around; the prepare script is
executed, not sourced, so its early exits cannot take the batch body with
it.  It warns loudly when the shared mirror has uncommitted tracked
changes (updateInstead would refuse every publish), when the clone and
mirror have diverged, and when a foreign post-commit hook is left alone.

Config: slurm.local_disk now also accepts true/false (README already
showed local_disk: true, which the parser rejected).  RemoteExecutor
gains local_disk_root; cli._build_executor sets it for confined targets
(config or --local-disk override) while keeping remote_mirror_root
shared.  Unconfined (salloc) targets keep the legacy all-on-/local layout
for now; README says so.

Smoke-tested on n0036.savio4 (job 38661192) under the batch job's
environment: prepare 4.7s (4 MB .git), a commit in the clone published to
the shared mirror by the hook without moving the mirror's checkout, the
dirty-mirror warning fired (the mirror held these very edits), and the
timer with a 1-minute cadence put refs/sucoder/wip/SuCoder on the shared
mirror with the dirty files and parent = the clone's HEAD.  A fresh
sbatch launch could not be exercised from inside a job.

Tests: tests/test_local_tier.py drives the prepare script under bash
against a real temporary shared mirror (fresh clone + hook publishes;
idempotent re-run keeps dirty files and fast-forwards; WIP restored only
onto its parent, kept afterwards; foreign hook left alone; dirty mirror
warns; missing/detached mirror fails loudly).  Batch-script, confined
launch (three staged files before sbatch, runtime snapshot path), config,
timer, and executor-kwargs tests added.  707 passed CI-style
(GITHUB_ACTIONS=true, lock-matched versions); mypy unchanged (new module
clean).

Impact analysis by hand (gitnexus cannot load here, libssl.so.3):
_build_batch_script <- _launch_confined + tests/test_batch_script.py;
build_timer_script <- cli._start_slurm_timer, mirror._launch_confined;
_parse_slurm_config <- _parse_targets; RemoteExecutor <- cli._build_executor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StcQgXQDE4F6eJer1sEXRb
…oc example indent

- _launch_confined waits up to 100 x 3s (was 20 x 3s) for the tmux session
  when tiering is on: the batch body now clones the mirror before
  new-session, and a fat mirror on slow Lustre can take minutes.
- The prepare script refuses to `rm -rf` a work path that is not of the
  form <root>/job<ID>/mirrors/<token>, documenting the invariant at the
  only destructive step.
- docs/persistent-presence.org example had system_prompt_extra indented
  under slurm:, the exact misplacement config.py warns about.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StcQgXQDE4F6eJer1sEXRb
The global --local-disk/--no-local-disk flag predates tiering; pin that
on a confined target with no slurm.local_disk in config it now resolves
to the default /local root while the mirror root stays shared.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StcQgXQDE4F6eJer1sEXRb
A global option beside --local-disk: picks the node-local root instead
of slurm.local_disk's path or /local.  Giving a root implies
--local-disk; combining it with --no-local-disk is rejected as a
BadParameter.  Precedence: --local-disk-root > slurm.local_disk > /local.
The value is normalised (trailing slash stripped) and travels on
ctx.obj["local_disk_root"]; _build_executor now also falls back to the
context for both overrides, so callers that hand over only cli_ctx (e.g.
`list models`) see them too.

Tests: root alone enables tiering with that root; CLI root beats the
config root; --no-local-disk + --local-disk-root exits non-zero with the
hint; the parsed value reaches _build_executor's context.  712 passed
CI-style; mypy unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01StcQgXQDE4F6eJer1sEXRb
@ligon
ligon merged commit 2b8ad99 into main Sep 9, 2026
4 checks passed
@ligon
ligon deleted the feat/local-tier-confined branch September 12, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant