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/releaseexpecting 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)
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_releasehandler 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):
- GATE: Validate gate attestation (if provided) or proceed automatically
- BUMP: For each repo — infer or apply version bump; update
pyproject.toml+__version__ - PIN: Update cross-repo dependency pins in downstream repos
- PR: Create release PR per repo via
gh pr create; enable auto-merge - MERGE: Wait for CI + merge queue; confirm merged
- TAG:
git tag v{version}+ push; trigger PyPI publish workflow - WAIT: Poll PyPI for package availability (unless
--skip-pypi-wait) - 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-runproduces 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
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