/loop-test
Local sandbox for experimenting with Claude Code's /loop command (interval + dynamic/self-paced scheduling) applied to LETS workflows. Use when the user says "loop test", "loop-test", "test the loop skill", "цикл LETS", "ScheduleWakeup experiment", or wants to design a periodic
$ npx -y skills add restarter/lets-workflow --skill loop-test --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/loop-test
Context preview
The summary Claude sees to decide when to auto-load this skill.
Local sandbox for experimenting with Claude Code's /loop command (interval + dynamic/self-paced scheduling) applied to LETS workflows. Use when the user says "loop test", "loop-test", "test the loop skill", "цикл LETS", "ScheduleWakeup experiment", or wants to design a periodic
SKILL.md
loop-test.SKILL.mdname: loop-test
description: Local sandbox for experimenting with Claude Code's /loop command (interval + dynamic/self-paced scheduling) applied to LETS workflows. Use when the user says "loop test", "loop-test", "test the loop skill", "цикл LETS", "ScheduleWakeup experiment", or wants to design a periodic /loop that polls state or babysits a long-running job. Local-only — not part of the lets plugin payload. For convergent autonomous work toward an end state, use sibling skill `goal-test` (which covers /goal).
loop-test (local experiment)
`/loop` schedules **periodic re-invocations of the same prompt**. Two modes:
| Mode | Syntax | Mechanic | |---|---|---| | **Interval** | `/loop 5m /foo` · `/loop /standup 1h` · `/loop check the deploy every 20m` | Cron entry in `.claude/scheduled_tasks.json`. Default `10m` when only `[interval]` shorthand. Survives session restart (durable). | | **Dynamic / self-paced** | `/loop check the deploy` (no interval anywhere) | Model calls `ScheduleWakeup(delaySeconds, prompt, reason)` at the end of each turn. Skip → loop ends silently. |
**`/loop` is the wrong tool for sequential workflow phases.** It repeats the *same prompt* each tick — to chain `plan → review → check`, the prompt itself has to be phase-aware (read state, advance, write back). For state-convergent autonomy, use `/goal` instead (see `goal-test` skill).
**Right tool for `/loop`:**
- Polling external state (CI runs, deploys, PR comments)
- Periodic checks (every 20m: any new bd-ready tasks?)
- Babysitting a long-running job (check progress, surface failures)
- Continuous monitoring during a long focused session
How `/loop` actually works
Reverse-engineered from `claude` binary v2.1.150.
Dynamic-mode contract
This is the mode you care about most — interval is just cron.
1. User runs `/loop <prompt>` (no interval). 2. Model does the work. 3. Before yielding, model calls `ScheduleWakeup(delaySeconds, prompt, reason)`:
- `delaySeconds` clamped to `[60, 3600]`
- `prompt` must be **exactly the same string** as the original `/loop` input
- `reason` is one sentence shown to user + sent to telemetry — be specific (`"polling CI run"` beats `"waiting"`)
4. Harness re-invokes the model at the scheduled time. Loop continues. 5. Skip `ScheduleWakeup` → loop ends silently. No error.
Cache TTL gotcha
Anthropic prompt cache TTL is 5 minutes. Picking `delaySeconds`:
- `< 270s` → cache stays warm, cheap re-invocation
- `>= 1200s` → commit to the miss, one cache-miss amortizes over a long wait
- **`300s` is worst-of-both** — you pay the miss without amortizing. Avoid exactly 300.
For idle ticks with no specific signal to watch, default `1200-1800s` (20-30 min). User can always interrupt if they need you sooner.
Minute-boundary rounding gotcha (empirical, Experiment 1)
`delaySeconds` is NOT honored exactly. The actual wakeup time is **rounded UP to the next clean minute boundary**. Binary cron expression is `MM HH * * *` (minute-only granularity).
Examples from Experiment 1:
- Requested `30s`, clamped to `60s`, actual scheduled `65s` (we were at HH:MM:55)
- Same request next tick, actual scheduled `101s` (we were at HH:MM:19)
**Implication:** for any tick under ~1min you get 60-119s of real delay. Designing precise polling cadences <2min is futile — pick `60s` (floor) and accept jitter, or accept >120s with predictable bucketing. Also note dispatch latency adds ~10-25s on top.
Cancellation / collisions
- Re-issuing `/loop` with the **same prompt** cancels the previous pending wakeup for that prompt (`ZP5(_)` in binary). One prompt = one live loop.
- User interrupt cancels **all** pending loop wakeups (`b76()` returns count cancelled).
- Recurring max age: `7 days` (`recurringMaxAgeMs = 604800000`). After that the loop auto-ages-out.
Sentinels (fully autonomous)
For unattended runs where there's no user prompt sitting in the channel:
- `<<autonomous-loop>>` — cron-mode autonomous (CronCreate-backed)
- `<<autonomous-loop-dynamic>>` — ScheduleWakeup-mode autonomous
Useful for `/lets:schedule`-style routines but probably YAGNI for LETS right now.
Telemetry
- `tengu_kairos_loop_dynamic` — feature flag for dynamic mode
- `tengu_loop_dynamic_wakeup_scheduled` — fired per ScheduleWakeup
- `tengu_loop_dynamic_wakeup_aged_out` — fired when 7-day cap hits
Designing a useful `/loop` for LETS
Three rules that fit this project:
1. **Loop only on read-only state polls.** Anything that mutates (commit, push, bd close) needs explicit user approval per AUTO MODE rule — auto-loop violates it. `/lets:status`, `/lets:check`, `bd ready` are safe; `/lets:commit`, `/lets:done` are not. 2. **One sentence of new information per tick, or nothing.** If state hasn't changed, the tick should yield "no change" and reschedule. Don't re-report the same status over and over — the user sees ticks pile up. 3. **Pick the delay for the actual signal, not a round number.** Polling a CI run that takes ~8min? `delaySeconds=480` is better than `delaySeconds=600`. Avoid 300 (cache trap).
Concrete `/loop` templates for LETS
Template A — poll for PR feedback
/loop check `gh pr view <N> --json comments,reviewDecision` for new activity since last check. If nothing new: report "no change" and reschedule (1200s). If new comments or review state changed: report what's new and stop the loop so user can act.
Use case: opened a PR, want to know within 20min when reviewer pings, without keeping the window in focus.
Template B — backlog watcher
/loop run `bd ready --json | jq length`. If count increased since last tick: report which new tasks appeared (bd show each new id, one-line summary). If unchanged: report "no change". Reschedule 1800s.
Use case: long deep-work session on a feature, want to surface if a higher-priority bug landed in the backlog without breaking focus.
Template C — long-running job babysit
/loop check the deploy status at <URL
Read more
name: loop-test description: Local sandbox for experimenting with Claude Code's /loop command (interval + dynamic/self-paced scheduling) applied to LETS workflows. Use when the user says "loop test", "loop-test", "test the loop skill", "цикл LETS", "ScheduleWakeup experiment", or wants to design a periodic /loop that polls state or babysits a long-running job. Local-only — not part of the lets plugin payload. For convergent autonomous work toward an end state, use sibling skill `goal-test` (which covers /goal).
loop-test (local experiment)
`/loop` schedules **periodic re-invocations of the same prompt**. Two modes:
| Mode | Syntax | Mechanic | |---|---|---| | **Interval** | `/loop 5m /foo` · `/loop /standup 1h` · `/loop check the deploy every 20m` | Cron entry in `.claude/scheduled_tasks.json`. Default `10m` when only `[interval]` shorthand. Survives session restart (durable). | | **Dynamic / self-paced** | `/loop check the deploy` (no interval anywhere) | Model calls `ScheduleWakeup(delaySeconds, prompt, reason)` at the end of each turn. Skip → loop ends silently. |
**`/loop` is the wrong tool for sequential workflow phases.** It repeats the *same prompt* each tick — to chain `plan → review → check`, the prompt itself has to be phase-aware (read state, advance, write back). For state-convergent autonomy, use `/goal` instead (see `goal-test` skill).
**Right tool for `/loop`:**
- Polling external state (CI runs, deploys, PR comments)
- Periodic checks (every 20m: any new bd-ready tasks?)
- Babysitting a long-running job (check progress, surface failures)
- Continuous monitoring during a long focused session
How `/loop` actually works
Reverse-engineered from `claude` binary v2.1.150.
Dynamic-mode contract
This is the mode you care about most — interval is just cron.
1. User runs `/loop <prompt>` (no interval). 2. Model does the work. 3. Before yielding, model calls `ScheduleWakeup(delaySeconds, prompt, reason)`:
- `delaySeconds` clamped to `[60, 3600]`
- `prompt` must be **exactly the same string** as the original `/loop` input
- `reason` is one sentence shown to user + sent to telemetry — be specific (`"polling CI run"` beats `"waiting"`)
4. Harness re-invokes the model at the scheduled time. Loop continues. 5. Skip `ScheduleWakeup` → loop ends silently. No error.
Cache TTL gotcha
Anthropic prompt cache TTL is 5 minutes. Picking `delaySeconds`:
- `< 270s` → cache stays warm, cheap re-invocation
- `>= 1200s` → commit to the miss, one cache-miss amortizes over a long wait
- **`300s` is worst-of-both** — you pay the miss without amortizing. Avoid exactly 300.
For idle ticks with no specific signal to watch, default `1200-1800s` (20-30 min). User can always interrupt if they need you sooner.
Minute-boundary rounding gotcha (empirical, Experiment 1)
`delaySeconds` is NOT honored exactly. The actual wakeup time is **rounded UP to the next clean minute boundary**. Binary cron expression is `MM HH * * *` (minute-only granularity).
Examples from Experiment 1:
- Requested `30s`, clamped to `60s`, actual scheduled `65s` (we were at HH:MM:55)
- Same request next tick, actual scheduled `101s` (we were at HH:MM:19)
**Implication:** for any tick under ~1min you get 60-119s of real delay. Designing precise polling cadences <2min is futile — pick `60s` (floor) and accept jitter, or accept >120s with predictable bucketing. Also note dispatch latency adds ~10-25s on top.
Cancellation / collisions
- Re-issuing `/loop` with the **same prompt** cancels the previous pending wakeup for that prompt (`ZP5(_)` in binary). One prompt = one live loop.
- User interrupt cancels **all** pending loop wakeups (`b76()` returns count cancelled).
- Recurring max age: `7 days` (`recurringMaxAgeMs = 604800000`). After that the loop auto-ages-out.
Sentinels (fully autonomous)
For unattended runs where there's no user prompt sitting in the channel:
- `<<autonomous-loop>>` — cron-mode autonomous (CronCreate-backed)
- `<<autonomous-loop-dynamic>>` — ScheduleWakeup-mode autonomous
Useful for `/lets:schedule`-style routines but probably YAGNI for LETS right now.
Telemetry
- `tengu_kairos_loop_dynamic` — feature flag for dynamic mode
- `tengu_loop_dynamic_wakeup_scheduled` — fired per ScheduleWakeup
- `tengu_loop_dynamic_wakeup_aged_out` — fired when 7-day cap hits
Designing a useful `/loop` for LETS
Three rules that fit this project:
1. **Loop only on read-only state polls.** Anything that mutates (commit, push, bd close) needs explicit user approval per AUTO MODE rule — auto-loop violates it. `/lets:status`, `/lets:check`, `bd ready` are safe; `/lets:commit`, `/lets:done` are not. 2. **One sentence of new information per tick, or nothing.** If state hasn't changed, the tick should yield "no change" and reschedule. Don't re-report the same status over and over — the user sees ticks pile up. 3. **Pick the delay for the actual signal, not a round number.** Polling a CI run that takes ~8min? `delaySeconds=480` is better than `delaySeconds=600`. Avoid 300 (cache trap).
Concrete `/loop` templates for LETS
Template A — poll for PR feedback
/loop check `gh pr view <N> --json comments,reviewDecision` for new activity since last check. If nothing new: report "no change" and reschedule (1200s). If new comments or review state changed: report what's new and stop the loop so user can act.
Use case: opened a PR, want to know within 20min when reviewer pings, without keeping the window in focus.
Template B — backlog watcher
/loop run `bd ready --json | jq length`. If count increased since last tick: report which new tasks appeared (bd show each new id, one-line summary). If unchanged: report "no change". Reschedule 1800s.
Use case: long deep-work session on a feature, want to surface if a higher-priority bug landed in the backlog without breaking focus.
Template C — long-running job babysit
/loop check the deploy status at <URL
A development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.
Repo: restarter/lets-workflow
Other skills on lets-workflow.
goal-test
Local sandbox for experimenting with Claude Code's /goal command (stop-hook condition + LLM-evaluator driving autonomous work toward a verifiable end state)…
loop-bb-pr
Compose a /loop that monitors a Bitbucket PR for activity (new comments, review state changes, merge, decline) via the bb-api wrapper. Use when the user says…
actor-fetch-personalit…
Internal skill for commands. Fetch and validate personality from URL or file path for Actor agent. Do not trigger on user conversation - only when commands…
artifact-path
Internal skill for commands. Resolve a unique, task-scoped, collision-safe path for an artifact written under .lets/ (plans, reviews, session snapshots). Do…
backlog-workflow
Dynamic Workflow asset for /lets:backlog review --workflow. Not auto-triggered - a workflow script invoked via scriptPath by /lets:backlog. Holds the…

