From ork
Automates nightly CI failure classification across open PRs using /ci-debug, posts verdicts as collapsed comments, and logs to a ledger. Never auto-pushes fixes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ork:ci-sentinel [install|status|enable|disable][install|status|enable|disable]sonnet.github/workflows/ci-sentinel.yml.sentinel/**This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Direct response to the 275-session insights audit (2026-05-16): 14 ci-debugging + 7 fix-ci-failures sessions in one month, most of them re-running the same 10-pattern classification you already encoded in `/ci-debug`. This skill makes the classifier autonomous.
Direct response to the 275-session insights audit (2026-05-16): 14 ci-debugging + 7 fix-ci-failures sessions in one month, most of them re-running the same 10-pattern classification you already encoded in /ci-debug. This skill makes the classifier autonomous.
⏰ daily cron (08:17 UTC)
│
▼
📥 gh pr list → PRs with FAILURE checks (yours, max 10)
│
▼
🤖 for each PR (skipping those already commented at this SHA):
claude -p → run /ci-debug → capture verdict markdown
│
▼
💬 post collapsed PR comment with marker so future runs dedupe
│
▼
📜 append { ts, pr, sha, tokens } to .sentinel/ledger.jsonl
│
▼
💰 if daily token spend > ORK_SENTINEL_DAILY_TOKEN_BUDGET → pause
🆕 flag in the comment; you find them on your normal status sweep, not via a notification storm./status is for.Read("${CLAUDE_PLUGIN_ROOT}/skills/shared/rules/untrusted-input-quarantine.md"), the classifier reads them read-only and extracts the failure class as structured facts; the propose-don't-apply design (no auto-push) already keeps the actor away from the raw bytes — quarantine makes that explicit, and deterministic signals (exit codes, test output) bypass the reader as ground truth.| Risk | Mitigation |
|---|---|
| Token cost runaway | ORK_SENTINEL_DAILY_TOKEN_BUDGET=1000000 ceiling, enforced by the workflow's first step. Resets daily. |
| Duplicate comments on the same SHA | Marker <!-- ork:ci-sentinel sha=<short> --> on every comment; workflow scans existing comments before posting. |
| Wrong-classification spam | Propose-don't-apply means the worst outcome is a noisy but accurate-looking comment. You can collapse them; you can't unmerge a bad auto-fix. |
| Stuck PR keeps re-classifying | Idempotent on SHA — only re-runs if you push new commits. |
| Sentinel itself breaking CI | Runs on ubuntu-latest, no pull_request trigger, no push trigger. Cannot block any other workflow. |
.github/workflows/ci-sentinel.yml from the OrchestKit repo into the target repo (this skill ships it).claude setup-token, then add it as the CLAUDE_CODE_OAUTH_TOKEN secret: gh secret set CLAUDE_CODE_OAUTH_TOKEN -R <owner>/<repo>. The workflow reads this natively from job-level env; ANTHROPIC_API_KEY is not used any more, and setting it alone leaves the run red: the workflow's auth canary hard-fails when CLAUDE_CODE_OAUTH_TOKEN is unset or expired.ORK_SENTINEL_DAILY_TOKEN_BUDGET env in the workflow.inputs.dry_run = true to validate the wiring.Rotate the token the same way when the canary reports a 401: claude setup-token, then re-run gh secret set.
If you run the sentinel locally via claude --bg instead of the workflow:
Pin it (CC 2.1.147+): Press
Ctrl+Tinclaude agentsto pin the session. Pinned background sessions stay alive when idle (no silent reaping between runs), restart in place to apply CC updates rather than dying, and under memory pressure are shed only after non-pinned sessions.
Resume it (CC 2.1.144+): Sessions started via
claude --bgnow appear in/resumemarkedbg— recover a crashed sentinel directly through/resumeinstead of the agent view.
The workflow is intentionally configured via in-file env vars (not workflow inputs) so a fork stays self-contained:
| Var | Default | Meaning |
|---|---|---|
ORK_SENTINEL_DAILY_TOKEN_BUDGET | 1000000 | Hard daily ceiling. Hour-of-day not enforced; calendar day in UTC. Bumped from 500k after dropping --bare (see "Why no --bare" below). |
ORK_SENTINEL_PER_PR_TIMEOUT_S | 300 | Per-PR wall-clock cap on the claude -p invocation. |
max_prs (workflow_dispatch input) | 10 | Cap on PRs analyzed in one sweep. |
dry_run (workflow_dispatch input) | false | Skip comment posting (for spec validation). |
Originally designed around claude -p --bare (CC 2.1.81+) for minimal plugin/hook load and predictable ~4k tokens/PR. First real dispatch revealed --bare doesn't honor ANTHROPIC_API_KEY env var, --settings.apiKey, or --settings.apiKeyHelper — every call returns "Not logged in · Please run /login". Reproduced locally against multiple settings shapes.
Dropped --bare; cost per PR rises ~4k → ~10k tokens (plugins + hooks load), partially offset by --no-session-persistence (avoids disk writes). Daily budget bumped 500k → 1M to absorb the change. (That budget bump predates the 2026-07 move to a daily cron and Max-plan OAuth auth; at the current cadence the ceiling is pure headroom, see "Cost model" below.)
If/when CC fixes --bare auth, the workflow can revert to bare mode by changing one line.
Each claude -p invocation locks the dispatch envelope so cost-per-PR stays predictable regardless of what the runner inherits:
| Flag | Value | Why |
|---|---|---|
--permission-mode | acceptEdits | The headless "use tools without prompting" mode. /ci-debug needs Bash (gh pr checks, gh api ...logs) to read the failure. dontAsk was the original value but it silently REFUSES permission-requiring tools, so every analysis came back empty (M146-7, #1862 Bug C). Never use bypassPermissions here. |
--max-turns | 4 | Cap on the conversation length. Sweep, classify, report — done. |
--output-format | json | Ledger needs usage.total_tokens for the budget circuit-breaker. |
--no-session-persistence | (flag) | Don't write session state to disk; sentinel runs are ephemeral. |
⚠️
acceptEditsis edit-capable. The permission mode is NOT what keeps the sentinel propose-don't-apply. Be precise about which control does what:
- The real structural control is
permissions: contents: readat the top ofci-sentinel.yml. TheGITHUB_TOKENthe job runs under simply cannot write repo contents, so agit push, a branch update, or agh pr mergeis rejected by GitHub's API regardless of what the model tries. That is enforcement, not convention. It is also why local edits the model makes to the runner checkout go nowhere: not because the runner is ephemeral, but because nothing can push them.- The model CAN still write, and those writes persist. The job sets
GH_TOKENand grantspull-requests: write+issues: write, and the prompt tells the model to use theghCLI. AnacceptEditsmodel can therefore post, edit, or delete comments, edit PR/issue titles and bodies, add labels, and close or reopen PRs and issues. None of that is undone when the job ends. Treat GitHub-conversation state as writable blast radius.- Prompt wording is a convention, not a control. The dispatch prompt is
Run /ci-debug on PR N ... Use only the gh CLI. Output the report markdown only, and/ci-debugis specified to propose and never apply. That is what keeps the model from using its write scope destructively, but it is unenforced, and CI logs and PR bodies are untrusted input.Consequences: keep
contents: read. Widening it tocontents: write(or adding a git write step plus the matching permission) is the change that actually converts this into an auto-fix bot, and it demotes prompt wording to the sole control. Narrowingpull-requests/issuestoreadwould shrink the remaining blast radius, at the cost of the verdict comment the sentinel exists to post. Never usebypassPermissionshere.
These are hardcoded in the workflow. If you need to override for a fork (e.g. you want a different permission-mode), edit .github/workflows/ci-sentinel.yml directly — intentionally not exposed as workflow_dispatch inputs to prevent accidental cost spikes from a one-off manual run.
Every verdict comment looks like:
<!-- ork:ci-sentinel sha=abc123def -->
<details><summary>🛰️ <b>CI Sentinel verdict</b> (sha abc123def)</summary>
## CI Debug: <repo> · #<n>
**Failing job:** ...
**Classification:** Pattern #N — <name>
**Reference:** memory <file.md>
**Proposed fix:** ...
</details>
Collapsed by default — no inbox noise unless you click. Always carries the SHA so you know whether the verdict is still current.
The insights audit's horizon-#1 design called for a CI_PLAYBOOK.md the sentinel mutates after each human-driven novel-failure fix. v1 doesn't write to the playbook — it just appends classification rows to .sentinel/ledger.jsonl. The playbook lives in /ci-debug's SKILL.md and stays human-curated for v1.
When v1.1 lands the journal:
/ci-debug SKILL.md.Per-PR analysis: ~8-12k tokens (full CC load — plugins + hooks — since --bare was dropped, see "Why no --bare" above).
One daily sweep with avg 3 failing PRs: ~30k tokens/day. A bad day at the max_prs=10 cap is ~120k tokens.
Daily budget 1M tokens = roughly 30x headroom on a typical day and ~8x at the cap. At this cadence the ceiling is a runaway-loop guard (a stuck retry loop), not a spend cap you will ever approach normally.
Dollar cost: the workflow authenticates with a Max-plan OAuth token, so a sweep draws on plan quota rather than metered credits, with no incremental invoice line. If you swap a fork back to a metered ANTHROPIC_API_KEY, ~30k tokens/day at ~$15/MTok (Sonnet input/output blended) is roughly $12-15/month per repo. Either way, against 21 manual ci-debug sessions/month at 10-20 minutes each, payback is immediate.
The old figures in this section ("~30k tokens/hour", "~720k/day") were derived from the original hourly cron. The workflow was throttled to daily in 2026-07 (
cron: "17 8 * * *") alongside the OAuth migration, since an hourly sweep would draw on the same interactive Max-plan quota.
/ci-debug does the classification (sentinel is the trigger)./ork:swarm-migrate (after sentinel diagnoses, you might fix-and-swarm across repos), /status (sentinel doesn't replace org-wide sweeps)./loop /ci-debug in your own session is the manual analog; ship the sentinel and unplug from that./ork:ci-sentinel install — copy the workflow into a new repo + check secrets./ork:ci-sentinel status — read .sentinel/ledger.jsonl and summarize the last 24h./ork:ci-sentinel enable / disable — toggle the workflow's on.schedule block.The daily cron run itself does not invoke this skill — the workflow calls claude -p against /ci-debug directly (headless mode — not --bare, which was dropped over the auth failure documented in "Why no --bare" above). This skill is for the human admin actions around the sentinel.
CC 2.1.183 hardens propose-don't-apply: Scheduled-task and webhook trigger deliveries now classify as task notifications, not keyboard input — so a delivery can no longer approve a pending action or set the session title in auto mode. If you ever run the sentinel inside a live auto-mode session (rather than the headless
-pcron), a triggered re-run can no longer auto-approve a fix prompt. That closes the trigger-delivery vector at the harness layer. It does not change the headless cron path: there, the enforced control ispermissions: contents: read(no push or merge is possible), while the model still holdspull-requests: write+issues: writeand is held to propose-don't-apply by prompt wording alone. See the dispatch-envelope note above.
You already have 10 encoded CI failure patterns in /ci-debug and 632 ScheduleWakeup invocations in your history — you trust async, and you trust pattern-matched classifications when they cite the memory entry. The CI sentinel is composition, not invention: a GitHub Actions cron triggers headless /ci-debug against every open red PR, posts a collapsed verdict, and respects propose-don't-apply. Worst case is a noisy comment. Best case is 21 sessions/month of recurring CI archaeology reclaimed. Build effort: hours. Payback: immediate.
npx claudepluginhub yonatangross/orchestkit --plugin orkGeneric CI environment rules for GitHub Actions workflows. Use when operating in CI — covers security, CI monitoring, comment formatting, and investigating session logs from other runs.
Integrates Claude Code into CI/CD pipelines using GitHub Actions, GitLab CI, pre-commit hooks for automated PR reviews, code generation, test validation, security scanning, headless mode, and cost control.
Diagnoses a failing CI run against an 11-pattern playbook, classifies the failure, cites relevant memory, and proposes the exact fix command without applying it.