OmniNode-ai

/onex:ship_dirty_canonical — Auto-ship dirty canonical repos

Detect uncommitted files in canonical workspace repo clones and auto-ship each dirty repo to a worktree + PR. Runs node_dirty_canonical_sweep in-process via its module entrypoint (omnimarket).

OmniNode-ai 4 3 Updated 1mo ago
GitHub

Install

npx skillscat add omninode-ai/omniclaude/plugins-onex-skills-ship-dirty-canonical

Install via the SkillsCat registry.

SKILL.md

/onex:ship_dirty_canonical — Auto-ship dirty canonical repos

Skill ID: onex:ship_dirty_canonical
Version: 1.0.0
Owner: omniclaude
Backing node: omnimarket/src/omnimarket/nodes/node_dirty_canonical_sweep/


Problem it solves

Agents can edit files in canonical workspace repo clones but cannot commit there.
Those edits sit uncommitted, are invisible to CI, and are at risk during routine
sync workflows.

This skill detects dirty canonicals and rescues the edits by:

  1. Running status detection on every configured repo
  2. Creating a worktree from dev at $ONEX_WORKTREES_ROOT/auto-ship-<ts>/<repo>
  3. Copying the dirty files there and committing
  4. Pushing and opening a PR with --label auto-ship
  5. Restoring the canonical clone to clean state only after publishing succeeds

Usage

/ship-dirty-canonical
/ship-dirty-canonical --dry-run
/ship-dirty-canonical --repos omniclaude,omnimarket
/ship-dirty-canonical --dry-run --repos omnibase_core

Dispatch

The backing handler is fully synchronous in-process git/gh subprocess work
(detect → worktree → copy → commit → push → PR → restore). It runs locally via
the packaged module entrypoint — pass options as argparse flags, not a JSON
payload:

# Run from the omnimarket project so the node package is importable.
OMNIMARKET_PROJECT="${OMNI_HOME:?set OMNI_HOME}/omnimarket"  # local-path-ok: operator-workspace surface; the workspace root deliberately keeps its OMNI_HOME spelling here
uv run --project "$OMNIMARKET_PROJECT" \
  python -m omnimarket.nodes.node_dirty_canonical_sweep \
  [--dry-run] [--repos REPO ...] [--omni-home PATH] \
  [--worktrees-root PATH] [--pr-label LABEL] [--base-branch BRANCH]

Do not dispatch this node via onex run-node (the Kafka path). That
command routes over the bus and waits for a terminal event, but this node has
no live consumer, so it always fails with
SkillRoutingError "Timeout after 30s waiting for response". The local
onex node/onex run alias is pinned to --project omnibase_infra, whose
installed metadata does not register this node's onex.nodes entry point, so
it raises Unknown node as well. The in-process module entrypoint above is
the only path that resolves and runs.

The entrypoint prints ModelDirtyCanonicalSweepResult as JSON on stdout:

  • repos_checked: total repos inspected
  • repos_dirty: repos that had uncommitted changes
  • repos_shipped: repos successfully shipped to PRs
  • repos_failed: repos where ship failed
  • results: per-repo ModelDirtyRepoShipResult entries (only dirty repos)

A non-zero exit means the handler raised; surface its error output directly and do not produce prose.


Safety invariants (enforced by node handler, not this skill)

  • never_commits_to_canonical_repo — the canonical clone is restored by the backing node only after successful shipping
  • restores_canonical_to_clean_state_on_success — canonical changes remain in place if publishing the rescue branch fails
  • one_worktree_per_dirty_repo — creates at most one worktree per dirty repo per invocation

Build-loop integration

This skill is designed to be invoked by the 15-minute build-loop cron.
Wire it as a CronCreate job to run every 15 minutes:

CronCreate(interval="15m", prompt="/ship-dirty-canonical")

The build loop can also invoke it explicitly during the VERIFYING phase as a
pre-condition before dispatching new tickets.


Backing node contract

omnimarket/src/omnimarket/nodes/node_dirty_canonical_sweep/contract.yaml