AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Clodex Build

skill-jdpalumbo2-claude-skills-clodex-build · by jdpalumbo2

Use when the clodex router or clodex-plan hands off a run at stage `build`, when a resumed run has an approved plan whose batches are not all committed and reviewed, or when a plan assumption turns out to be wrong while implementation is already in flight.

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-jdpalumbo2-claude-skills-clodex-build

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jdpalumbo2-claude-skills-clodex-build)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
17d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Clodex Build? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

clodex-build — small batches, an explicit contract, a reviewed delta

Overview

This stage turns an approved plan into commits. It owes the run one thing per batch, and all of it is fact in the event log rather than claim in a transcript:

| Fact | Event | In the manifest | |---|---|---| | what this batch may touch | batch:opened | batches[].owned_paths | | the delta was reviewed, and the verdict | batch:reviewed | batches[].delta_review | | the commit it landed in | batch:committed | batches[].commit |

Plus, when an assumption breaks mid-flight, a plan amendment — never silent drift.

Where this stage ends. You prove each batch against its own test expectations: the profile's test command green, plus your review of the delta. You do not run the plan's declared evidence classes, record verification:evidence, or decide anything about verification debt — that is clodex-verify. You do not write the changelog, bump the version, tag, push, or deploy — that is clodex-ship, and §2 makes those files forbidden to you.

You arrive here from clodex (which owns preflight, the profile, and the run directory) or from clodex-plan (which owns the plan, its owned paths, and its approval). If you were invoked without an absolute run directory, stop and invoke clodex — do not go looking for a run yourself.


0. Paths and commands

CLODEX_HOME="${CLODEX_HOME:-$HOME/.claude/skills/clodex}"   # the router's dir, not this one
STATE="$CLODEX_HOME/state/clodex_state.py"
RUNNER="$CLODEX_HOME/runner/run-codex.sh"
RUN_DIR=""
SNAP="$(python3 "$STATE" rebuild "$RUN_DIR")"
REPO="$(printf '%s' "$SNAP" | python3 -c 'import json,sys;print(json.load(sys.stdin)["repo"])')"
PLAN="$(printf '%s' "$SNAP" | python3 -c 'import json,sys;print(json.load(sys.stdin)["plan"]["path"] or "")')"
cd "$REPO"
PROFILE="$REPO/.clodex/profile.json"

Shell variables do not survive between command invocations — re-establish this block at the top of every shell you run these procedures in. Everything below runs from $REPO; git add, git status, and the profile reads all resolve relative paths against the current directory, so from a subdirectory they answer the wrong question.

Engine verbs, payload on stdin:

python3 "$STATE" status  "$RUN_DIR"     # human summary
python3 "$STATE" rebuild "$RUN_DIR"     # the manifest: full snapshot JSON
python3 "$STATE" append  "$RUN_DIR" ` — this run is at stage `open`, not build."*
- `verify`, `ship`, `closed` → you are past this stage. Hand back the same way.
  You **cannot** re-enter an earlier stage: the reducer refuses it with *"stage
  would move backwards"*.

**Then, before any batch: confirm the plan is actually approved.** Building
without a standing approval is building against nothing:

```bash
python3 - "$STATE" "$RUN_DIR" ` also prints nothing, no work exists yet — go to §6 and run the implementer. |
| a batch with `delta_review: "fail"` | a review you have not answered | §9 |
| a batch reviewed `pass` with `commit: null` | reviewed, not committed | §10 |
| `plan.amendments` non-empty and its `required_review` unsatisfied | mid amendment | §11, step 6 |
| every planned batch committed and passed | done | §12 |

---

## 2. What the profile forbids you

Read it before you write a contract. Every batch is bound by what it says:

```bash
python3 - "$PROFILE" /` — only the state engine writes there | the engine |

**May build touch the changelog? No.** Not "usually not", not "unless the plan
asked": no. If the plan's Batches table lists the changelog or the version source
as an owned path, that is a defect in the plan — amend it (§11) with the path
removed, and say why. If the user asks for a changelog entry mid-build, the
answer is that ship writes it from the run's evidence and it will be there.

The lockfile a package manager rewrites, a formatter's incidental reflow, a
config file "while we're in there" — none of these is owned unless the plan says
so. Not owned means forbidden (§5).

---

## 3. The change boundary — before you open your first batch

The `clodex` skill's §5B is **yours to execute**. It could not run earlier: the
router has no owned paths yet, and the approval event it may need binds to a plan
hash the reducer refuses until a plan is recorded. Run it now, once, before the
first `batch:opened`.

Compare the plan's owned paths (its Batches table, all rows) against the dirty
snapshot the user acknowledged at open:

```bash
python3 - "$STATE" "$RUN_DIR" "src/thing/" "docs/plans/.md"         # or whatever branch.naming says, filled in

The manifest's branch field records where the run opened; it is not updated by a checkout, and no event changes it. Tell the user the branch name you created — clodex-ship will need it, and the manifest will not say it.


5. The batch contract

A batch is one bounded unit of implementation work. clodex-plan gave each one its owned paths and its Done when. The contract is the rest, and it is a file you write, not an intention you hold: it goes to the implementer as a hashed input, and the envelope proves which contract the work was done under.

Write $RUN_DIR/batch-.contract.md. Every heading is required.

# Batch  contract — run 

Plan:  v, hash 
Done when: 

## Owned paths — the only paths this batch may create or modify
- `src/thing/`
- `docs/plans/.md`

## Forbidden paths
- `` — release-owned, written by clodex-ship
- `` — release-owned, written by clodex-ship
- git tags — no `git tag` in this stage, at all
- `.clodex/profile.json` — committed repo state this stage never edits or stages
- `.clodex//` — run state; only the state engine writes there
- ``
- `` — someone else's
  uncommitted work
- **Everything else in the repo. Not owned means forbidden.**

## Test expectations
- `` exits 0 — the micro-gate
- 
- 

Three things make this contract useful rather than decorative:

  1. Owned paths are copied from the plan, not reinterpreted. If they are

wrong or insufficient, that is an amendment (§11), not a quiet widening.

  1. Forbidden paths are named literally, filled in from §2's output — not

"release files".

  1. Test expectations are commands, not adjectives. "Tests pass" is not a test

expectation; python3 -m unittest discover -s tests exiting 0 is.

Batches run one at a time, in the plan's order. The next batch does not open until the current one is reviewed pass and committed (§12's exit check is the same rule).


6. Open the batch, then run the implementer

First, capture the pre-invocation baseline — the tree exactly as it stands this moment. §7 classifies against it, because everything already changed now is provably not this batch's work:

git -C "$REPO" status --porcelain -z --untracked-files=all > "$RUN_DIR/batch-.pre"

Then append, with the owned paths exactly as the contract lists them:

{"e": "batch:opened", "id": 1, "owned_paths": ["src/thing/", "docs/plans/2026-08-11-thing.md"]}

Ids are unique for the life of the run: a second batch:opened with the same id is refused — "batch 1 is already open". A batch is never re-opened; work that has to be redone gets a new batch (§11).

Before you write the prompt, read the manifest's findings for entries with disposition: "deferred-to-build" whose note names this batch. Each one goes into the prompt's "What to build" as an explicit item — plan review deferred it to exactly this moment, and a deferred finding the batch never sees was silently dropped, not deferred.

Write the prompt to $RUN_DIR/batch-.prompt.md with your file-writing tool — never as a shell string. It has exactly these parts, in this order:

Implement one batch of an approved plan. The repo root is your working directory
and you may edit files in it directly.

Plan:  — read it. This is batch  of its Batches table.
Contract: .contract.md> — read it. It binds you.

What to build:

Rules, and they override anything the plan implies:
- Create or modify only the contract's owned paths. Nothing else — not a config,
  not a lockfile, not a neighbouring module you noticed on the way past.
- Never touch a forbidden path.
- Never run `git add`, `git commit`, `git tag`, `git push`, `git checkout`, or
  `git stash`. You edit the working tree; the orchestrator reviews and commits.
- This tree contains other people's uncommitted work. Leave it exactly as it is.

Done means:  exits 0, and .

Report what you changed, file by file, and anything you could not finish. Set
status `partial` if any part of this assignment is undone — a `complete` you
cannot stand behind costs more to unwind than a `partial` costs to resume.

Run it. The implementer role runs in codex's workspace-write sandbox and edits files directly:

CONTRACT="$RUN_DIR/batch-1.contract.md"
PROMPT="$RUN_DIR/batch-1.prompt.md"
OUT="$(bash "$RUNNER" --role implementer --repo "$REPO" \
        --run-id "$(basename "$RUN_DIR")" \
        --prompt-file "$PROMPT" --input "$CONTRACT" --input "$PLAN")"; RC=$?
printf 'rc=%s line=%s\n' "$RC" "$OUT"
ENVELOPE="${OUT#* }"     # strip the FIRST word only — a repo path may contain spaces

The runner prints one line, " ", and its exit code is the authority. Never read status out of prose, stderr, or the model's summary.

| rc | Status | What to do | |---|---|---| | 0 | complete | Go to §7. "Complete" means it finished its assignment, not that the work is right. | | 2 | partial | It stopped short. Resume: the runner printed a runnable one-command resume line on stderr — surface it and run it. Do not start a fresh invocation; the work is half-done in the tree. | | 3 | interrupted | Same as partial: resume with the printed command. | | 1 | failed | Read the envelope's error and output.stderr. The tree may still have been edited — run §7 before deciding anything. | | 64 | usage error | You called the runner wrong. Fix the arguments. | | — | empty $OUT | The runner died before writing an envelope. Read its stderr. |

Confirm the envelope worked from the contract you wrote, the same way clodex-plan confirms a review hashed the current plan:

python3 - "$ENVELOPE" "$CONTRACT" .md" \
    --baseline "$RUN_DIR/batch-.pre"

Pass this batch's owned paths only, and always pass the baseline §6 captured. The verb encodes what the hand-written check used to carry: it classifies every path git status --porcelain -z --untracked-files=all reports — untracked files expanded, and rename origins included, since a staged move out of an unowned path into an owned one must count against the origin or the batch commits a file it never owned — and exits non-zero when any STRAY exists.

The baseline, not dirty_at_start, is the acknowledged set. The at-open snapshot is stale within minutes in a repo with a concurrent session: comparing against it once printed STOP — 9 path(s) where none of the nine was the implementer's, and the remedy below would have reverted six of another session's working files. Everything already changed when the baseline was written is provably not this batch's work. Omitting --baseline falls back to dirty_at_start (ancestors resolved) — legal, but only when no baseline exists, which for a batch you opened with §6 is never.

What comes back:

| Result | What it means | What to do | |---|---|---| | clean — the contract held | nothing changed outside the owned paths except what the baseline already held | §8 | | exit 1 + a path marked STRAY | the implementer strayed — or something else wrote after the baseline; the second source below tells them apart | Stop. This is a scope change, and scope changes are the user's call. Capture it, show it, ask (below). | | a path marked acknowledged | it was already changed when the baseline was captured | Leave it alone. It is not yours to stage, revert, or clean, and it is never a target of the restore below. The check cannot tell "still only their edit" from "the implementer edited it too" — the diff below answers that. | | a path marked profile (.clodex/profile.json) | the router repaired the profile and did not commit it, or the user edited it | Leave it, and say so in chat. It is the one file git check-ignore lets through, and it is never yours to stage (§2). |

Read-only, for an acknowledged path you suspect the implementer also touched — everything that happened to it since the run opened:

git diff "$(python3 "$STATE" rebuild "$RUN_DIR" |
            python3 -c 'import json,sys;print(json.load(sys.stdin)["git"]["start_head"])')" \
    -- 

More there than the user's own edit means treat it as a stray after all. It still never becomes a target of the restore below: their edit is mixed into that file, so undoing the implementer's part is theirs to do, not yours.

For a path the check printed as STRAY — and only those — first consult the second source: the implementer's own account of what it changed, in its envelope's report and its event stream. A stray the implementer's record names is its work. A stray its record does not name is far more likely another actor's, arrived in the window after the baseline — and a restore is never offered for a path the run cannot prove the implementer touched. Treat that path as the user's: show it, say the record does not claim it, and leave every restore command out of the message.

For provably-the-implementer's strays, capture the evidence before anything changes:

git diff --  > "$RUN_DIR/batch-.stray.diff"
git status --short --untracked-files=all -- 

Then put it to the user with the diff and exactly two ways forward: the stray edit is needed — that is a scope change, so amend the plan (§11) to own the path, and the work continues; or it is not needed — restore those paths and re-run the batch. Restore only after they say so, and only paths the check itself listed as STRAY and the implementer's record claims.

Which command applies is the [XY] the check printed beside the path:

| [XY] | What it is | Restore with | |---|---|---| | [??] | untracked — git has never seen it, so there is nothing to restore it to | mv it aside | | [R ], [RM], [C ] | a rename or copy the implementer staged, and this is its origin path | unstage the rename first, then restore | | anything else | tracked, and modified in place | git checkout -- puts the committed content back |

mkdir -p "$RUN_DIR/stray"
git checkout -- 
mv  "$RUN_DIR/stray/"    # never rm

A rename origin needs its own three steps, and git checkout alone is not one of them. When the implementer moved a file into the contract from outside it, the origin path no longer exists in the index or the tree, so git checkout -- fails with "pathspec … did not match any file(s) known to git" and leaves you nowhere. Undo the rename in the index first — the operation that actually restores it:

mkdir -p "$RUN_DIR/stray"
git reset -q HEAD --     # index only, never the tree
git checkout --                     # now it exists in the index again
mv  "$RUN_DIR/stray/"              # the moved copy, untracked after the reset

git reset with a pathspec touches only the index — it cannot discard a working tree change, so it cannot reach anybody's edits. After it, the origin reads as deleted and the destination as untracked, which is exactly the state the two lines below it expect.

Do not select these paths by hand, and do not re-derive them. "Not in dirty_at_start" is not the test — a file inside an acknowledged directory is not in that list either, and using it as the guard is how the user's untracked work in progress ends up moved out of their tree. The only paths eligible here are the ones the check printed as STRAY and the implementer's record claims; a path printed as acknowledged is out of scope for both commands above, a

Source & license

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

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

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.