# Release

> Org-wide coordinated release pipeline (STRUCTURAL PLACEHOLDER — node_release performs no git/gh/PyPI I/O today; full handler implementation is pending). Target design bumps versions, pins cross-repo deps, creates PRs, merges, tags, and triggers PyPI publish across all OmniNode repos in dependency-tier order

- Skill: `omninode-ai/release` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add omninode-ai/release`
- Raw SKILL.md: https://api.skillmd.com/api/skills/omninode-ai/release/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: OmniNode-ai (https://skillmd.com/u/omninode-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/omninode-ai/release

---


# Release

**Announce at start:** "I'm using the release skill."

> ## ⚠️ Implementation status: STRUCTURAL PLACEHOLDER
>
> **The routed node (`node_release`) performs NO git, `gh`, `uv`, or PyPI I/O today.**
> It is a pure in-memory FSM bookkeeper: it transitions through named phases and
> emits transition/completion events, but it never bumps a version, opens a PR,
> pushes a tag, or publishes a package. The caller tells the FSM whether each phase
> "succeeded"; the node does not execute the phase.
>
> Everything in the **"Target design (NOT YET IMPLEMENTED)"** section below — the
> 14-phase pipeline, the per-repo `gh pr create` / `git tag` / PyPI-publish steps,
> the idempotency dedup table, and crash-safe state-file resume — describes the
> intended future behavior. **None of it runs today.** Do not invoke `/release`
> expecting it to cut a real release.
>
> Full handler implementation is pending (the placeholder-origin work should not be reopened). A honesty reconciliation pass aligned this documentation with the actual implementation state.

## Usage

```
/release omniclaude omnibase_core        # (placeholder) parse args, run FSM only
/release --all --bump patch              # (placeholder) parse args, run FSM only
/release --dry-run                       # (placeholder) parse args, run FSM only
/release --resume <run_id>               # (placeholder) resume flag is parsed but no state file is persisted
/release --gate-attestation <token>      # (placeholder) token is parsed only
```

> All flags are parsed by the node CLI (`__main__.py`) and threaded into the start
> command, but no flag currently produces a release side effect.

## What actually executes today

### Step 1 — Parse arguments

The node CLI (`omnimarket.nodes.node_release.__main__`) parses `repos`, `--all`,
`--bump`, `--dry-run`, `--resume`, `--skip-pypi-wait`, `--autonomous`, and
`--gate-attestation`, builds a `ModelReleaseStartCommand`, and prints it as JSON.
This supports contract verification only; it scans no repository and reads no commits.

### Step 2 — Initialize node (contract verification)

```bash
onex run-node node_release \
  --input '{"repos": [], "bump": null, "dry_run": false, "autonomous": false}' \
  --timeout 300
```

On non-zero exit, a `SkillRoutingError` JSON envelope is returned — surface it
directly, do not produce prose. The handler is a structural placeholder; full
migration is pending.

### Step 3 — Run the placeholder FSM

`HandlerRelease` (`omnimarket/.../node_release/handlers/handler_release.py`) is a
**pure-logic FSM with no external I/O** (its own docstring: "Pure logic — no
external I/O"). It exposes `start()`, `advance()`, `run_full_pipeline()`, and
`handle()`. `run_full_pipeline()` walks the phase sequence below, emitting a
`ModelReleasePhaseEvent` per transition and a `ModelReleaseCompletedEvent` at the
end. Whether a phase "succeeds" is supplied by the caller via `phase_success` /
`phase_results` — the handler does **not** bump versions, create PRs, tag, or
publish.

**Actual phase enum (`EnumReleasePhase`, 9 states):**

```
IDLE → BUMP_VERSIONS → PIN_CROSS_REPO → CREATE_PRS → MERGE → TAG → PUBLISH → DONE
                                                                              (FAILED)
```

These are **labels only** — advancing to `TAG` does not push a git tag; advancing
to `PUBLISH` does not publish to PyPI. The handler also implements a circuit
breaker: `max_consecutive_failures` (default 3) consecutive failures transition the
FSM to `FAILED`. Per-phase repo metrics (`repos_succeeded` / `repos_failed` /
`repos_skipped`) are accumulated from caller-supplied counts, not computed from
real release outcomes.

### Step 4 — Report

The completion event carries `final_phase` and the accumulated repo counts. There
is no release table, no PR/tag/PyPI status, and no `ModelSkillResult` file written
by the node today.

## Models that exist today

| Model | Purpose |
|-------|---------|
| `EnumReleasePhase` | 9-state FSM enum (see above) |
| `ModelReleaseCommand` / `ModelReleaseStartCommand` | Start command DTO |
| `ModelReleaseState` | Frozen FSM state (current phase, repos, counts, circuit-breaker) |
| `ModelReleasePhaseEvent` | Emitted per phase transition |
| `ModelReleaseCompletedEvent` | Emitted on terminal phase |

Contract: `omnimarket/src/omnimarket/nodes/node_release/contract.yaml`
(subscribe `onex.cmd.omnimarket.release-start.v1`, publish
`onex.evt.omnimarket.release-completed.v1`).

## Architecture

```
SKILL.md   -> thin shell (this file)
node       -> omnimarket/src/omnimarket/nodes/node_release/ (STRUCTURAL PLACEHOLDER — pure FSM, no I/O)
contract   -> node_release/contract.yaml
```

---

# Target design (NOT YET IMPLEMENTED)

> **Everything below this line describes intended future behavior and does NOT run
> today.** The routed `node_release` handler performs none of these git / `gh` /
> `uv` / PyPI operations. This section is preserved as the design target for the
> the migration target. Treat every "create PR", "push tag", "publish", "write state
> file", and "dedup" statement below as a specification, not a description of
> current behavior.

## Target execution — release phases

The target pipeline processes repos in dependency-tier order (tier 0 → tier N):

1. **GATE**: Validate gate attestation (if provided) or proceed automatically
2. **BUMP**: For each repo — infer or apply version bump; update `pyproject.toml` + `__version__` <!-- skill-boundary-ok: repo iteration is performed by node_release, this skill only dispatches -->
3. **PIN**: Update cross-repo dependency pins in downstream repos
4. **PR**: Create release PR per repo via `gh pr create`; enable auto-merge
5. **MERGE**: Wait for CI + merge queue; confirm merged
6. **TAG**: `git tag v{version}` + push; trigger PyPI publish workflow
7. **WAIT**: Poll PyPI for package availability (unless `--skip-pypi-wait`)
8. **VERIFY**: Confirm installed version matches released version

Target reporting: display a release table (repo, old version, new version, PR, tag,
PyPI status) and write `ModelSkillResult` to
`$ONEX_STATE_DIR/skill-results/{context_id}/release.json`.

## Target safety

- Proceeds automatically — no Slack approval gate
- `--dry-run` produces zero side effects: no bumps, PRs, tags, or PyPI triggers
- Resume support: state written after each phase; `--resume <run_id>` skips completed phases
- Cross-repo dependency pins use exact ==X.Y.Z format for determinism (exact pin policy)

## Target dependency graph

Repos are released in dependency-tier order to guarantee downstream consumers get updated pins:

| Tier | Repos |
|------|-------|
| Tier 1 | omnibase_compat |
| Tier 2 | omnibase_core |
| Tier 3 | omnibase_spi |
| Tier 4 | omnibase_infra |
| Tier 5 | omniclaude, omniintelligence, omnimemory, omnimarket |
| Tier 6 | omninode_infra, omnidash |

Tier N+1 repos pin the released version of Tier N repos. If a Tier 2 release fails,
Tiers 3 through 6 are BLOCKED.

## Target error table

| Error Code | Condition | Behavior |
|------------|-----------|----------|
| GRAPH_DRIFT | Dependency graph differs from last snapshot | Abort and report |
| NOTHING_TO_RELEASE | No version bump inferred from commits | Skip repo (not an error) |
| LINT_FAILED | ruff/mypy CI gate fails | TIER_BLOCKED for downstream |
| PYPI_TIMEOUT | Package not available on PyPI after timeout | Mark as PARTIAL |
| TIER_BLOCKED | Upstream tier failed | Skip repo, continue with others |
| GATE_REJECTED | Gate attestation invalid | Abort entire release |

## Target ModelSkillResult

```python
class ModelSkillResult:
    status: Literal["SUCCESS", "PARTIAL", "FAILED", "DRY_RUN"]
    repos_succeeded: list[str]
    repos_failed: list[str]
    run_id: str
```

## Target phase state machine

> **NOT IMPLEMENTED.** This 14-phase per-repo state machine is the migration
> target; it does not match the 9-state FSM that runs today (see "Actual phase
> enum" above). The target FSM intends each repo to progress through:

```
PLANNED → WORKTREE → BUMPED → PINNED → CHANGELOG → LOCKED
       → LINT → COMMITTED → PUSHED → PR_CREATED → MERGED
       → TAGGED → PUBLISHED → DONE
```

Target phases: PLANNED, WORKTREE, BUMPED, PINNED, CHANGELOG, LOCKED, LINT,
COMMITTED, PUSHED, PR_CREATED, MERGED, TAGGED, PUBLISHED, DONE.

The target design writes state atomically after each transition using a temp file
+ rename to guarantee crash-safe resume. **No state file is written today.**

## Target idempotency

> **NOT IMPLEMENTED.** The target design intends to deduplicate all mutations on
> resume. The handler performs none of these checks today:

| Operation | Idempotency Key |
|-----------|----------------|
| PR dedupe | Check `gh pr list --head <branch>` before creating |
| Tag dedupe | Check `git tag -l <version>` before tagging |
| Worktree reuse | Reuse existing worktree at `$ONEX_WORKTREES_ROOT/<run_id>/<repo>` |

## Target cross references

- **merge-sweep**: Used to verify merges succeeded and queues are clear
- **pr-safety**: Validates PR is mergeable (no conflicts, no blocking reviews)
- **release.yml**: GitHub Action triggered post-merge for PyPI publish
- **auto-tag-reusable**: Reusable workflow for git tag + push

