Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Until

ASecurity

Use when a session's stopping condition is being written or repaired — \"set the stopping condition\", \"hold it until the tests pass\", \"what should the objective be\", \"give it something to work toward before I go to bed\", \"write the brief for the spawned agent\", \"it stopped overnight\", \"it idled while I was asleep\", \"it asked me a question instead of continuing\", \"why did it stop\", \"is the hold actually armed\", \"make it keep working\", \"run this unattended\", \"leave it ru...

21 stars
0 votes
0 copies
0 views
Added 9/19/2026
toolsgoshellbashgit

Works with

terminalcli

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add edwinhu/workflows --skill until --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Until?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Until
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/edwinhu-until/badge)](https://www.skillsdirectory.com/skills/edwinhu-until)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: until
description: "Use when a session's stopping condition is being written or repaired — \"set the stopping condition\", \"hold it until the tests pass\", \"what should the objective be\", \"give it something to work toward before I go to bed\", \"write the brief for the spawned agent\", \"it stopped overnight\", \"it idled while I was asleep\", \"it asked me a question instead of continuing\", \"why did it stop\", \"is the hold actually armed\", \"make it keep working\", \"run this unattended\", \"leave it running overnight\". Use proactively BEFORE handing work to any session that will outlive the user's attention — a spawned agent, a background job, a craft dispatch left running, or this session at night. NEGATIVE ROUTING: composing a craft run's own stopping condition is work-dispatch.sh and needs no help; spawning the session is agent-spawn; delegating a task to a subagent is farm-out. This skill owns the CHECK COMMAND that settles it, the standing authority that travels with it, and the proof that both mechanisms are live."
allowed-tools: [Bash, Read, Edit, Write, Grep, Glob]
---

# until — a hold that runs a check, and a heartbeat that keeps asking

**What this skill carries** — grep `references/` for any subject the names below miss:
!`d=${CLAUDE_SKILL_DIR}; command -v skill-toc >/dev/null 2>&1 && exec skill-toc "$d"; s=$HOME/.claude/skills/plugin-utils/bin/skill-toc; [ -x "$s" ] && exec "$s" "$d"; echo "(skill-toc unavailable: references and scripts are NOT listed here — install the plugin-utils plugin, or start a new session so its bin/ reaches PATH)"`

Two mechanisms, neither with a transport, so neither can fail to be delivered.

**HOLD** — `until-arm.sh` writes a session-scoped state file; `hooks/until.ts` runs the check on
every Stop and blocks while it exits non-zero. It decides whether to continue.

**HEARTBEAT** — `CronCreate`, a model tool called directly. It guarantees something asks once the
session has gone quiet, which no Stop hook reaches: nothing fires when no turn is running.

An unattended run needs both.

## Arm the hold

```bash
A=${CLAUDE_SKILL_DIR}/scripts/until-arm.sh
bash $A '<the CHECK command>' --rounds 8 --minutes 720
bash $A --status     # armed? prints the check and the ceilings
bash $A --disarm     # release early
```

Pass the check as **one single-quoted argument, with no apostrophes in it** — an apostrophe ends the
quote, and double quotes would hand every backticked fragment to the shell to run first. Write "the
exemption in vendor-lint.sh", not "vendor-lint.sh's exemption".

The hold is **self-clearing**: the hook removes the state file the moment the check exits 0, and
again when either ceiling is reached — that release says UNMET, and saying so is the session's job.

## The CHECK COMMAND

The check is the whole objective. There is no separate end state to write down: a sentence and a
command disagree eventually, and only one of them runs.

- **It must be RED when armed.** `until-arm.sh` refuses a check that already exits 0 — a hold on a
  passing check holds nothing and teaches the session that the block is noise.
- **It must be able to run.** Exit above 1 is could-not-run, not a verdict, and is refused too;
  arming on a broken command would hold the session forever on a typo.
- **One clause per claim.** `bun test x.test.ts && bash measure.sh --rate-below 0.01` — each half
  fails on its own terms, and the failure names which.
- **It must run in that session's own cwd.** A check whose paths live in another repo can never be
  met where it runs, only released unmet.
- **No milestone.** `test -f report.md` is true while the objective is unmet. Name what the WORK
  reaches: a suite passing, a rate under a number, a count at zero.

## The escapes are flags, not sentences

`--rounds N` stops a *losing* run; `--minutes M` stops a *stuck* one. The hook counts and clocks
both, so neither is prose the session can re-adjudicate away. Defaults are 8 and 720; the clock has
to outlast `rounds × a round`.

## The CRON PROMPT — the text that re-enters the session

`CronCreate` takes a prompt, and that prompt is the only thing present when a tick fires into a quiet
session. The authority, the continuation rule and the terminal blockers live in it.

```
Run `<CHECK>` and report its exit code — judge from the command, not from the conversation. If it
fails, take the next action now rather than proposing it. If it passes, spend the remaining budget:
hunt for work the check does not cover — an ungated checker, a suite nothing runs, a vendored copy,
a count that has drifted — fix the largest one and say in one line why you picked it. Standing
authority: <commit / push / pick the next scope / re-dispatch> without asking. The only terminal
blockers are <a missing credential, a dead network, an irreversible or outward-facing action>;
everything else is the next task, difficulty included. When the budget is spent, end this heartbeat
with CronDelete.
```

**Enumerate the terminal blockers.** The list is what keeps a hard problem from being filed as a
blocker. "When done or blocked, notify" produced a 5-hour idle: the session hit a fixture it could
not cut cleanly and, having no list, called the difficulty a blocker.

Lint it before creating the cron:

```bash
bun ${CLAUDE_SKILL_DIR}/scripts/cron-prompt-lint.ts "<the prompt>"
bun ${CLAUDE_SKILL_DIR}/scripts/cron-prompt-lint.ts --file BRIEF.md
```

Exit 0 clean, 1 findings, 2 usage. Eleven rules, all decidable from the string. Fix every critical
and major. It settles what an exit code can settle and renders no opinion on whether the objective
is *right*.

## Prove both are live

`bash ${CLAUDE_SKILL_DIR}/scripts/until-arm.sh --status` settles the hold. **`CronList` — the tool,
not a command — is the only thing that proves the heartbeat exists.** Call it. A cron you remember
creating is not a job; measured 2026-09-16, a creation reported success while `CronList` showed none.

## Two teardowns

| | |
|---|---|
| the hold | self-clears when the check exits 0 or a ceiling is hit; `--disarm` ends it early |
| the cron | `CronDelete`, a model tool with no CLI. Nothing else can end it — a session cron lives in memory, and no hook event fires when the work completes — so the last sentence of the cron prompt is the only thing present at the moment it should stop |

<EXTREMELY-IMPORTANT>
## IRON LAW: NO OBJECTIVE A FINISHED STEP CAN SATISFY

**An objective names the state the WORK reaches, never the event on the way there.**

"craft has returned a verdict", "the recon report exists", "BRIEF.md has been carried out", "the
agent has reported back" — each is true while the objective is still unmet. The moment it closes the
session stops, and at 02:00 the work stops for the night. `craft has returned a verdict` closed on
`overallPass=false` with 0 of 5 tasks done and 20 blocking findings; craft's own loop is FAIL → fix
→ re-run, and calling FAIL "done" stops the loop that was going to fix it.
</EXTREMELY-IMPORTANT>

<EXTREMELY-IMPORTANT>
## IRON LAW: NO UNATTENDED RUN WITHOUT STANDING AUTHORITY

**Every decision left open becomes a question asked into an empty room.**

If the session may commit, push, pick round-2 scope, choose between two branches it named itself, or
spend the rest of its round budget — the cron prompt says so, in one sentence, up front.
</EXTREMELY-IMPORTANT>

## The continuation rule

**When a sub-run returns and the check still fails, take the next action. Do not propose it.**

Every measured stall happened at a moment of legitimate completion — a verdict landed, a brief
finished, a recon report arrived. That is the moment with the most information the session will ever
have, and the next action is the one it just finished naming. If two branches are genuinely open,
pick one, say why in a clause, do it. A menu offered at 02:00 is a five-hour pause with extra steps.

## Red flags — STOP

| About to | Why wrong | Do instead |
|---|---|---|
| Self-send anything — a prompt, a command, a stopping condition — from a session that will not go idle | a typed transport needs an idle pane and a session working back-to-back never offers one; the send exits 0 and nothing lands | `until-arm.sh` writes a file, `CronCreate` is a tool call: both land inside the turn |
| Report a heartbeat as armed because you created one | 29 of 81 heartbeats raised on 2026-09-16 never armed, and nothing told the session | `CronList` settles it — no job, no heartbeat |
| Arm a check that already exits 0 | it holds nothing and teaches the session the hold is noise | `until-arm.sh` refuses it; pick the check that is red now |
| Arm a check whose paths live in a different repo | it can never be met where it runs, only released unmet | the check must run in that session's own cwd |
| Put an apostrophe in the check | it ends the single quote, and double quotes run every backticked fragment first | write the word without it |
| Arm a hold on YOUR OWN session to try the hook out | it then blocks your own stop until the check passes | read `tests/until.test.ts`, or arm a check you can satisfy on demand |
| Treat a stopping condition you wrote down as binding on yourself | prose is re-adjudicated away; only the hook blocks a stop. Measured 2026-09-02: a session wrote "I'm treating that as binding regardless" and idled three hours later | arm it, and confirm with `--status` |
| Restate `--rounds` or `--minutes` as prose in the prompt | two ceilings that can disagree, and the prose one is the bug | the flags; the hook counts and clocks them |
| Write "has returned a verdict" / "the report exists" as the check | milestone: true while the objective is unmet | a suite passing, a rate under a number, a count at zero |
| Leave a session running overnight on the hold alone | nothing in a Stop hook runs once the session is quiet | `CronCreate` a heartbeat; `CronDelete` when the work is done |
| End a turn with a question mark under an armed hold | at 02:00 that is a five-hour pause | answer it in one line and act |
| Write "when done or blocked, notify and stop" | every difficulty becomes terminal | enumerate the terminal blockers; the rest is the next task |
| Hold a green commit "because the user is asleep" | the authority clause should have pre-authorized it; the commit is reversible, the silence is not | commit, and say so in the report |
| Put "and the user has approved" in the objective | a session cannot close it by working — measured 18h | review after the hold releases, as a step it performs |
| Write "or stop after N turns" | nothing in the harness counts turns | `--rounds`, which the hook counts |
| Report "N of M rounds used" and stop at N | the budget was the authorization, not a ceiling on ambition | spend it, or say why the remainder is unusable |

## References

- `references/templates.md` — the check-command and cron-prompt templates, the unattended-brief
  template, and three real stalls rewritten side by side.
- `scripts/until-arm.sh`, `../../hooks/until.ts` — the arm and the hold. The hook's header records
  the three properties that make a Stop hook safe rather than a trap.

Attribution

edwinhuedwinhu
View sourceMore from edwinhu →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

ucoz-landing-skill

Playbook for creating and editing uCoz landing pages via MCP tools (`templates_tool`, `ftp_tool`, `modules_tool`). Use for tasks such as: "build a landing page", "update the homepage as a landing page", "create a promo page on the homepage", "add a lead form / menu / SEO to the homepage". Homepage: `page_list`, `page_get`; first publish — `page_update` with full `page_tmpl`; HTML edits after generation — `patch_template` (module_id=2, template_id=1), not `update_template`. Activate the mail f...

107 votes

Paperclip

Interact with the Paperclip control plane API to manage tasks, coordinate with other agents, and follow company governance. Use when you need to check assignments, update task status, delegate work, post comments, set up or manage routines (recurring scheduled tasks), or call any Paperclip API endpoint. Do NOT use for the actual domain work itself (writing code, research, etc.) — only for Paperclip coordination.

798221 votes

Daw Music

Digital Audio Workstation usage, music composition, interactive music systems, and game audio implementation for immersive soundscapes.

761 votes

Instantly Rdsthomas Mission Control

Instantly.ai cold email outreach API - manage campaigns, leads, accounts, and analytics. Use for cold email automation, lead management, campaign creation/monitoring, and email account warmup.

761 votes

Caveman Compress

Compress natural language memory files (CLAUDE.md, todos, preferences) into caveman format to save input tokens. Preserves all technical substance, code, URLs, and structure. Compressed version overwrites the original file. Human-readable backup saved as FILE.original.md. Trigger: /caveman-compress FILEPATH or "compress memory file"

1023330 votes
View all in tools →