SuperAPI AI Directory

goal-and-loop

General & other

View repository

Updated 10-15 21:37

About this skill

goal-and-loop — a stopping condition, and something that keeps asking

/goal decides whether to continue. /loop guarantees something asks — a cron tick the model cannot cancel, reaching the case a goal cannot: a session already gone quiet, because nothing in a goal runs when no turn is running. An unattended run needs both.

Raise it

Three commands, in this order. Do not hand-write the transport.

S=${CLAUDE_SKILL_DIR}/../craft/scripts/goal-self-send.sh
bash $S "/goal <the linted goal>"
bash $S "/loop 30m Check the goal. If it is not met, take the next action now rather than proposing it."

Pass the goal as one single-quoted argument. The template is backticked, so double quotes hand every <CHECK> to the shell to run before the script sees the string, and the goal then carries that command's output where its text should be.

Never build the command as GOAL="$(cat goal.txt)"; bash $S "/goal $GOAL". A staging file is fine for goal-lint.ts --file, but interpolating it makes a compound command that no permission rule can allowlist, so auto mode blocks the whole send.

A craft dispatch raises both for you — craft-dispatch.sh self-sends the composed goal and the loop. Do not add a second loop; two crons means two ticks.

craft-dispatch.sh does not notice. It calls goal-self-send.sh twice and treats a non-zero exit as "not fatal", so under auto mode a run proceeds with NEITHER a stopping condition NOR a heartbeat — the unattended idle this skill exists to prevent, arriving silently. A session that must raise its own goal therefore cannot run in auto mode: start it in another permission mode, or have the user type the /goal line. Either way goal-verify.sh is what settles it.

Verify it — the send is not the setting

goal-self-send.sh queues; it does not set. It exits 0 on a successful enqueue, and a detached drainer sends the line once this turn ends. So on your next turn, prove it:

bash ${CLAUDE_SKILL_DIR}/scripts/goal-verify.sh    # 0 = active (prints it), 1 = not set, 2 = can't tell

Exit 1 means no goal is set whatever the send reported. Re-send, or hand the user the /goal line and say plainly that it is not active.

Three things make a self-send land, and all are load-bearing. It must arrive when the pane is IDLE — a prompt arriving mid-turn is enqueued and parsed as literal text. It must be TYPED, not pasted — herdr agent prompt honors bracketed paste, so the drainer uses pane send-text + send-keys enter. And it must be CHUNKED: Claude Code turns any single insert over 800 characters (kre=800 in its bundle) or ~2 lines into a [Pasted text #N] block, and the slash parser requires input that literally starts with /, which a paste placeholder never does. Sub-threshold inserts concatenate exactly like typing — which is why a long goal the user TYPES works and the same goal sent in one call does not. Measured: a 1,460-char goal failed four consecutive attempts in one call and executed first try when split into three.

Screen scraping cannot substitute for goal-verify.sh: pane wait-output matches the assistant's own prose about Goal set:, and the /goal active chrome renders only in the working spinner, so an idle pane shows nothing either way.

Clear it

A closing goal does not stop the cron — that is the CronDelete tool, not the model deciding it is finished. bash $S '/goal clear' for the goal; CronDelete for the loop.

A goal 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 a goal calling FAIL "done" stops the loop that was going to fix it.

Every decision the goal leaves 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 goal says so, in one sentence, up front.

The four parts

1. END STATE the objective in its own terms, with a number someone could dispute. An adjective is a feeling.
2. CHECK the backticked command whose exit code settles it, so running it is evidence rather than a claim. A goal settled by re-reading the conversation gets reasoned out of.
3. ESCAPES a work counter AND a wall clock, both readable by the session. A counter stops a losing run, a clock stops a stuck one. The clock must outlast maxRounds × a round — craft's 6 and 720 min are the defaults to borrow.
4. AUTHORITY what it may decide alone, plus the SHORT list of what genuinely stops it — a missing credential, a dead network, an irreversible or outward-facing action. Everything not on that list is the next task, difficulty included.

Lint it before you send it

bun ${CLAUDE_SKILL_DIR}/scripts/goal-lint.ts "<the goal text>" --unattended
bun ${CLAUDE_SKILL_DIR}/scripts/goal-lint.ts --file BRIEF.md --unattended

Exit 0 clean, 1 findings, 2 usage. Twelve rules, all decidable from the string. Fix every critical and major. goal-self-send.sh runs it at the chokepoint anyway — a critical refuses the send with exit 8 and sends nothing; --no-lint overrides.

It settles what an exit code can settle and renders no opinion on whether the goal is right.

The continuation rule

When a sub-run returns and the goal is still open, 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. Under an open goal 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
Set a goal on YOUR OWN session to test the transport its Stop hook blocks idle, the clear needs idle, and only the user can break it test against a throwaway pane, never the session you are working in
Set a goal whose check paths live in a different repo it can never be met where it runs, only cleared the check must be runnable in that session's own cwd
Report "goal set" because the send exited 0 the send only queues; 4 of 4 reported success and set nothing goal-verify.sh next turn
Self-send from a session running in auto mode Tmux Self Drive denies it, and no allowlist or agent judgment clears an adversarial-pattern rule raise it in another permission mode, or have the user type the line; prove with goal-verify.sh
Add an allowlist entry to get a self-send past auto mode that is the Auto-Mode Bypass pattern — a second flagged behaviour, not a fix change the permission mode, or hand the user the /goal line
Treat a goal you wrote down as binding on yourself prose is re-adjudicated away; only the harness blocks a stop verify, or tell the user it is not active
GOAL="$(cat goal.txt)"; bash $S "/goal $GOAL" a compound command no permission rule can allowlist pass the text as one single-quoted argument
Send inline without waiting for idle lands as a queued prompt, parsed as literal text, no goal the drainer's agent wait — never remove it
Switch the drainer to herdr agent prompt it pastes, and a paste is never parsed as a slash command pane send-text + send-keys enter
Treat a delivery receipt as proof the goal is set delivery is not execution; only an executed command writes a <command-name> record goal-verify.sh
Leave a session running overnight on a goal alone nothing in a goal runs once the session is quiet add /loop 30m, CronDelete when it closes
Write "has returned a verdict" / "the report exists" milestone: true while the objective is unmet name PASS, or the number the work must reach
End a turn with a question mark under an open goal 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 goal 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 a goal a session cannot close it by working — measured 18h review after the goal, as a step it performs
Write "or stop after N turns" nothing counts turns a counter file it can cat, and a wall clock
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
Hand-write a goal for a craft run compose-goal.sh already emits a conforming one craft-dispatch.sh

References

  • references/templates.md — the goal template, the unattended-brief template, and three real goals rewritten side by side.
  • scripts/goal-verify.sh, ../craft/scripts/goal-send-drain.sh — the proof and the transport.
  • ../craft/scripts/compose-goal.sh — the reference implementation; its header records why each clause is worded as it is.