Back to the catalog

agentic-sdlc

Provider-native multi-agent SDLC with Seeds, git worktree waves, and evidence-graded research teams; cmux remains an optional view/event lay

Open source Open in the app JSON README (API)

About

Provider-native multi-agent SDLC with Seeds, git worktree waves, and evidence-graded research teams; cmux remains an optional view/event layer

Details

Kind
Plugins
Topic
AI, RAG & memory
Publisher
codeseys-labs
Origin
gemini
Category
ferramentas
Version
0.7.6
Last push
2026-08-28T06:05:39Z
Repository state
ativo
Language
Python
License
MIT
Added
2026-08-30 14:13:39
Updated
2026-08-30 14:13:39
Origin id
codeseys-labs/agentic-sdlc

README

# Agentic SDLC Orchestrator

Reusable, provider-native operating kit for project-scale agentic software delivery across
Codex, Claude Code, and other skill-capable hosts. cmux is an optional view/event layer;
tmux is never a baseline requirement.

**Install it.** [mise](https://mise.jdx.dev) is the only bootstrap prerequisite. Read
`mise.toml` before step 2 — that step authorizes whatever it says at that moment:

```bash
git clone https://github.com/Codeseys-Labs/agentic-sdlc.git && cd agentic-sdlc
mise trust ./mise.toml          # persistent, per-path, and needs your explicit approval
mise --locked install           # 12 pinned tools, ~1.3 GB
mise run lifecycle:install -- --agent claude   # or --agent codex; one plane per run, no default
```

The dispatcher is `bin/ccodex` in the tree you just cloned; run it from there, or let mise expose
it (see [`ccodex` — the operator dispatcher](#ccodex-the-operator-dispatcher)). There is no
separate step that copies it into `~/.local/bin`.

Then `mise run lifecycle:status -- --agent claude` should report `N ok, 0 conflict, 0 absent`. Full walkthrough with
the reasoning behind each step: [Quickstart from a clean clone](#quickstart-from-a-clean-clone).
Every command with what it does: [`ccodex` — the operator dispatcher](#ccodex-the-operator-dispatcher)
and [the task table](#install-and-run-the-bundle). Verified from nothing on a fresh
`ubuntu:24.04` container against the public remote — `docs/research/2026-08-08-fresh-host-install-verification.md`
records the transcript, including two defects it found.

**Architecture: an open plugin — the multi-host pattern, since no unified plugin standard
exists (verified 2026-07).** The portable layer is the `skills/` tree (the
[Agent Skills](https://agentskills.io) format, natively read by Claude Code, Codex, Gemini
CLI, OpenCode, Cursor, Goose, Kiro, and ~40 hosts) + a root `AGENTS.md` router (read by
Codex/Gemini/OpenCode). On top sit THIN per-host manifests, all version-locked by
`scripts/bump-version.sh`:

| Host | Manifest |
|---|---|
| Claude Code | `.claude-plugin/{plugin,marketplace}.json` |
| Codex CLI | `.codex-plugin/plugin.json` + `.agents/plugins/marketplace.json` |
| Gemini CLI | `gemini-extension.json` (contextFileName → AGENTS.md) |
| OpenCode / Goose / Kiro / others | pure skills-tree discovery via the symlink installer |

Adding a skill means adding a `skills/<name>/SKILL.md` directory. The installer, validator, and
all distribution planes pick it up automatically. Never hand-edit one manifest's version.
`scripts/bump-version.sh <x.y.z>` writes all of them; `--check` gates drift in CI — the
validator reports a disagreeing manifest as an error, so the gate and CI fail closed on drift.

The baseline shape:

```text
Agent entrypoint (Codex, Claude Code, or another capable host)
  -> provider-native roles/subagents/workflows (or direct execution)
  -> Seeds queue
  -> git worktrees
  -> tests/review
  -> squash/rebase/PR

Optional adapters:
  + cmux for an already-active view/event layer
  + tmux only when an optional adapter uses it
```

**Capability-negotiated baseline:** a host may run the native Frame -> Ship loop only after the
required Git, Seeds, gate, trust, and selected-adapter capabilities are present, pinned where
applicable, and verified. Missing, untrusted, unpinned, or ambiguous required capability
fails closed. An unselected optional adapter does not block the native path. Never install,
start, or enable cmux or tmux merely to use this bundle.

## Contents

**The rule on third-party skill libraries has two parts, and the distinction between them is
the whole point: this bundle never *vendors* a foreign library's bytes, and it can *invoke* a
library's own installer on explicit request.**

- **Never vendored.** No foreign library's bytes are copied into this repository's tree. That
  is what would trigger a `NOTICE` donor obligation under ADR-0001, drag another licence into
  this distribution, freeze one snapshot of somebody else's catalog, and put entries this
  bundle did not author onto its own selection surface. Foreign *ideas* enter here by exactly
  one path: an adapted `references/*.md` file with a root `NOTICE` donor entry landed in the
  same change, re-expressed in this bundle's own prose rather than copied.
- **Installable on request, from a closed catalog.** `libraries:list`, `libraries:status`, and
  `libraries:install` support exactly `mattpocock/skills`, ECC (`affaan-m/ECC`), and
  hyperresearch through each library's *own* front door. Unlisted libraries—including gstack—are
  operator-owned foreign state until separately verified and onboarded; these tasks do not adopt,
  inspect for ownership, migrate, or remove them. Running a supported third party's installer
  copies nothing here: the bytes land in the
  operator's home, written by the library's own code, under its own name and licence, exactly
  as if the operator had typed the command. So no donor obligation attaches. These tasks are
  opt-in and collision-checked, and **no gate leaf, `contributor:setup`, deprecated `setup`,
  or `lifecycle:install` path reaches them** — `check`'s dependency closure is `validate`, `test`,
  `self-test`, `secrets`, and `contributor:setup` contains only `lifecycle:install` plus
  `hooks:install`. Installation is therefore a deliberate choice, never a side effect.

The installer's ownership model is what makes the two coexist: an entry this bundle does not
own is classified `foreign` and preserved rather than replaced. Decisions and evidence:
`docs/adr/0009-external-skill-libraries-are-opt-in-through-their-own-front-doors.md` (the
opt-in mechanism) and
`docs/adr/0008-third-party-skill-libraries-are-the-operators-own-install.md` (the
no-vendoring rule it refines), plus `skills/external-skill-libraries/`.

- `skills/agentic-sdlc/`: the flagship, provider-native orchestration skill
  for any skill-capable CLI agent.
- `skills/codex-research-os/`: vendored research-team OS — a repo-scaffolding installer
  (`scripts/install_research_os.py`) that bootstraps a 17-role research organization
  (director + specialists), claim/experiment ledgers, greenfield/brownfield workflows,
  schemas, and Make validation gates into any target repo. Pairs with the flagship's
  `references/research-team.md` (the distilled principles).
- `skills/model-tier-rightsizing/`: first-class model-routing skill. Load it before any
  model dispatch. Its four-tier policy pairs exact Sol/Fable for frontier derail work,
  Terra/Opus for judgment-workhorse silent-degrade work, and Luna/Sonnet for capable-volume
  visible-retry work; the mechanical floor selects the cheapest certified fully gated route.
  The canonical calibration records exact IDs, explicit requested effort, transport hazards,
  evidence boundaries, complements, controls, quotas, and roadmap lanes. Selection is by task
  fit, independent perspective, quota, and verified transport—not provider preference or
  artificial all-six representation. The flagship hands off through
  `references/tiered-orchestration.md`.
- `skills/dispatching-exact-ocx-models/`: exact-route dispatch seam after rightsizing resolves a
  `RuntimeAssignment`. It distinguishes generated `ocx-*` Agent types from Workflow `agent()`
  injection, checks model-visible tool compatibility, and requires verified post-run identity.
- `skills/reviewing-overengineering/`: independent complexity/deletion audit for one immutable
  plan or diff. It pairs deletion pressure with a safety-preservation rebuttal and classifies work
  as essential safety complexity, accidental complexity removable now, or speculative deferral.
  Ponytail may complement it but is never required.
- `skills/cmux-event-bus-messaging/`: optional cmux-only event-bus pub/sub pattern (publish via
  `cmux log --source msg:<topic>`, subscribe via `cmux events` with replay/resume, the
  claim-check pattern, both race gotchas). Pairs with `references/cmux-integration.md`
  and `scripts/cmux-bus.sh`.
- `skills/repo-toolchain-gates/`: the standard local gate stack — mise (pinned toolchain
  incl. CI-parity linter pins + task-runner; `mise run check` = THE gate), lefthook
  (pre-commit/pre-push enforcement, installed via mise `[tools]`), betterleaks (secrets
  gate incl. full-history scans). Includes the two verified worktree facts: hooks ARE
  shared into worktrees, mise trust is NOT. Pairs with `references/seeds-worktrees.md`.
- `skills/stacked-prs/`: the tool-agnostic stacked-PR methodology — ship a chain of small
  dependent PRs instead of one fat branch (small-batch rationale, stack structure,
  bottom-up merge, the restack discipline, when NOT to stack). The PR-landing strategy for
  dependent Seeds in a wave.
- `skills/stacked-prs-gh-cli/`: the same with ONLY plain `gh` + git — no gt/spr/ghstack.
  gh has no `stack` command (v2.95); GitHub's primitives are `--base` targeting + explicit
  retarget/requery/restack. Covers the squash-merge `--onto` restack gotcha and
  `--force-with-lease` safety. Pairs with `stacked-prs`.
  - `references/sdlc-loop.md` — phase gates, backflow, done criteria.
  - `references/seeds-worktrees.md` — Seeds queue, native worktree waves, PR flow, optional
    worker/view adapters, and the canonical in-workspace `.worktrees/<seed-id>-<slug>/`
    substrate rule (never a sibling directory).
  - `references/worktree-lifecycle.md` — one wave worktree end to end, each step with its
    refusal and recovery: create, gate, review, integrate (squash-preferred, and why),
    reconcile through the conductor-only record seam, clean up. Includes the executed Git
    facts behind those refusals.
  - `references/cmux-integration.md` — optional cmux view/event integration. Load only when
    cmux is already active or explicitly requested.
  - `references/delegation-planes.md` — native-first per-provider decision matrices plus
    optional durable/mixed-engine adapters.
  - `references/worktree-integration.md` — fan-in hazards: merge-base footprint, placeholder
    traps, re-gate-on-main, clean-apply ≠ semantic correctness, squash-scope discipline.
  - `references/mission-loop.md` — the autonomous **backlog-zero doctrine**: 8-class
    milestone classification (only ACTIVE_MILESTONE executes), seeds-first no-inline-fixes,
    WIP caps, priority math, the concurrent critique team, honest definition of done.
  - `references/tiered-orchestration.md` — model-tier assignment, honest provider/model
    resolution, the native-first capability ladder with optional adapters, bounded backflow,
    and worker lifecycle at scale.
  - `references/research-team.md` — **evidence-graded research teams** for standing
    research efforts: the evidence ladder (promote slowly, downgrade quickly), role
    separation-of-powers (attacker ≠ fixer, writer ≠ originator), one-loop discipline
    with a recorded next-action, greenfield/brownfield loops, the cheapest-decisive-
    experiment rule, gates-as-executables (no decorative model pins).
- `agents/claude/sdlc-*.md` + `agents/codex/sdlc-*.toml`: eight role agents in both CLI
  forms (symlinked globally) — **cartographer** (read-only Discover mapper), planner,
  implementer, reviewer, **researcher** (bounded unknown-resolution), **critic** (standing
  snapshot-only critique team), **integrator** (the only merging agent), **documentarian**
  (read-only documentation worker — evidence-linked doc proposals for conductor capture,
  writes only its own artifact). Full loop wiring:
  cartographers (parallel, per area) → planner → implementers → reviewers → integrator,
  critic concurrent, researchers on demand. Provider-neutral role definitions contain no static
  model/effort pin, never dispatch, and consume a conductor-supplied certified
  `RuntimeAssignment` with a certified exact model ID. `resolution_state` must be `resolved`.
  Exact model/effort request injection is mandatory and immutable. `resolved_provider` and
  `resolved_model_id` require verified model identity; independently observed provider/model
  source may be unavailable only for a unique exact-ID mapping backed by immutable request/model
  evidence. Effective effort/context readback may be honestly unavailable; requested values never
  become readback. If the assignment is requested, inherited, unresolved, incomplete, or cannot
  inject both requested model and effort, the conductor stops before dispatch and returns one
  SeedProposal. Prompt prose does not enforce a Codex model or effort.
- `agents/codex/research/`: the 17-role research-team TOMLs (repo-scoped reference
  copies — NOT globally installed; see its README; scaffolded per-repo by
  codex-research-os).
- `commands/sdlc-{init,frame,wave,mission,rightsize}.md`: Claude Code slash commands —
  `/sdlc-init` activates Agentic SDLC inside a repository without reinstalling global
  capabilities. It establishes a reviewed tracked Git baseline, Seeds queue,
  mise/lefthook/betterleaks gate stack, per-worktree trust policy, cross-host `AGENTS.md`
  guidance, and CI parity. It is a reviewed runbook: claims of idempotence or Git-wave readiness
  require observed evidence; it preserves existing project policy and stops on ambiguity. `/sdlc-frame` frames one run,
  `/sdlc-wave` runs one Seeds-backed Git-worktree wave, `/sdlc-mission` runs an
  autonomous backlog-zero mission with concurrent critique and bounded backflow, and
  `/sdlc-rightsize` discovers the operator's live OCX routes plus usable Claude-subscription
  passthrough, asks only environment-relevant source/model/task/budget questions, and produces a
  regenerable v2 `.agentic-sdlc/rightsize/model-task-map.json` + `.md` + `.evidence.json` trio. Published
  benchmarks nominate candidates; only an explicitly approved, bounded target-local evaluation
  can recommend `role-qualified`, and the checked-in runtime receipt policy still separately
  controls production admission. `mise run rightsize:evaluate -- plan ...` is read-only;
  `evaluate` consumes provider/subscription capacity only with its exact displayed authorization
  digest. Neither a benchmark, local evaluation, nor map authorizes dispatch (see
  `skills/model-tier-rightsizing/references/model-task-map-schema.md`,
  `docs/adr/0015-local-evaluation-is-the-rightsizing-promotion-boundary.md`, and
  `skills/model-tier-rightsizing/references/workflow-prompt-budget.md`).
- `.claude-plugin/{plugin.json,marketplace.json}`: the repo doubles as a Claude Code
  plugin/marketplace — `claude plugin marketplace add <path-or-git-url>` then
  `claude plugin install agentic-sdlc@agentic-sdlc` is an alternative to
  symlinks. The marketplace source may be this repository's public Git URL, so no clone
  is required first; see [Install as a Claude Code plugin](#install-as-a-claude-code-plugin).
  `plugin/` is the marketplace entry's `source`: a directory holding the plugin manifest
  plus real copies of `skills/`, `agents/claude/`, `commands/`, `output-styles/`, and
  `workflows/`. It exists because Claude Code discovers agents only in a plugin
  root's own `agents/`, and this repo nests the Claude roster one level down under
  `agents/claude/`; installing from the repo root therefore finds the skills but zero
  agents. Copies, not symlinks, and both shapes were measured on Claude Code 2.1.245
  (agentic-sdlc-d0ab): a component directory that IS a symlink is read without being
  followed, so `claude plugin validate ./plugin --strict` exited 1 with one warning per
  link, and a materialisation carrying only the plugin subtree — what a `github` or
  `archive` source fetches, reproducible offline with `git archive HEAD plugin` — left
  every link dangling and installed at exit 0 while reporting `Skills (0)  Agents (0)`.
  With copies, that same subtree install reports `Skills (18)  Agents (8)`, and
  `claude plugin validate ./plugin --strict` passes with zero warnings. The argument form
  decides what is validated: a directory dispatches on the manifest it finds, so
  `claude plugin validate .` validates the marketplace catalog and passes, while
  `claude plugin validate .claude-plugin/plugin.json` walks the repo root AS a plugin and
  passes non-strict only — strict flags two deliberate repo files, the root `CLAUDE.md`
  and the Codex roster README. Pointing a manifest field at the shared tree instead is not
  available: `"skills": "../skills"` is refused as a path-traversal attempt. `mise run
  validate` re-derives every copy from its source and fails on any drift, so a stale
  `plugin/` is repaired with
  `uv run --python 3.12.11 --script scripts/sync_plugin_tree.py --write` rather than by
  hand. The manifest also declares `outputStyles` and `workflows` — the two kinds no
  component walk discovers — and deliberately declares no `hooks`, because a plugin's
  hooks surface auto-enables with the plugin.
- `scripts/check-agentic-sdlc-prereqs.sh`: native-baseline preflight plus informational
  checks for optional adapters. Missing cmux or tmux never fails it.
- `scripts/install-skill-bundle.sh`: **one-shot global install for every native agent CLI
  present** (Claude Code skill+agents+commands, Codex skill+role TOMLs). Symlinks by default; `--copy`
  to copy. Never clobbers non-symlink files.
- `scripts/validate-bundle.sh`: pre-commit/CI gate — SKILL.md frontmatter, name==dirname,
  the 1024-char Codex description cap (silent-skip trap), broken references, TOML/JSON
  parses, shell `bash -n`, plugin manifest validation, secret/internal-hostname sweep.
- `scripts/cmux-bus.sh`: optional cmux-only event-bus helper (pub/sub/seq).

## First task: a small hello world

After the bundle is installed, start with one small, local change rather than a mission or a
multi-worker plan. Three terms are enough for the first pass: **Seeds** is the project's durable
work queue; a **Frame** is the short plan for one task; and a **Wave** is the reviewed worktree
execution of ready Seeds.

1. In the target project, if its Seeds queue is absent, route to
   [`/sdlc-init`](commands/sdlc-init.md) and stop. It is a reviewed activation runbook: it
   establishes evidence for the Git baseline, queue, gates, and guidance, or stops on a conflict.
   Do not use Frame or Wave to improvise activation.
2. Frame one observable task, for example:

   ```text
   /sdlc-frame Add a hello command that prints "hello" and one test for it.
   ```

   The Frame records the done condition, scope, required gate, review point, queue state, and any
   outward operation that would need separate explicit approval.
3. If a certified delegation route is available, run the ready task through
   [`/sdlc-wave`](commands/sdlc-wave.md). Every actual worker or model spawn still requires a
   conductor-supplied certified `RuntimeAssignment`; an inherited, unresolved, or unverified route
   stops before dispatch.
4. If no certified delegation route exists, the Frame may choose **exactly one** bounded,
   non-delegated conductor execution instead. It runs in one clean, dedicated Git worktree with
   the same framed scope, acceptance criteria, gate, snapshot review, and conductor-only queue
   reconciliation as a Wave. It has zero workers, zero model spawns, and no `RuntimeAssignment`
   claim. It is not a convenience fallback: stop rather than execute when the task needs another worker,
   another direct pass or retry, parallel work, or cannot be bounded and reviewed.
5. Review the stable diff, run the named gate, and reconcile findings before describing the task as
   complete. A gate, review, queue state, or local status never authorizes a push, PR mutation,
   merge, publication, deployment, or another outward effect.

The detailed command flow lives in [`/sdlc-frame`](commands/sdlc-frame.md) and
[`/sdlc-wave`](commands/sdlc-wave.md); both route an absent queue back to
[`/sdlc-init`](commands/sdlc-init.md).

## Install and run the bundle

The commands below describe the **current** checkout-backed distribution, which stays the home
for customization, contribution, gates, and release building. A prerelease quick install exists
and is container-proven (2026-08-24, v0.7.4), EXACT-VERSION ONLY: declare
`[tools."github:Codeseys-Labs/agentic-sdlc"]` with `version = "0.7.4"` and `prerelease = true`,
run `mise install`, and the installed tree exposes exactly one command, `ccodex`. Running
`mise trust` on that tree's reviewed `mise.toml` is a persistent mutation needing your explicit
operation-specific approval; after it, `ccodex bundle install --agent claude`
activates the plugin. **That spelling is retired now** — it refuses at exit 2 naming
`ccodex install --scope user --agent <claude|codex>` — and the sentence keeps the verb it
executed because it records what the container proved, not what to type. Until the acquisition
half lands, `mise run lifecycle:install -- --agent claude` is the working equivalent on a release
tree: the top-level `install` is the receipted activation and refuses without an acquired
candidate. The unversioned `mise use -g github:Codeseys-Labs/agentic-sdlc` does not
resolve a prerelease and is not claimed to work. The release tree carries no `.git`; gates and
Seeds stay on the managed checkout. Contract, payload split, and boundaries:
[`docs/plans/2026-08-14T163833Z-Install-UX.md`](docs/plans/2026-08-14T163833Z-Install-UX.md) and
ADR-0011 as amended.

### Managed fetch, without cloning by hand

If you would rather not choose a directory or track where the tree lives,
`scripts/bootstrap-agentic-sdlc.sh` fetches this repository into a managed location and then
stops, printing the remaining commands instead of running them. Download it, read it, then run
it — in that order, because it is a script that clones:

```bash
curl -fsSL -o bootstrap-agentic-sdlc.sh \
  https://raw.githubusercontent.com/Codeseys-Labs/agentic-sdlc/main/scripts/bootstrap-agentic-sdlc.sh
less bootstrap-agentic-sdlc.sh
bash bootstrap-agentic-sdlc.sh --dry-run
bash bootstrap-agentic-sdlc.sh
```

`--dry-run` prints the exact `git clone` it would run and creates nothing. `--remote <git-url>`
selects the exact Git remote (rather than relying only on `AGENTIC_SDLC_REMOTE`), and `--ref`
selects its branch or tag. The managed clone lands in
`${XDG_DATA_HOME:-$HOME/.local/share}/agentic-sdlc`, reported by `--print-path` and overridable
with `AGENTIC_SDLC_HOME`; the resolved commit is recorded in
`${XDG_STATE_HOME:-$HOME/.local/state}/agentic-sdlc/bootstrap-receipt.json`. Its final handoff
prints the receipt, checkout commit, reviewed files, exact trust/toolchain commands, an explicit
Claude-or-Codex install choice, and the status verification command. Removing both paths removes
everything the script created. Re-running is idempotent: it reports the existing clone's ref and
commit, refuses rather than clobbering an unexpected remote, a dirty tree, or a ref mismatch, and
fetches only under `--update`. `--help` documents each of those flags without running a tool.

The script requires mise and git and installs neither, so it adds no bootstrap prerequisite. It
deliberately does not trust the config, resolve the toolchain, or install bundle entries: those
stay separate approvals against a tree you have read, and it prints them as steps 2 through 5
below. HTTPS authenticates the transport, not the contents — nothing here verifies a signature
over the fetched commit, so read the tree before trusting it. `docs/adr/0011` records why the
clone is managed rather than eliminated: the tasks still come from a tree on disk, because every
task command and installed symlink resolves against one. For a genuinely tree-free install of the
plugin payload alone, see the marketplace plane below.

### Quickstart from a clean clone

Five steps, in order, from an empty directory. Step 3 is a persistent mutation with its own
approval gate; steps 2 and 3 are what a fresh clone otherwise fails on. The managed fetch above
replaces step 1 only.

1. Clone the repository and enter it. Obtaining the source needs Git, which stays a
   runtime-readiness capability rather than a second bootstrap prerequisite:

   ```bash
   git clone https://github.com/Codeseys-Labs/agentic-sdlc.git
   cd agentic-sdlc
   ```

2. Review the two files that step 3 authorizes: `mise.toml` (the pinned toolchain and every
   task command) and `mise.lock` (per-platform source URLs and SHA-256 checksums). Read them
   before trusting them; the trust decision covers whatever they say at that moment.

3. Trust the reviewed config. This is a persistent per-path mutation. It requires explicit
   operation-specific approval for that exact config path; a general clone, run, or
   implementation approval never covers it:

   ```bash
   mise trust ./mise.toml
   ```

   Skipping this step is the first-run failure: every later `mise` command in the repository
   exits with `config files are not trusted`. To validate without persisting anything, use
   `mise --no-config --cd . exec ...` instead of trusting.

4. Resolve the locked toolchain. `mise.lock` pins **12 tools** — `uv`, `node`, `npm`, `bun`,
   `lefthook`, `jq`, `ripgrep`, `fd`, `gh`, `betterleaks`, `opencodex`, and `seeds-cli` — for
   roughly 1.3 GB and about 30 seconds on a warm network. mise ships `auto_install` enabled, so
   skipping this step does not avoid the cost: the first `mise run <task>` installs all 12
   without prompting.

   ```bash
   mise --locked install
   ```

5. Choose an install plane explicitly. Claude entries go under the configured Claude home
   (`~/.claude` by default); Codex entries go under the configured Codex home (`~/.codex` by
   default, or `CODEX_HOME`). `--agent` is REQUIRED on install, status, and uninstall — there is
   no default and no wildcard, and a selector-free run refuses at exit 2 naming both planes. To use
   non-default roots, pass `--claude-home <path>` and `--codex-home <path>` after `--`.

   ```bash
   mise run lifecycle:install -- --agent claude
   # or: mise run lifecycle:install -- --agent codex
   # inspect one plane without writing: mise run lifecycle:status -- --agent claude
   ```

   A detected Claude marketplace install is reported once as a Claude-plane conflict and blocks
   only direct Claude installation; Codex continues. Use either the marketplace or direct
   installation for Claude, not both. Foreign or changed entries are preserved with a reason and
   a retry instruction; never delete a reported path merely to make the installer green.

6. Nothing further is needed to reach the dispatcher. `bin/ccodex` is committed in the tree and
   self-locates its distribution root as the parent of its own `bin/`, so `<checkout>/bin/ccodex`
   works immediately and mise's `github:` backend exposes exactly that one command on an installed
   release. There is no install step that writes a second copy into `${XDG_BIN_HOME:-$HOME/.local/bin}`
   — see [the dispatcher section](#ccodex-the-operator-dispatcher) and, if you ran the retired
   installer on an earlier release, [Retired: the operator-tools PATH plane](#retired-the-operator-tools-path-plane).

Each bundle lifecycle action ends in a terminal summary. `mise run lifecycle:status -- --agent
<claude|codex>` reports only
entries already present in the lifecycle ownership record: either
`no owned entries for this host (run: mise run lifecycle:install)`
or an `N ok, M conflict, K absent` summary — the empty-plane line quoted here in full, remedy hint
included, because the truncated paraphrase is a shape the product never prints
(`agentic-sdlc-b97e`; `tests/test_verification_runbook_contract.py` now binds both shapes to
`status_summary()`'s own returns). It does not inventory unowned names in a configured
collection. Before installation, or when a path may have been installed through another checkout,
use `mise run lifecycle:install -- --agent <claude|codex> --dry-run`; that read-only preview discovers
an occupied unowned destination, reports it as preserved, and never adopts, overwrites, or removes
it. Install and uninstall summaries separately name installed/removed, preserved, planned, and
conflict counts. `mise run check` runs the authoritative gate. Each command's exit code and output
are evidence about that run only; neither authorizes any outward effect.

**Mise 2026.4.27 or newer is the only bootstrap prerequisite.** It is the managed-tool bootstrap,
not the sole readiness prerequisite. The checked-in `mise.toml` pins `uv`. `mise.lock` records
source URLs and SHA-256 checksums for Linux, macOS, and Windows. `uv` supplies Python `3.12.11`
for every authoritative Python entrypoint. Git, a verified Seeds distribution, supported trust
behavior, repository gates, and the selected adapter remain runtime-readiness capabilities, not
additional bootstrap prerequisites. Resolve and record the actual provider/model only when the
adapter proves it; otherwise record inherited or unresolved.

Every v1 dispatch receipt uses exactly `requested_model_id`, `requested_effort`,
`requested_context_form`, request-injection evidence, resolved provider/model identity evidence,
and effective effort/context readback status plus evidence. Request-injection evidence binds
canonical exact requested model/effort/context bytes, adapter identity/version/config digest, and
request-byte digest. It validates internal consistency only: it never proves external injection,
no-bypass enforcement, or spawned-worker identity. Effective effort/context may be `unavailable`
when the transport does not expose them; requested values never become readback. An external
harness calls receipt admission immediately before spawn and correlates its digest. It remains
responsible for injection, no-bypass, and spawn identity; this repository supplies no host
launcher. Only an admitted, certified tuple can reach spawn. Exact Claude `[1m]` forms remain
denied pending tuple-specific policy evidence; base Claude eligibility and calibration-supported
GPT `[1m]` tuples remain. A passing local status or gate never authorizes push, publication, PR
mutation, merge, deployment, credential, or other outward effect.

The flagship skill ships the portable Node-stdlib `tools/seeds-launcher.mjs`. From an exact clean
Git distribution root, run its explicit `bootstrap --distribution <distribution-root>` mode under
Node `22.23.2`. Both bootstrap and inspect reject any other executing Node. Bootstrap rejects
nested, staged, dirty, untracked, or ignored distribution content, then alone runs reviewed
`mise --locked install`. That install isolates HOME, mise config/data/cache, hooks, npmrc, and
registry selection from ambient values. Only the reviewed root `mise.toml`/adjacent lock, the
fixed official npm registry, npm backend, and private empty configs select acquisition. It
resolves exact config-free Node `22.23.2`, Bun `1.4.0`, and Seeds `npm:@os-eco/seeds-cli@0.5.15`
roots. It accepts the released package's benign string `engines.bun` compatibility metadata
while rejecting actual config/macro/preload controls. It atomically publishes an exact Git
commit/tree and tool-hash receipt. The Seeds lock proves the exact version and npm backend, not
tarball or transitive dependency integrity. Neither that claim nor the receipt closes a same-UID
TOCTOU race between validation and execution.

Before any persistent `mise trust` operation—including the bootstrap below—obtain explicit
operation-specific approval for the exact reviewed config path. The same gate applies to
persistent Codex/global config edits, shell aliases, and credential writes; a general run or
implementation approval is insufficient. Process-scoped validation may instead use
`mise --no-config --cd <repo> exec ...` without persisting trust.

Bootstrap the repository and inspect the available lifecycle tasks:

```bash
mise -C <distribution-root> tasks
<exact-node-22.23.2-root>/bin/node <installed-flagship>/tools/seeds-launcher.mjs bootstrap --distribution <exact-clean-git-root>
```

After explicit bootstrap, read-only Seeds operations use `inspect --target <target>` against only
the active receipt. Inspect never installs, networks, calls mise, or repairs state. It allows only
`--version`, `prime`, `ready [--format json]`, and `blocked [--format json]`; all other input fails
before exact Bun starts. Exact Node uses `shell:false` to invoke only absolute recorded Bun and entry
paths. Bun receives `--config=<trusted-empty-file>`, `--no-env-file`, and `--no-install`; its
allowlisted environment isolates target `bunfig`, `.env`, package configuration, ambient `BUN_*`,
`NODE_OPTIONS`, npm/mise overrides, and unreviewed Seeds debug settings. PATH contains only the
independently recorded Git directory, with system/global Git config isolation.

Queue mutation uses `record` with `--queue-writer conductor`. A repository with no `.seeds` node may
use only `--expect-queue absent init`; the launcher rejects existing/partial/file/symlink/redirected
surfaces, snapshots `.gitattributes`, and refuses non-UTF-8 or exact-line/substr-match-ambiguous
prestates before mutation. It invokes exact pinned `init --json` and verifies exactly the five
initializer files plus only the precise missing merge-union append. Existing queues require an
exact sha256 and admit only verified create/update deltas. Standalone claim, close, delete, prune,
disposition, sync, and other mutation forms remain forbidden. The prerequisite checker exposes
separate exact-runtime inspect, init, and record front doors; the skill and
`references/seeds-worktrees.md` define their unambiguous contracts.

Mise trust is scoped to each absolute config path. Every linked worktree needs separate
explicit operation-specific approval before trusting its reviewed `mise.toml`, after reviewing
the diff. `MISE_PARANOID=1` deliberately rejects an untrusted worktree. After that approval,
apply `MISE_PARANOID=1 mise trust <worktree>/mise.toml`, then rerun the command. Locked
resolution fails closed when the current platform is absent from `mise.lock`.

Every task this repository defines, so `mise tasks` never reveals an undocumented one:

| Task | Purpose |
|---|---|
| `lifecycle:install` / `lifecycle:status` / `lifecycle:uninstall` | Install, inspect, or remove entries for one plane on the current host. Each requires `-- --agent claude` or `-- --agent codex`; a selector-free run refuses at exit 2. |
| `lifecycle:install:claude` | Install only the Claude Code plane on the current host. |
| `lifecycle:install:codex` | Install only the Codex plane on the current host. |
| `lifecycle:install:all-hosts` | Install the current host and, from WSL, the native Windows host too. |
| `lifecycle:status:all-hosts` | Report current-host and native-Windows state when run from WSL. |
| `research-os:install` | Scaffold the repo-scoped research OS through pinned uv/Python; pass installer arguments after `--`. `--target` is required, so there is no implicit current-directory scaffold. |
| `claude:statusline:status` / `claude:statusline:activate` / `claude:statusline:deactivate` | Inspect or explicitly manage only Claude Code's `statusLine` fields. |
| `claude:hooks:status` / `claude:hooks:activate` / `claude:hooks:deactivate` | Inspect or explicitly wire one installed agent hook into your Claude settings, one owned `hooks.<Event>` array element at a time (`-- --hook <name>`, never "all"). Each activation is its own settings mutation, no `lifecycle:*` path reaches it, and a foreign or modified element is preserved and reported. |
| `release:build` | Build the deterministic unpublished-candidate archive of the committed HEAD tree into `dist/`. Refuses a dirty tree. A built archive is evidence of what was archived, never a release or a publication. |
| `release:smoke` | Run `policy/release-smoke.v1.json` against an EXTRACTED archive (`-- --tree <extracted-root>`). Every case asserts report content rather than a bare exit code, because exit 3 is a legitimate status here. A tree inside this checkout is refused rather than smoke-tested as the artifact. |
| `usage:report` | Advisory read-only usage projection over the local evidence stores. Subscription cost is always unpriced, the default output is dollar-free, refusals appear in the output itself, and there is no cross-store total because the two stores overlap unprovably. Never a gate leaf, and a report authorizes nothing. |
| `ocx:launch` / `ocx:ultracode` | Launch Claude Code through the gateway using your own `~/.claude` login — native Claude models on your subscription, gateway models on their own providers — normally or with session-only Ultracode. Ordinary permissions are the default; a first `--yolo` is the explicit unsafe bypass profile. |
| `ocx:status` / `ocx:restart` / `ocx:configure` | Report opencodex gateway reachability, restart it cleanly, or configure providers through their own login flows. |
| `libraries:list` / `libraries:status` | List the installable external skill libraries with their front doors and surface cost, or report which are already present in this home. Read-only. |
| `libraries:install` | Install explicitly named external skill libraries through their own front doors; dry run unless `--yes`. Vendors nothing into this tree, and no gate leaf, `contributor:setup`, or deprecated `setup` path reaches it. |
| `libraries:migrate` | De-duplicate a name another channel holds for the same upstream: retire that channel's copies through its own removal path, then install. Dry run unless `--yes`; names at least one library, never migrates everything. |
| `mermaid:provision` | Provision the pinned Linux x64 Mermaid browser runtime. Downloads a pinned browser, so it is an explicit operator step and never a gate leaf. |
| `mermaid:linux-test` | Run the bounded Linux Mermaid renderer tests; they skip with named reasons when the runtime is absent. |
| `rightsize:evaluate` | Explicit non-gate rightsizing CLI: discover, plan, evaluate after digest-bound approval, or deterministically render prior evidence. Live evaluation may consume provider/subscription capacity and send the selected task-pack data outward. |
| `validate` | Run the portable bundle validator alone (the pre-commit hook's subset). |
| `test` | Run the installer test suite. |
| `self-test` | Exercise install/status/uninstall in an isolated home. |
| `secrets` | Scan tracked plus nonignored-untracked regular files with the pinned scanner and tracked extend-only config. Symlinks and paths beneath symlinked parents are not followed; ignored runtime state stays out, and history remains a separate consented step. |
| `check` | Run the authoritative validation, tests, self-test, and secrets gate. Last measured on Linux 2026-08-26: the `test` leaf ran 2426 tests in 426s (`OK (skipped=13)`), while `validate` and `self-test` each finished in under 5s and `secrets` scanned about 12 MB in 15s, so the suite dominates and 15 minutes is a reasonable budget — more on a loaded host, since gate runs contend for CPU and I/O. Treat both numbers as stale-by-design: the count grows with the suite, the clock varies by host, and the gate's verdict is the evidence. |
| `hooks:install` | Install the checked-in lefthook hooks. |
| `contributor:setup` | Install the configured bundle planes plus this repository's Git hooks. |
| `setup` | One-release deprecated forwarder to `contributor:setup`. |

A normal Unix install uses symlinks. On Windows, automatic mode uses directory junctions
for directories and file symlinks for files. When the host cannot create those links, it
falls back to copies. Strict link mode does not use that fallback. The installer records
per-entry ownership in the platform state directory (`XDG_STATE_HOME` on Unix,
`LOCALAPPDATA` on Windows). Lifecycle operations use that record to distinguish bundle
entries from user files. Write-capable lifecycle commands are serialized per state file.
Ownership is BYTE identity: a record carries the digest of the bytes this lifecycle published, and
nothing else identifies the destination. There is no birth-timestamp or device/inode requirement, so
there is no minimum libc and no filesystem that has to expose `statx` birth time — a host where
`cp -r` works can install. See "Ownership and lifecycle rules" below for what that costs.

```bash
mise run lifecycle:install -- --agent claude
mise run lifecycle:status -- --agent claude
mise run check
```

The native Windows path runs the ordinary current-host task; it does not invoke WSL. When
`lifecycle:install:all-hosts` or `lifecycle:status:all-hosts` is run from WSL, it runs the WSL
current-host lifecycle first and then invokes the native Windows mise task. The two host
summaries remain separate, and the native task's arguments and exit code are preserved.

### Where the statusline comes from

The Claude/Codex bundle installer and the plugin do not own shell aliases, PATH, or global Claude
settings, and no lifecycle here writes into a PATH directory at all. Writing your `settings.json` is
a persistent user-environment mutation that requires explicit operation-specific approval for that
exact file; a general install approval never covers it.

The packaged statusline is one **bundle ledger row**: `lifecycle:install -- --agent claude` publishes
`assets/claude/statusline-command.sh` to `<claude-home>/.claude/statusline/agentic-sdlc-statusline`
at mode `0755`, and that owned path is the only place `claude:statusline:activate` will take a
command from — so a statusline that is absent, unowned, drifted, or unexecutable is a named refusal
rather than a `statusLine.command` pointing at bytes no lifecycle owns. Installing it does not
activate it; writing `statusLine.type` and `statusLine.command` into your settings is the separate
operation-specific grant below.

```bash
mise run lifecycle:install -- --agent claude   # publishes the owned statusline command
mise run claude:statusline:status           # read-only: active | inactive | unmanaged | conflict
```

Activation, dry run, and removal are covered under
[Which login a launch uses](#which-login-a-launch-uses), which is where the settings-mutation grant
is described.

### Retired: the operator-tools PATH plane

An earlier release shipped a separate Unix operator-tools lifecycle that rendered `ccodex` and
`agentic-sdlc-statusline` into `${XDG_BIN_HOME:-$HOME/.local/bin}` and refused unless that directory
was already on `PATH`. **It is deleted.** `bin/ccodex` is committed, self-locating, and exposed
directly by mise, so a second dispatcher existed only to be placed on `PATH` by a lifecycle that
would not edit `PATH`.

Deleting the installer deleted `operator-tools:uninstall` with it, so **if you ran it, you still own
those files and nothing here will remove them for you.** `ccodex doctor` names the leftover
store whenever it is present. Remove them by hand:

```bash
rm -f "${XDG_BIN_HOME:-$HOME/.local/bin}/ccodex" \
      "${XDG_BIN_HOME:-$HOME/.local/bin}/agentic-sdlc-statusline"
rm -rf "${XDG_STATE_HOME:-$HOME/.local/state}/agentic-sdlc-operator-tools"
```

Two things worth checking while you are there. A stale `~/.local/bin/ccodex` earlier on `PATH` than
mise's shim keeps answering as if nothing changed — `type ccodex` tells you which file wins. And if
you activated the statusline through the old plane, `statusLine.command` in your Claude settings
still points into `~/.local/bin`; re-run `claude:statusline:activate` after a
`lifecycle:install -- --agent claude` so it names the owned ledger path instead. The historical
`ocx-launch` and `ocx-ultracode` aliases, if you have them, are removed the same manual way.

### `ccodex` — the operator dispatcher

`ccodex` is the whole daily use surface without mise in the way. Plain `claude` remains the direct
Anthropic-routed CLI; `ccodex launch` adds the gateway to that same login, so one session serves
both catalogs — native claude ids pass through to Anthropic on your subscription and gateway ids
route to their own providers (ADR-0014). It is not a separate non-Anthropic-only route. Fresh
Nothing here creates `ocx-launch` or `ocx-ultracode`; if an old release left you copies, remove them
by hand (see [the retirement section](#retired-the-operator-tools-path-plane)). Every gateway command remains reachable as
`ccodex ocx <verb>`, the low-level compatibility form; `ccodex --help` prints the surface at any time.

**Gateway plane** — running one Claude Code session that can reach both catalogs:

| Command | What it does |
|---|---|
| `ccodex ensure` | Ensure the gateway is healthy without launching Claude Code. |
| `ccodex launch [--yolo] [claude args...]` | Ensure the gateway is healthy — start it if down, restart once if half-up — then launch Claude Code in the caller's current workspace through the distribution's pinned gateway toolchain, using your own `~/.claude` login. Native claude models pass through to Anthropic on your subscription while gateway models route to their own providers, in one session. Fails closed if the gateway never becomes healthy, and refuses (exit 3) when exported, persistent, or explicit `--settings` configuration would silently defeat the route. Accepted Claude arguments are forwarded unchanged. A first `--yolo` is consumed by ccodex and explicitly selects Claude Code's permission-bypass mode; it is unsafe outside an isolated, disposable environment. Use `-- --yolo` only to forward that spelling literally. |
| `ccodex launch --model <id>` | Pick any id in the running gateway's live catalog, including a namespaced one: `--model muse/muse-spark-1.2`. Run `ccodex models` for the list. |
| `ccodex ultracode [--yolo] [claude args...]` | The same fail-closed launch path with session Ultracode applied. Ordinary permissions remain the default. A first `--yolo` explicitly selects the same unsafe permission-bypass mode as `ccodex launch --yolo`; this is the ccodex equivalent of the historical `ccode-ultracode` alias. Ultracode owns the session `--settings` value and refuses a competing setting. |
| `ccodex status` | Read-only supervision view: pid, port, uptime, healthy/down, log location, configured providers each compared against the LIVE catalog, whether anything exported here or in the settings documents Claude Code reads for `env` would defeat the gateway route — the check NAMES the documents it read and what it did not read — and the attribution log command. Exit 0 means the gateway answered an identity-checked probe at that moment — evidence, not authorization. |
| `ccodex restart` | Stop the gateway cleanly, then ensure it is back up. Fails closed on an unclean stop. Interrupts in-flight turns in every routed session, and `ocx` rewrites shared `~/.codex` config as part of its lifecycle. |

**Providers and models** — what a launched session can actually pick:

| Command | What it does |
|---|---|
| `ccodex providers` | Configured providers, and which are LIVE in the running gateway. A provider in the config file is **not** live until `ccodex restart` has published it — check here rather than trusting an add's success message. |
| `ccodex models` | The running gateway's flat live catalog. Muse models appear as ordinary namespaced entries, not as a separate plane. |
| `ccodex set-fast-model [<exact-model-id\|->]` | With no argument, choose a Claude Code family or a model in the gateway's live OCX catalog, or clear the override to use the normal subscription Haiku slot. One argument preserves the exact noninteractive path; `-` clears. The write goes through OpenCodex and is **not** Auto mode's permission classifier. Claude families are entitlement-checked when used; only the OCX rows are proven live when the menu is built. |
| `ccodex configure` | With no arguments, print the admitted configuration surface in detail. |
| `ccodex configure provider add\|edit\|remove\|set-default <name> ...` | Reviewed provider mutation for non-Anthropic providers. Writes the **config file only** — see [the key sequence below](#adding-a-provider-that-needs-an-api-key). |
| `ccodex configure account add-key <name>` | Store a provider API key, read **only** from piped stdin. |
| `ccodex configure account list\|current <name>` | Masked credential inspection. |
| `ccodex configure help <verb>` | Inspect the upstream `ocx` surface without running it. |

**Experimental non-Claude Auto fallback, per launch only.** Claude Code exposes no supported
`classifierModel` setting. The only documented indirect experiment is to exclude Sonnet 5 from one
launch, which makes current Claude Code fall back to the session model when that route is otherwise
eligible:

```bash
auto_settings='{"availableModels":["gpt-5.6-sol"]}'
ccodex launch --model gpt-5.6-sol --permission-mode auto --settings "$auto_settings"
```

Use the same exact routed ID in both places and keep all inline settings in that one document.
`availableModels` constrains session, subagent, workflow, skill, and advisor selection for the
whole launch—not only Auto—and routed GPT/Muse Auto support is undocumented. This recipe is not an
identity claim; a real claim needs a separately approved bounded canary with request-correlated
gateway attribution. See
[`docs/research/2026-08-12-claude-code-auto-mode-controls.md`](docs/research/2026-08-12-claude-code-auto-mode-controls.md).

**Installed-bundle lifecycle** — managing what is installed, without mise:

| Command | What it does |
|---|---|
| `ccodex install\|status\|update\|uninstall --scope user\|project --agent claude\|codex` | The one lifecycle verb family. BOTH selectors are required on all four; a run missing either refuses at exit 2 naming the flag, and there is no default and no wildcard for either. `--scope project [--project PATH]` resolves ONE repository root — the named path, or a walk up from the working directory — and keys that plane by it, so two worktrees of one repository are two independent planes; project scope is copy-only, and an unresolvable, forbidden, or non-git root refuses at exit 3 by name with nothing written. `--agent codex` refuses project scope by name: its configured root IS its agent root, so it has no repository-local collection. Project scope places the whole selected plane's payload set, workflow bytes included: it is the ONE path that owns a repository's `.claude/workflows/` since the per-file `claude:workflows:*` manager was deleted, so a placement there enables the workflow at the target's next session — every completed project-scope run says so — while hook bytes land inert, since wiring one into settings is its own grant. An already-enabled copy byte-identical to the payload's own bytes is adopted as removable; a drifted one is preserved and named. |
| `ccodex doctor [--json]` | The whole-box read: every state store, by absolute path, with a verdict each. No selectors — "what is on this machine" spans every scope by definition. Six rows today, each carrying a component, a `live`/`retired` kind, and `present`/`absent`/`unreadable`; an absent store is still named, so the count is a fact about this distribution rather than about your machine. This sentence was aspirational until 2026-08-26 — a populated host used to read out three of the six — and it is now checked by a test that re-derives the store set from the source (gh #8 acceptance 9). Naming a store never creates it, and only a retired one carries a removal remedy. |
| `ccodex recover --dry-run [--json]` / `--apply <plan-sha256>` | Propose the one pending transition this host can carry and render the digest that approves exactly that plan, or resume it. The approval IS the digest. |
| `ccodex bundle …` / `ccodex sdlc …` | **Retired.** Both refuse at exit 2 with the replacement invocation named. The checkout tasks that shared the `bundle` name are `lifecycle:*` now. |
| `ccodex libraries list\|status` | List installable external skill libraries with their front doors and surface cost, or report which are already in this home. Read-only. |
| `ccodex libraries install <name> [--yes]` | Install a named external library through **its own** front door. Dry run unless `--yes`; vendors nothing into this tree. |
| `ccodex libraries migrate <name> [--yes]` | Retire another channel's copies of the same upstream through that channel's own removal path, then install. Dry run unless `--yes`. |
| `ccodex statusline status\|activate\|deactivate` | Inspect or explicitly manage only Claude Code's `statusLine` fields. Inactive until you activate it. |
| `ccodex version` | This command's resolved repository root and runtime dependencies. |

**Help, and how to reach the wrapped tool's help.** `ccodex <verb> --help` prints that verb's own
help and **runs nothing** — no gateway, no session state, no constructed settings. To reach the
help of the tool *behind* a launch verb, end this command's options with `--`:

```bash
ccodex launch --help                # this verb's help; prepares nothing, launches nothing
ccodex launch -- --help             # `--` forwards verbatim: Claude Code's OWN help
ccodex launch -- --print "prompt"   # any Claude Code argument, through a prepared session
                                     # Claude Code's own help, only if gateway is already healthy
```

`providers` and `models` are the two exceptions: they take no options of their own, so they run
the query rather than printing a help page, and `models` exits 1 when the gateway is down because
an unanswerable query is a failure rather than a refusal.

**Exit codes**, uniform across every route: `0` ok · `1` failure or unhealthy · `2` usage ·
`3` refused, meaning a boundary declined the operation rather than failing at it.

Only the **use** surface is installed. The maintenance tasks — `test`, `validate`, `check`,
`secrets`, `self-test`, `mermaid:*`, `hooks:install` — are deliberately absent, because they
belong to working *on* this repository rather than to using what it installed; run those with
`mise run <task>` inside the checkout. One owned dispatcher rather than a dozen named commands
means one ownership record, one place a new verb appears, and no PATH namespace land-grab.

**A shell function or alias named `ccodex` will shadow this command.** Bash resolves functions
and aliases before `PATH`, so a leftover definition silently wins and the installed dispatcher is
never reached — the symptom is `ccodex --help` printing the *wrapped tool's* help instead of the
table above. Diagnose with `type ccodex`: it must report a **file** inside a distribution tree's own
`bin/` — a checkout's, or mise's install directory for the version you selected — not a function, not
an alias, and not a leftover copy in `${XDG_BIN_HOME:-$HOME/.local/bin}` from the retired PATH plane.
`which ccodex` is not enough, since it reports the file that a function is hiding.

### Adding a provider that needs an API key

`provider add` writes the provider's SHAPE only. **It accepts no key** — it has no `--api-key`
flag and it does not read stdin, so a key piped to it is silently dropped and every later request
fails with `401 invalid_api_key` while the routing itself looks correct in the attribution log.
The key goes in through a different verb, and the gateway must already be running **and already
restarted** when it does. Measured end to end in one clean run on 2026-08-23:

```bash
# 1. the gateway must be UP: add-key stores nothing against a stopped proxy.
ccodex ensure

# 2. the provider's shape. No key here.
ccodex configure provider add muse --adapter openai-responses \
  --base-url https://api.meta.ai/v1 --default-model muse-spark-1.2

# 3. the PUBLISH step: step 2 wrote the config file, not the running catalog.
ccodex restart

# 4. the key, read only from piped stdin -- never argv, which `ps` exposes host-wide.
printf '%s' "$YOUR_KEY" | ccodex configure account add-key muse --label my-key

# 5. confirm, then use it.
ccodex providers                    # muse should be LIVE, not merely configured
ccodex launch --model muse/muse-spark-1.2
```

After step 4 the provider carries `apiKey` and `apiKeyPool`; before it, only `adapter`, `baseUrl`,
and `defaultModel`. Check with `ccodex providers` rather than trusting step 2's success message.

**Step 3 is the publish step, and step 4 is why it has to come first.** `add-key` validates the
provider against what the RUNNING gateway serves rather than against the config file, so run
between steps 2 and 3 it fails `Error: unknown provider` for a provider `ccodex providers` listed
as configured one command earlier, and against a stopped proxy it fails `Proxy not reachable`.
Measured 2026-08-23 in one container with no Codex installed and `ocx sync` never run: the restart
alone took the live catalog from 7 ids serving none of the new provider to 420 serving 413 of it,
and the key then stored on the first try. Both constraints are upstream opencodex behavior,
reproduced against the raw pinned binary, and neither is in the configure help — the message says
`unknown provider` where it means *configured but not yet published*. What `add-key` needs is a
running gateway whose catalog already includes the provider, and a restart after the provider add
is the only order that guarantees it. The older 2026-08-08 sequence in
[`docs/research/2026-08-08-fresh-host-install-verification.md`](docs/research/2026-08-08-fresh-host-install-verification.md)
stored a key before its restart because its `ensure` came after the provider add and started a
gateway that was down — a cold start rather than a stale one.

Two upstream notes. `ocx sync` — which the post-mutation notice names — needs Codex installed and
reports `Codex config not found` on a host without it; the gateway still starts and still routes,
so the restart path above is sufficient. And `ocx ensure` may log
`Provider model discovery ... failed with HTTP 401 [fallback=configured]` for a key-authenticated
provider: that is ocx not sending the key on its discovery probe, it is harmless, and the
configured model id is used instead.

**Reasoning models need a real token budget.** `muse-spark-1.2` spent 163 of 176 completion tokens
on reasoning for a two-word answer. A `max_tokens` that looks generous for the visible reply
returns `content: null` with `finish_reason: "length"` — which reads exactly like a broken
credential and is not. Size the budget for the reasoning trace.

**A distribution tree is required, and `ccodex` resolves every tool through that tree's own pins.**
`ccodex` is a thin entry point rather than a self-contained copy: its launcher and Python lifecycle
scripts live in the tree beside it, and it resolves `ocx`, `jq`, `uv`, and the pinned CPython through
that tree's reviewed `mise.toml` + `mise.lock` at the moment a verb needs them. Nothing is bound at
install time any more — the retired operator-tools plane was the only thing that ever rendered
absolute tool paths into a dispatcher, and deleting it removed both the binding step and the refresh
step that kept it current. The consequence to know is that the pinned toolchain must be resolvable
in the tree the dispatcher located, and `mise` itself is found on `PATH` because it is this
repository's documented sole bootstrap prerequisite. Launch and Ultracode start Claude Code in the
caller's physical current workspace; the tree selects code and runtime identity, not the project
Claude works on. `$AGENTIC_SDLC_OCX` and `$AGENTIC_SDLC_JQ` remain caller-supplied exact-absolute
overrides, admitted only as absolute paths, and nothing shipped here sets either.

No shell startup file or PATH value is edited. Every launch route delegates to
`scripts/opencodex-claude.sh`, so identity-checked supervision and the route-integrity
refusals remain mandatory, including validation of every explicit `--settings` value before the
gateway starts. The ADR-0005 credential refusal, the environment scrub, and the
separate Claude config dir are GONE from this launcher (ADR-0014): it uses your own `~/.claude`
so Claude Code can present its existing login to the gateway. `ccodex ultracode` refuses a
competing `--settings`; on either launch form, `--yolo` refuses competing permission controls.
Launch/resta

More