# Clodex

> Use when any development work starts in a repository — a feature request, a dense brief, a bug fix, "continue where we left off", or an ask whose shape is unclear. Also use when a repo has a .clodex/ directory with an unfinished run, and before invoking clodex-plan, clodex-build, clodex-verify, or clodex-ship.

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

## Install

```sh
agentstack add skill-jdpalumbo2-claude-skills-clodex
```

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

## About

# clodex — the front door

## Overview

One entry point for all work, so nobody has to remember stage names. This skill
runs five things in order and then hands off:

1. **Preflight** — prove the environment before a stage burns tokens on it.
2. **Open-run detection** — a run already in flight is resumed, never
   overwritten.
3. **Profile** — load `.clodex/profile.json`, or interview once and write it.
4. **Lane classification** — feature-shaped work enters the core path; every
   other shape is named and handed back, because those lanes are v0.2.
5. **Change boundary** — record what was already dirty, and never let the run
   commit someone else's work.

Then it opens the run and hands to a stage skill.

This skill does not edit code, does not call Codex, and does not run release
actions. The stage skills own all three.

## Paths and commands

```bash
CLODEX_HOME="${CLODEX_HOME:-$HOME/.claude/skills/clodex}"  # the dir holding this SKILL.md
STATE="$CLODEX_HOME/state/clodex_state.py"
REPO="$(git rev-parse --show-toplevel)"
cd "$REPO"    # do this, do not just assume it
REMOTE="$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null | cut -d/ -f1 || true)"
[ -n "$REMOTE" ] || REMOTE="$(git remote | head -1)"   # empty = this repo has no remote
```

Shell variables do not survive between separate command invocations, so
**re-establish this block at the top of any shell you run these procedures in**.
Later sections use `$REPO`, `$STATE`, and `$REMOTE` as if it were already there.

**Every command below runs from `$REPO`.** `git check-ignore`, the repo
inspection in §3, and `git add` all resolve relative paths against the current
directory, so from a subdirectory they answer the wrong question — a correct
`.gitignore` reads as missing. If a step cannot `cd`, use `git -C "$REPO" …`.

| Thing | Path | Committed? |
|---|---|---|
| Run-state engine | `$STATE` | catalogue |
| Profile schema | `$CLODEX_HOME/profile.schema.json` | catalogue |
| Codex runner (stages only, never here) | `$CLODEX_HOME/runner/run-codex.sh` | catalogue |
| Repo profile | `$REPO/.clodex/profile.json` | **yes** |
| Run directory | `$REPO/.clodex//` (`$RUN_DIR` below) | **no — gitignored** |

A run's state is its directory: `events.ndjson` is an append-only event log and
is **the** record of what happened; `run.json` is a snapshot derived from it by
replaying the log; `lock.json` says which session owns the run; `write.lock`
serializes writes and is not something you reason about. You add to a run only
by appending an event.

Engine commands (payloads travel on **stdin**, never argv):

```bash
python3 "$STATE" status  "$RUN_DIR"          # human summary, incl. the lock line
python3 "$STATE" rebuild "$RUN_DIR"          # full snapshot JSON
python3 "$STATE" append  "$RUN_DIR" /plan-reviewer/.envelope.json",
             "input_hashes": [""],
             "duration_s": 501, "resumed": false}}
  ```
  `invocation_id` and `role` are required — a record nothing can be matched to
  would read as evidence that a round happened. Everything comes off the
  envelope the runner already wrote; `input_hashes` is what binds a review to
  the plan version it actually read.

  **A leg, not an invocation.** A resumed round overwrites its own envelope,
  which then reports only the resume — the pilot's envelopes under-reported the
  review by 25% that way. So append one `codex` block for the interrupted leg
  (`"status": "interrupted"`) and another for the resume (`"resumed": true`),
  both with the same `invocation_id`. The legs sum to what the round cost.

Attach a `codex` block to the first event that invocation causes — the finding
it raised, the `batch:reviewed` it produced, the `plan:amended` it forced. A
round that found nothing still gets one: put it on the event its clean result
unlocked (`plan:approved`, `batch:reviewed`), or the log cannot tell "reviewed
clean" from "never reviewed".

**Copied, never estimated.** `duration_s` is the envelope's `exit.duration_ms`
over 1000, and `status` is the envelope's `status` — never a number you
remember, never a state you assert. Estimated durations ran 1.1–1.8× actuals
the first weekend this was tested at scale, and two logs contradicted their
envelopes' status outright. And because the attach discipline itself fails
under load — seven completed invocations went unrecorded that same weekend —
every stage reconciles at its exit step:

```bash
python3 "$STATE" telemetry-sync "$RUN_DIR" "$REPO/.clodex/runner"
```

Exit 1 means it printed one ready-to-attach `codex` block per orphaned
envelope, every field copied from disk. Attach each block to an event that
stage still appends — any event carries one `codex` block. The diff is
recomputable, so a block with no carrier left is not hand-carried in prose: it
surfaces again at the next stage's reconcile, whose own appends can carry it.

`finding:recorded` additionally takes `severity`, `summary`, `location`,
`detail`, `recommendation`, `round`, `invocation` and `plan_hash` — `location`
and `detail` copied verbatim from the envelope's finding, so the manifest can
answer *where* and *why* without anyone opening envelopes — and
`finding:disposed`'s `note` is promoted into the snapshot beside them. See
`clodex-plan` §9.

### Long rounds: `--detach`, and how to watch one

A review or implementer round can outlive a harness tool timeout. Do not
babysit it in the foreground and do not hand-roll nohup — the runner does it:

```bash
bash "$RUNNER" --role implementer --repo "$REPO" \
     --run-id "$(basename "$RUN_DIR")" --prompt-file "$PROMPT" --detach
# -> detached  pid  log 
```

Watch **both** signals, because either alone lies: the pid (gone means the
run ended, says nothing about how) and the runner log's final status line —
`grep -E '^(complete|partial|interrupted|failed) ' ` — which also names
the envelope. Heartbeats stream into the same log, so a stalled run and a
slow one are distinguishable mid-flight. When the pid is gone, map the status
the way the stage skills' rc tables do. `--resume ` works
alone: the runner recorded the prompt path in the invocation's meta.

Stage skills always pass `--run-id "$(basename "$RUN_DIR")"`. It keys the
runner state by run (`.clodex/runner///…`), so two runs in one
repo never interleave their envelopes.

---

## 1. Preflight — before any stage runs

Run every check. A failed check stops here; do not "proceed and see." Report the
results in chat **and carry them into the run**: the `run:opened` event in §6
takes a `preflight` field holding every check's verdict, so "was this
environment ever verified?" is answerable from the manifest instead of from a
transcript. Preflight that runs again — on a resume, or after the user fixes a
failed check — rides on whatever event you append next, and appends a second
record rather than replacing the first.

A run that is resumed rather than opened has no `run:opened` to carry it: put
the `preflight` field on the `stage:*:entered` event the stage skill appends,
which is the first event of the resumed session.

**When a check fails**, the invocation does not end: name the check, say exactly
what would fix it, and wait for the user. When they say it is fixed, **resume
from that check** — re-run it and continue down the list. Do not silently re-run
the checks that already passed, and do not skip the ones after it.

**Order on a first run:** checks 4 and 6 read the profile, which does not exist
yet. Do checks 1–3 and 5, run the interview (§3), then come back and finish 4
and 6. A first run also has no `.clodex/` directory, so §2 finds nothing and
costs one `ls`.

1. **Repo root.** `git rev-parse --show-toplevel`. Not inside a work tree → stop
   and ask where the work lives. Also note the branch: `git rev-parse
   --abbrev-ref HEAD`.
2. **Remote state.** `$REMOTE` comes from the preamble — the remote is not
   always named `origin`, and halting a workflow over a hardcoded name is a
   self-inflicted outage:
   ```bash
   if [ -n "$REMOTE" ]; then
     git ls-remote --exit-code "$REMOTE" HEAD >/dev/null
   else
     echo "no remote configured"
   fi
   git status -sb | head -1                       # ahead/behind
   ```
   `ls-remote` proves the remote is reachable *and* that credentials work.
   Report divergence now — a repo behind its remote gets resolved before
   planning, never at ship. "No remote configured" is a **pass**, not a failure:
   say so out loud, because ship will have no push step.
3. **`.clodex/` ignore rule.** Run state must never be committed; the profile
   must be:
   ```bash
   git check-ignore -q .clodex/ANY-RUN-ID/events.ndjson  # expect exit 0 (ignored)
   git check-ignore -q .clodex/profile.json              # expect exit 1 (NOT ignored)
   ```
   `check-ignore` tests the path against the ignore rules, so neither path has to
   exist and `ANY-RUN-ID` is a stand-in, not a reserved name. Exit 1 on the
   second probe is the **pass**, not an error — run them as two separate commands
   so a shell with `set -e` cannot swallow the result.
   Either result wrong → the remedy is the **nested ignore file**,
   `.clodex/.gitignore`, self-contained inside the directory it governs so no
   other session's edit to the shared `.gitignore` can collide with it (that
   collision once nearly swept 56 events into someone else's commit):
   ```
   *
   !.gitignore
   !profile.json
   !claims.json
   ```
   The `!.gitignore` line lets the file exempt itself; without it the remedy
   ignores its own carrier. `claims.json` is the shared-claims ledger (check
   8) — committed state like the profile, so it gets the same negation (for a
   root-style remedy: `!.clodex/claims.json`). Show the user, write it after they agree, and make
   sure it gets **committed** (§3 step 4 commits it beside the profile) — an
   uncommitted nested file does not exist in a fresh worktree, which is exactly
   where run state most needs ignoring. Root-`.gitignore` lines (`.clodex/*` +
   `!.clodex/profile.json`) remain a legal remedy where a repo already has
   them; do not migrate a working one.

   **Second ignore mechanisms replace `.gitignore` — probe every one present.**
   `.gcloudignore`, `.dockerignore`, `.vercelignore`, `.npmignore`: each makes
   its uploader ignore *its* list instead of git's, so a repo whose git ignore
   is perfect can still ship run state — sixteen worktrees of clodex logs and
   evidence screenshots once entered a production Cloud Build upload exactly
   this way. For each such file that exists:
   ```bash
   for f in .gcloudignore .dockerignore .vercelignore .npmignore; do
     [ -f "$f" ] && { grep -qE '(^|/)\.clodex(/|$)|^\.clodex\b' "$f" \
       || echo "$f does not exclude .clodex/ — run state will ride its uploads"; }
   done
   ```
   A miss is handled the way this check handles `.gitignore`: show the exact
   lines to add (`.clodex/` — plus `worktrees/` when lanes live under the
   repo), and write them only after the user agrees.
4. **Runtimes.** For each entry in the profile's `runtimes`: `command -v
   `, plus the version check when `min_version` is set. Missing runtime
   → stop; it fails later and more expensively inside a stage. An empty list is
   a legal answer (this repo pins no runtimes); a *missing* `runtimes` key is a
   profile that never answered the question — go fix it in §3.

   **In a worktree, a missing prerequisite gets an offer — never an auto-run,
   never a shrug.** The profile's `commands.install` is required by the
   schema, filled at the interview, and executed by nothing — while every
   lane rediscovers the same folklore (symlinked env files, a venv built from
   the parent checkout) by hand. When this check, or a dry probe of the test
   command, shows dependencies, a venv, or an env file absent in a worktree:
   **offer** to run `commands.install`, and offer to materialize the env
   files the main checkout carries (symlink or copy; the user names which
   files, and their contents are never read or printed). Run either only
   after the user says yes. Preflight's charter stays proof, not
   construction — the offer is the construction path, and it is theirs to
   take.
5. **Codex auth.** `command -v codex`, then `codex login status` (expect exit 0
   and a logged-in line). Codex is not optional: plan review is default-on and
   build delegates to it. Not logged in → stop and ask the user to run
   `codex login`.
6. **Credentials.** For each name in the profile's `required_env`:
   ```bash
   printenv "$NAME" >/dev/null || echo "missing credential: $NAME"
   ```
   Names only. Never print, echo, log, or write a credential value.
7. **Bootstrap (worktree lanes only).** When this checkout is a linked
   worktree — `git rev-parse --git-dir` differs from `git rev-parse
   --git-common-dir` — first-run setup must already be **committed**, never
   re-created here:
   ```bash
   git ls-files --error-unmatch .clodex/profile.json   # expect exit 0 (tracked)
   ```
   Tracked → record `{"name": "bootstrap", "status": "pass"}` among the
   preflight checks. Not tracked → **stop; a lane never interviews.** Two
   lanes once interviewed independently, six minutes apart, produced
   contradictory profiles, and merge order silently picked the winner — every
   later lane inherited answers nobody chose. The fix is the bootstrap ritual
   (§3), run once from the main checkout on the default branch before lanes
   fork; tell the user that, and wait. In the main checkout this check is a
   no-op — §3 handles the first run there.
8. **Claims (when `.clodex/claims.json` exists).** The shared-claims ledger:
   collision-prone resources — migration numbers, ports, workflow ids,
   property names — claimed for the repo's concurrent lanes. **Orchestrator-
   owned: only the orchestrator writes or commits it; lanes read.** Shape:
   ```json
   {"claims": [{"resource": "migration-008", "holder": "lane-C",
                "note": "room-liveness schema"}]}
   ```
   Check every resource this run will need — the brief usually names them,
   and the plan's `Claims:` line (clodex-plan §5) re-checks at plan time:
   ```bash
   python3 - "$REPO/.clodex/claims.json" ...  -- ` until
   it appears — no new event, no shared mutable state, and the poll answers
   from committed truth rather than from another lane's promises.

---

## 2. Is a run already open?

```bash
ls -1d "$REPO"/.clodex/r-*/ 2>/dev/null
python3 "$STATE" status "$REPO/.clodex/"
```

A run is **open** when `status` shows a `stage:` other than `closed`.

`stage: -` is not an open run — it is a directory that never got its
`run:opened` event, left by a session that died in the window §6 opens between
`mkdir` and the first append. It has no brief, lane, or start commit, so there
is nothing to resume. Close it out and open a new run:
`echo '{"e":"run:closed"}' | python3 "$STATE" append "$RUN_DIR"` (legal on an
empty log; it yields `stage: closed`).

**One open run per *checkout*.** A linked worktree is its own checkout with
its own `.clodex/`, which is what lets parallel lanes each carry a run — a
documented feature, not an accident of `--show-toplevel`. Within one checkout:
if you find two open runs, the older is the one whose run id sorts first —
the ids are `r--`, so plain lexicographic order is
chronological. Resume it, or close it with the same `run:closed` append,
before opening anything new. The **repo-wide** picture — every run in the
main checkout and every worktree — is the index, not a rule:

```bash
python3 "$STATE" runs ""
```

One line per run: path, stage, open findings, release state. It replaces the
shell loop every orchestrator otherwise hand-rolls over `worktrees/*/`.

`status` also prints a `lock:` line when `lock.json` exists. That line decides
what you may do:

| `status` shows | What it means | What to do |
|---|---|---|
| no `lock:` line | no write is in flight and no writer died mid-write | Offer resume (below); an `append` will be accepted. This is the *normal* state even while

…

## Source & license

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

- **Author:** [jdpalumbo2](https://github.com/jdpalumbo2)
- **Source:** [jdpalumbo2/claude-skills](https://github.com/jdpalumbo2/claude-skills)
- **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:** yes
- **Shell / process execution:** yes
- **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-jdpalumbo2-claude-skills-clodex
- Seller: https://agentstack.voostack.com/s/jdpalumbo2
- 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%.
