# Lanes

> Start, restart, stop, and check loop lanes as named background Claude Code sessions seeded from canonical prompt files — the scripted replacement for the manual morning refresh (cancel loop, clear, re-paste the canonical prompt) across N lanes on a machine. `start`/`restart` first pull the repo and refresh the plugin marketplace, then launch each configured lane with its per-lane model/effort. Us…

- **Type:** Skill
- **Install:** `agentstack add skill-melodic-software-claude-code-plugins-lanes`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [melodic-software](https://agentstack.voostack.com/s/melodic-software)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [melodic-software](https://github.com/melodic-software)
- **Source:** https://github.com/melodic-software/claude-code-plugins/tree/main/plugins/claude-ops/skills/lanes

## Install

```sh
agentstack add skill-melodic-software-claude-code-plugins-lanes
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

## Pre-computed context

claude CLI: !`command -v claude >/dev/null 2>&1 && echo "present ($(claude --version 2>/dev/null))" || echo "MISSING (required)"`
jq: !`command -v jq >/dev/null 2>&1 && echo "present" || echo "MISSING (required)"`
Repo root: !`git rev-parse --show-toplevel 2>/dev/null || echo "unknown (pass --repo)"`
Lane config: !`c="${CLAUDE_OPS_LANES_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.work/lanes.json}"; [[ -f "$c" ]] && echo "$c ($(jq -r '(.lanes//[])|length' "$c" 2>/dev/null) lanes)" || echo "absent ($c) — author one (see context/config.md)"`

## Variables

Arguments: `$ARGUMENTS`

## Purpose

Running N loop lanes on a machine means a daily ritual: for each lane, cancel its
loop, clear, and re-paste its canonical prompt. This skill collapses that to one
command. `start`/`restart` pull the repo and refresh the plugin marketplace once,
then launch each configured lane as a **named background session** seeded from the
lane's canonical prompt file, mirroring that lane's model/effort onto the launch.
`status`/`stop` read and manage those sessions through the CLI's own
background-session surface.

**Owns only its own lanes.** `stop`/`restart` act on a session **only** when its
name is a lane in the resolved config — a hand-started session (e.g. an interactive
`work` window, or an unrelated `PR Babysit`) is never stopped by this skill.

## A relaunch is the only context reset a loop lane gets

`/loop` re-invokes its prompt in the **same** session (it self-paces via
`ScheduleWakeup`), so a lane's context carries forward across every cycle, subagent
return, and operator turn — the loop never starts a fresh one. A running loop
**cannot** reset its own context on demand: a built-in like `/clear` issued from a
loop re-run reaches the model as plain text, not an executed command, so the model
cannot `/clear` itself. Any "restart the loop when context passes ~N%" discipline is
therefore an admonition with no in-session enforcement — the lane has no reliable way
to measure its own context usage and no way to act on the threshold if it could.

The context change that *does* happen automatically in-session is Claude Code's
**auto-compaction**: when the conversation nears the model's input limit, older
history is summarized in place to free space. That is not a reset — the session
continues on a lossy summary of what came before, not a fresh context — so a lane that
runs long enough will lose earlier context to compaction well before any operator
relaunch, not keep every turn until then.

`restart` (stop the session, relaunch from the canonical prompt) is the only
*fresh-session* reset: the relaunched lane starts a **fresh** session seeded from the
prompt file, carrying none of the prior conversation. That reset is **operator- or
launcher-initiated**, not automatic — there is no per-cycle or threshold trigger that
runs `restart` for you today. Auto-compaction can still fire between restarts, but it
only summarizes; it does not clear the accumulated, increasingly degraded context.
Until an automatic relaunch trigger exists, treat the periodic `restart` (e.g. the
morning refresh) as the mechanism that keeps long-running lanes off a stale,
repeatedly-compacted context, and do not let a lane prompt assume each `/loop` cycle
begins with fresh context.

## Run it

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/lanes/scripts/lane-launcher.sh" --data-dir "${CLAUDE_PLUGIN_DATA}" $ARGUMENTS
```

Print the script's output verbatim — it is the deliverable. Preview any mutating
run first with `--dry-run` (prints the exact `claude`/`git` commands, seeds
nothing, kills nothing).

**`--data-dir` is passed explicitly, not left to the script's own
`$CLAUDE_PLUGIN_DATA` env-var fallback.** Per
[plugins-reference](https://code.claude.com/docs/en/plugins-reference#environment-variables),
`${CLAUDE_PLUGIN_DATA}` is exported as a real environment variable only to hook
processes and MCP/LSP subprocesses — for skill content it instead resolves by
**inline text substitution anywhere the placeholder appears** in the rendered
skill body, exactly like `${CLAUDE_PLUGIN_ROOT}` above. A script this skill
shells out to via the Bash tool does **not** inherit `CLAUDE_PLUGIN_DATA` as an
env var, so leaving `--data-dir` off here would silently fall through to
`lane-launcher.sh`'s own `~/.claude/plugins/data/claude-ops` guess instead of
the marketplace-qualified directory Claude Code actually resolves. `$ARGUMENTS`
comes after `--data-dir`, so an explicit `--data-dir` the caller passes in
`$ARGUMENTS` still wins (last flag wins in `lane-launcher.sh`'s parser).

## Action Router

Parse `$ARGUMENTS` for the action (first token); remaining tokens are lane names
(targets for `restart`/`stop`; an unknown name is rejected).

| Action | Mutates | Description |
|---|---|---|
| `start` (default) | Yes | Pull + marketplace update, then launch every configured lane **not already running** |
| `restart [lane...]` | Yes | Pull + marketplace update, then stop-and-relaunch each target lane (all, or named) |
| `status` | No | Per-lane table: model, effort, running/stopped, and the live sessionId |
| `stop [lane...]` | Yes | Stop each running target lane (all, or named) via `claude stop ` |

Options: `--config FILE`, `--repo DIR`, `--no-pull`, `--no-update`, `--dry-run`,
`--agents-json FILE` (read the session list from a file instead of the live CLI —
offline/scripted reuse), `--data-dir DIR` (base dir for the per-lane
launch-commit marker; default `$CLAUDE_PLUGIN_DATA`). Exit codes: `0` ok · `3`
bad argument/config · `4` prerequisite missing or repo/config unresolved.

## Lane config

Lanes are defined in a JSON config, resolved first-hit-wins:
`--config FILE` → `$CLAUDE_OPS_LANES_CONFIG` → `/.work/lanes.json`. Each lane
carries a `name`, a `prompt` file path, and optional `model`/`effort`/`settings`
(a session-only `claude --settings` override — e.g. opting the lane into the
`autonomy` plugin's lane-stop gate). The full
schema, resolution rules, and the prompt-storage seam live in
[context/config.md](context/config.md) — read it before authoring a config.

**Prompt storage is provisional (composes with #480).** Today prompt files live in
a session-local `.work` dir (`prompt_dir`, default `.work`). Issue #480 (loop-prompt
authoring skill) is slated to own durable prompt storage. When it lands, repoint
`prompt_dir` at that home; the launcher resolves the prompt dir in exactly one place
(`resolve_prompt_dir` in the script), which is the single seam to update.

## Mid-session staleness & restart cadence

A **running** lane keeps the skill versions it loaded at launch: a fix merged to a
plugin the lane runs does **not** reach that lane mid-session. This is not a missing
feature to build around — it is verified Claude Code behavior (a live session keeps
its launch-time plugin versions, `/loop` never re-reads a skill's body on later
cycles, and a loop can't self-trigger `/reload-plugins`). Restart is the honest
refresh mechanism — the same `restart` that clears context bloat (#496). Detect an
unconsumed self-fix with a read-only git probe against the repo's default branch,
then restart that lane at its next cycle boundary. The probe reads the launch
commit `lane-launcher.sh` records per lane at `start`/`restart`
(`${CLAUDE_PLUGIN_DATA}/lanes//-launch-commit`, #792 — the data
directory is plugin-wide, so ``, a digest of the repo's canonical
path, keeps a conventional `work` lane in two different checkouts from sharing
one marker) — no manual fill-in needed.
Full reasoning, the probe, and the cadence live in
[context/refresh.md](context/refresh.md) — read it before answering "why is my
merged fix not live in the lane?" or setting a restart frequency.

**Carry this line into that probe** — it is the `data_dir` assignment
`context/refresh.md` deliberately leaves unresolved, because only skill content
(this file) substitutes the placeholder:

```bash
data_dir="${CLAUDE_PLUGIN_DATA}"
```

Copy it as it renders **here**, already substituted to an absolute path. Writing
the placeholder — or a `${CLAUDE_PLUGIN_DATA:-…}` env fallback — inside
`context/refresh.md` would not work: that file is read raw, and per
[plugins-reference](https://code.claude.com/docs/en/plugins-reference#environment-variables)
`CLAUDE_PLUGIN_DATA` reaches only hook and MCP/LSP subprocesses as a real
environment variable, never a script the Bash tool runs. The probe would then
read the unqualified `~/.claude/plugins/data/claude-ops` guess, find no marker,
and skip the staleness check silently.

## Verified CLI surface

The launcher shells out only to primitives confirmed on this machine's `claude`
(`--help` / real invocation): `claude --bg -n  [--model M] [--effort E]
[--settings JSON] ""` (launch a named background session, return
immediately; `--settings` accepts inline JSON and applies session-only, per the
CLI reference),
`claude agents --json` (list active sessions: pid, cwd, kind, startedAt,
sessionId, name, status),
`claude stop ` (stop one session; conversation kept, resumable with
`claude attach`), and `claude plugin marketplace update`. There is no
`claude agents stop` verb — stop resolves the sessionId from `agents --json` and
only for a configured lane name.

## Gotchas

- **No durable prompt home yet.** `.work` is session-local; a fresh machine/session
  has no prompts until they are authored there (or `prompt_dir` is pointed at a
  committed dir). This is the #480 dependency, not a bug.
- **Name is the identity.** Lanes are matched by session `name` **and** `kind:
  background` — every lane is launched with `--bg`, so an interactive window sharing
  a lane name is never matched or stopped. Two lanes must not share a name; a
  hand-started *background* session sharing a lane name would still be treated as
  that lane, so keep lane names distinct from ad-hoc background session names.
- **`start` is idempotent-ish, `restart` is not.** `start` skips a lane already
  running; `restart` always stops-and-relaunches (discarding the running lane's
  in-flight conversation). Use `start` for "bring up whatever is down".
- **A missing/empty prompt file skips that lane** (with an error) rather than
  launching an empty session. `status` flags `[prompt MISSING]`.
- **The launch-commit marker is per-machine and best-effort.** It lives under
  `${CLAUDE_PLUGIN_DATA}` (a per-machine dir, not synced), so a lane restarted
  on a different machine has no marker there yet. A write failure only warns —
  it never fails an already-launched (or already-stopped-and-relaunched) lane —
  so a missing marker means "never started here via `lane-launcher.sh`", not
  "launcher broken". A (re)start that *cannot* record its commit also deletes
  any marker the previous launch left, so "missing" always beats a stale commit
  the probe would otherwise trust.
- **A lane name must be a single path component.** It is the marker's filename,
  so config preflight exits `3` on a name containing `/` or `\`, or equal to `.`
  or `..` — otherwise two distinct lanes could share one marker.

## Per-cycle deterministic scripts (#538)

Two lane-cycle mechanics need no reasoning, so they are scripted here and a lane
prompt references the script instead of re-deriving the work every cycle. Both
follow `lane-launcher.sh`'s conventions (jq CRLF wrapper, the `--help` header as
the full contract, explicit exit codes). The header is the source of truth for
each — the summary below is a pointer, not a copy.

- **`scripts/machine-behavior.sh`** — emits the lane's MACHINE-BEHAVIOR block (gh
  identity, clone path, worktree inventory, installed plugin versions) as a
  verbatim-printable text block. Pure environment inspection: it emits only
  mechanically unambiguous facts and deliberately does NOT compute "deviations
  from standing rules" — that stays a model judgment made by reading these facts
  against the prose rules. Plugin versions are the INSTALLED runtime versions,
  which per [context/refresh.md](context/refresh.md) can lag repo HEAD mid-session
  (the honest number for a running lane). `--plugin ` (repeatable) scopes the
  block to the plugins a lane runs.

- **`scripts/telemetry-upsert.sh`** — maintains exactly ONE marker-identified
  telemetry comment on a tracking issue, editing it in place instead of posting a
  second (the interim home of the #502 telemetry contract). Given `--issue N
  --marker STR --body-file PATH`, it writes a machine-detectable sentinel `` as the comment's first line, finds
  that sentinel across ALL comments (paginated — a match on any page prevents a
  duplicate) and PATCHes it; failing that (first migration off a hand-authored
  comment) it adopts the most recent comment BY THE AUTHENTICATED USER carrying the
  raw marker text; else it creates one. `STR` is `[A-Za-z0-9:._-]+` (so it can
  never close the HTML comment early), and one writer identity owns a given marker.
  Body input: prefer `--body-file -` (stdin) for a body generated in memory (e.g.
  piped from `machine-behavior.sh`); a real `--body-file PATH` must resolve under
  `--body-dir` (default `$CLAUDE_PLUGIN_DATA`), may not be a symlink, and is capped
  at 64 KiB — a prompt-driven script must not be coaxed into posting an arbitrary
  file (a secret, a token store) as a public comment.

## Cross-references

- [context/refresh.md](context/refresh.md) — why a running lane can't hot-reload a
  merged fix to its own skills, the git staleness probe, and the restart cadence
  (#514).
- `/claude-ops:plugins` — the authoritative, richer plugin-fleet sync (scope
  divergence, new-catalog installs). This skill's marketplace refresh is the light
  `claude plugin marketplace update` step of a launch, not a substitute.
- `/claude-ops:morning-brief` — reads the loop-lane **telemetry** (per-lane
  last-cycle freshness). This skill starts/stops the lanes that emit it.
- #480 (loop-prompt authoring skill) — forward dependency that will own durable
  prompt storage. #496 (context economy / restart discipline) — why lanes get
  restarted. #502 (telemetry) — the per-lane telemetry the running lanes feed.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [melodic-software](https://github.com/melodic-software)
- **Source:** [melodic-software/claude-code-plugins](https://github.com/melodic-software/claude-code-plugins)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-melodic-software-claude-code-plugins-lanes
- Seller: https://agentstack.voostack.com/s/melodic-software
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
