feat(skills): /sleep-on-it 6-phase wizard + kei-sleep-queue CRUD + incubation prompt
Priority-scaled time budgets (quick/standard/deep/marathon/weekly), marathon-mode for hard derivations (skips Phase B REM for one task), checkpointing every N minutes via partial commits.
This commit is contained in:
parent
c6c572dcf4
commit
3d928b41db
9 changed files with 1114 additions and 0 deletions
269
_primitives/kei-sleep-queue.sh
Executable file
269
_primitives/kei-sleep-queue.sh
Executable file
|
|
@ -0,0 +1,269 @@
|
|||
#!/usr/bin/env bash
|
||||
# kei-sleep-queue.sh — v0.12.0 "sleep on it" queue CRUD helper.
|
||||
# Commands: add / list / show / done / fail / purge.
|
||||
# Env: KEI_MEMORY_REPO_PATH (sourced from ~/.claude/secrets/.env).
|
||||
# Bypass: KEI_SLEEP_GENESIS_BYPASS=1 skips genesis-scan gate on `add`.
|
||||
set -u
|
||||
|
||||
SECRETS_FILE="${HOME}/.claude/secrets/.env"
|
||||
[ -f "$SECRETS_FILE" ] && [ -z "${KEI_MEMORY_REPO_PATH:-}" ] && \
|
||||
. "$SECRETS_FILE" 2>/dev/null || true
|
||||
|
||||
REPO_PATH="${KEI_MEMORY_REPO_PATH:-}"
|
||||
QUEUE_DIR="${REPO_PATH}/sleep-queue"
|
||||
DONE_DIR="${REPO_PATH}/sleep-queue-done"
|
||||
FAIL_DIR="${REPO_PATH}/sleep-queue-failed"
|
||||
GENESIS_BIN="${HOME}/.claude/agents/_primitives/_rust/target/release/genesis-scan"
|
||||
SYNC_SH="${HOME}/.claude/agents/_primitives/kei-sleep-sync.sh"
|
||||
|
||||
err() { printf 'kei-sleep-queue: %s\n' "$*" >&2; }
|
||||
die() { err "$*"; exit 1; }
|
||||
|
||||
ensure_repo() {
|
||||
[ -n "$REPO_PATH" ] || die "KEI_MEMORY_REPO_PATH not set (run /sleep-setup)"
|
||||
[ -d "${REPO_PATH}/.git" ] || die "sync-repo not initialised at $REPO_PATH"
|
||||
mkdir -p "$QUEUE_DIR" "$DONE_DIR" "$FAIL_DIR" 2>/dev/null || true
|
||||
}
|
||||
|
||||
gen_uuid() {
|
||||
if command -v uuidgen >/dev/null 2>&1; then uuidgen | tr 'A-Z' 'a-z'
|
||||
else printf '%s-%s' "$(date -u +%s)" "${RANDOM}${RANDOM}"; fi
|
||||
}
|
||||
|
||||
iso_utc() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
||||
push_async() { [ -x "$SYNC_SH" ] && "$SYNC_SH" >/dev/null 2>&1 || true; }
|
||||
|
||||
scan_prompt() {
|
||||
[ "${KEI_SLEEP_GENESIS_BYPASS:-0}" = "1" ] && return 0
|
||||
[ -x "$GENESIS_BIN" ] || return 0
|
||||
"$GENESIS_BIN" --stdin --exit-on-hit --format text < "$1" >&2 && return 0
|
||||
err "genesis-scan flagged the prompt — see scanner output above"
|
||||
err "bypass (false positives only): KEI_SLEEP_GENESIS_BYPASS=1 $0 add ..."
|
||||
exit 2
|
||||
}
|
||||
|
||||
# Find a queue file by uuid prefix in dir; echoes path or returns 1.
|
||||
find_by_uuid() {
|
||||
local uuid="$1" dir="$2" f
|
||||
[ -d "$dir" ] || return 1
|
||||
for f in "$dir/${uuid}-"*.md "$dir/${uuid}.md"; do
|
||||
[ -f "$f" ] && { printf '%s\n' "$f"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Extract a frontmatter field value (first match) from a file.
|
||||
fm_field() { awk -F': ' -v k="^$2:" '$0 ~ k {print $2; exit}' "$1"; }
|
||||
|
||||
# Parse "<N>m" → N (minutes), or die with context.
|
||||
parse_minutes() {
|
||||
local raw="$1" label="$2" stripped="${1%m}"
|
||||
case "$stripped" in ''|*[!0-9]*) die "bad $label: $raw (expected <N>m)" ;; esac
|
||||
printf '%s\n' "$stripped"
|
||||
}
|
||||
|
||||
# Priority defaults: TIME_BUDGET_MINUTES, CHECKPOINT_EVERY_MINUTES, MARATHON.
|
||||
priority_defaults() {
|
||||
case "$1" in
|
||||
quick) printf '15 0 false\n' ;;
|
||||
standard) printf '60 20 false\n' ;;
|
||||
deep) printf '240 30 false\n' ;;
|
||||
marathon) printf '480 30 true\n' ;;
|
||||
weekly) printf '60 20 false\n' ;;
|
||||
*) die "bad --priority: $1 (expected quick|standard|deep|marathon|weekly)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Validate add flags; sets ADD_* including time-budget / checkpoint / marathon.
|
||||
parse_add_flags() {
|
||||
ADD_TYPE=""; ADD_PRIORITY=""; ADD_FORMAT=""; ADD_PROMPT=""
|
||||
ADD_TIME_BUDGET=""; ADD_CHECKPOINT=""; ADD_MARATHON=""; ADD_NO_TIMEOUT=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--type) ADD_TYPE="$2"; shift 2 ;;
|
||||
--priority) ADD_PRIORITY="$2"; shift 2 ;;
|
||||
--format) ADD_FORMAT="$2"; shift 2 ;;
|
||||
--prompt-file) ADD_PROMPT="$2"; shift 2 ;;
|
||||
--time-budget) ADD_TIME_BUDGET="$(parse_minutes "$2" --time-budget)"; shift 2 ;;
|
||||
--checkpoint-every) ADD_CHECKPOINT="$(parse_minutes "$2" --checkpoint-every)"; shift 2 ;;
|
||||
--no-timeout) ADD_NO_TIMEOUT=1; shift ;;
|
||||
--marathon) ADD_MARATHON="true"; shift ;;
|
||||
*) die "unknown flag: $1" ;;
|
||||
esac
|
||||
done
|
||||
case "$ADD_TYPE" in deep|pipeline|pattern|compare|custom) ;; *) die "bad --type: $ADD_TYPE" ;; esac
|
||||
case "$ADD_FORMAT" in md|adr|checklist|table) ;; *) die "bad --format: $ADD_FORMAT" ;; esac
|
||||
[ -n "$ADD_PROMPT" ] && [ -f "$ADD_PROMPT" ] || die "missing --prompt-file"
|
||||
resolve_priority_fields
|
||||
}
|
||||
|
||||
# Apply priority defaults for any ADD_* fields that weren't overridden.
|
||||
resolve_priority_fields() {
|
||||
local defaults budget cp marathon
|
||||
defaults="$(priority_defaults "$ADD_PRIORITY")"
|
||||
budget="$(printf '%s' "$defaults" | awk '{print $1}')"
|
||||
cp="$(printf '%s' "$defaults" | awk '{print $2}')"
|
||||
marathon="$(printf '%s' "$defaults" | awk '{print $3}')"
|
||||
[ -z "$ADD_TIME_BUDGET" ] && ADD_TIME_BUDGET="$budget"
|
||||
[ -z "$ADD_CHECKPOINT" ] && ADD_CHECKPOINT="$cp"
|
||||
[ -z "$ADD_MARATHON" ] && ADD_MARATHON="$marathon"
|
||||
[ "$ADD_NO_TIMEOUT" = "1" ] && ADD_TIME_BUDGET="null"
|
||||
if [ "$ADD_MARATHON" = "true" ] && [ "$ADD_PRIORITY" != "marathon" ]; then
|
||||
err "warning: --marathon set but --priority=$ADD_PRIORITY (expected marathon)"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_add() {
|
||||
parse_add_flags "$@"
|
||||
ensure_repo
|
||||
scan_prompt "$ADD_PROMPT"
|
||||
local uuid ts file
|
||||
uuid="$(gen_uuid)"
|
||||
ts="$(iso_utc)"
|
||||
file="${QUEUE_DIR}/${uuid}-$(date -u +%s).md"
|
||||
{
|
||||
printf -- '---\n'
|
||||
printf -- 'uuid: %s\n' "$uuid"
|
||||
printf -- 'submitted_at: %s\n' "$ts"
|
||||
printf -- 'type: %s\n' "$ADD_TYPE"
|
||||
printf -- 'priority: %s\n' "$ADD_PRIORITY"
|
||||
printf -- 'format: %s\n' "$ADD_FORMAT"
|
||||
printf -- 'time_budget_minutes: %s\n' "$ADD_TIME_BUDGET"
|
||||
printf -- 'checkpoint_every_minutes: %s\n' "$ADD_CHECKPOINT"
|
||||
printf -- 'marathon: %s\n' "$ADD_MARATHON"
|
||||
printf -- 'status: pending\n---\n\n'
|
||||
cat "$ADD_PROMPT"
|
||||
printf '\n'
|
||||
} > "$file" || die "write failed: $file"
|
||||
printf '%s\n%s\n' "$uuid" "$file"
|
||||
push_async
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local filter="pending" dir
|
||||
[ $# -gt 0 ] && case "$1" in
|
||||
--pending) filter="pending" ;;
|
||||
--done) filter="done" ;;
|
||||
--failed) filter="failed" ;;
|
||||
*) die "unknown filter: $1" ;;
|
||||
esac
|
||||
ensure_repo
|
||||
case "$filter" in
|
||||
pending) dir="$QUEUE_DIR" ;; done) dir="$DONE_DIR" ;; failed) dir="$FAIL_DIR" ;;
|
||||
esac
|
||||
printf '%-36s %-10s %-8s %-9s %s\n' UUID SUBMITTED TYPE PRIORITY FILE
|
||||
local f u s t p
|
||||
for f in "$dir"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
u="$(fm_field "$f" uuid)"
|
||||
s="$(fm_field "$f" submitted_at | cut -c1-10)"
|
||||
t="$(fm_field "$f" type)"
|
||||
p="$(fm_field "$f" priority)"
|
||||
printf '%-36s %-10s %-8s %-9s %s\n' "${u:--}" "${s:--}" "${t:--}" "${p:--}" "$f"
|
||||
done
|
||||
}
|
||||
|
||||
cmd_show() {
|
||||
[ $# -ge 1 ] || die "usage: show <uuid>"
|
||||
ensure_repo
|
||||
local uuid="$1" f dir
|
||||
for dir in "$QUEUE_DIR" "$DONE_DIR" "$FAIL_DIR"; do
|
||||
f="$(find_by_uuid "$uuid" "$dir" 2>/dev/null)" && { cat "$f"; return 0; }
|
||||
done
|
||||
die "uuid not found: $uuid"
|
||||
}
|
||||
|
||||
cmd_done() {
|
||||
[ $# -ge 1 ] || die "usage: done <uuid>"
|
||||
ensure_repo
|
||||
local src dest uuid="$1"
|
||||
src="$(find_by_uuid "$uuid" "$QUEUE_DIR")" || die "pending uuid not found: $uuid"
|
||||
dest="${DONE_DIR}/${uuid}.md"
|
||||
sed 's/^status: pending$/status: done/' "$src" > "$dest" || die "write failed: $dest"
|
||||
rm -f "$src"
|
||||
printf 'moved: %s -> %s\n' "$src" "$dest"
|
||||
push_async
|
||||
}
|
||||
|
||||
cmd_fail() {
|
||||
local uuid="" reason=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--reason) reason="$2"; shift 2 ;;
|
||||
*) [ -z "$uuid" ] && { uuid="$1"; shift; } || die "unknown arg: $1" ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$uuid" ] || die "usage: fail <uuid> --reason <text>"
|
||||
ensure_repo
|
||||
local src dest
|
||||
src="$(find_by_uuid "$uuid" "$QUEUE_DIR")" || die "pending uuid not found: $uuid"
|
||||
dest="${FAIL_DIR}/${uuid}.md"
|
||||
{
|
||||
sed 's/^status: pending$/status: failed/' "$src"
|
||||
printf '\n---\n## Failure reason\n\n%s\n' "${reason:-(no reason given)}"
|
||||
} > "$dest" || die "write failed: $dest"
|
||||
rm -f "$src"
|
||||
printf 'moved: %s -> %s\n' "$src" "$dest"
|
||||
push_async
|
||||
}
|
||||
|
||||
cmd_purge() {
|
||||
local days=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--older-than) days="${2%d}"; shift 2 ;;
|
||||
*) die "unknown flag: $1" ;;
|
||||
esac
|
||||
done
|
||||
case "$days" in ''|*[!0-9]*) die "--older-than <N>d required (N integer)" ;; esac
|
||||
ensure_repo
|
||||
local removed=0 f dir
|
||||
for dir in "$DONE_DIR" "$FAIL_DIR"; do
|
||||
while IFS= read -r f; do
|
||||
rm -f "$f" && removed=$((removed + 1))
|
||||
done < <(find "$dir" -maxdepth 1 -type f -name '*.md' -mtime "+$days" 2>/dev/null)
|
||||
done
|
||||
printf 'purged %d file(s) older than %sd\n' "$removed" "$days"
|
||||
push_async
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
kei-sleep-queue.sh — v0.12 sleep-on-it queue helper
|
||||
|
||||
add --type <deep|pipeline|pattern|compare|custom>
|
||||
--priority <quick|standard|deep|marathon|weekly>
|
||||
--format <md|adr|checklist|table>
|
||||
--prompt-file <path>
|
||||
[--time-budget <N>m] override minutes from priority default
|
||||
[--checkpoint-every <M>m] override partial-result cadence
|
||||
[--no-timeout] time_budget_minutes: null (run until done)
|
||||
[--marathon] explicit marathon flag
|
||||
list [--pending|--done|--failed]
|
||||
show <uuid>
|
||||
done <uuid>
|
||||
fail <uuid> --reason <text>
|
||||
purge --older-than <N>d
|
||||
|
||||
Env: KEI_MEMORY_REPO_PATH (required)
|
||||
KEI_SLEEP_GENESIS_BYPASS=1 skip genesis-scan gate on add
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
main() {
|
||||
[ $# -ge 1 ] || usage
|
||||
local sub="$1"; shift
|
||||
case "$sub" in
|
||||
add) cmd_add "$@" ;;
|
||||
list) cmd_list "$@" ;;
|
||||
show) cmd_show "$@" ;;
|
||||
done) cmd_done "$@" ;;
|
||||
fail) cmd_fail "$@" ;;
|
||||
purge) cmd_purge "$@" ;;
|
||||
-h|--help|help) usage ;;
|
||||
*) die "unknown command: $sub" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
211
_primitives/templates/sleep-incubation-prompt.md
Normal file
211
_primitives/templates/sleep-incubation-prompt.md
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
# Nightly incubation — Phase A (KeiSeiKit v0.12.0 "sleep on it")
|
||||
|
||||
<!--
|
||||
Prepended to the v0.11 nightly trigger prompt. The remote agent reads this
|
||||
block FIRST and processes the queue before running the existing REM
|
||||
consolidation (Phase B). Phase A and Phase B commit separately so the
|
||||
morning pull shows two distinct diffs.
|
||||
-->
|
||||
|
||||
## Phase A — Incubation ("sleep on it")
|
||||
|
||||
1. **Discover pending tasks.** List `sync-repo/sleep-queue/*.md` files
|
||||
ordered by the `submitted_at` frontmatter field ascending (FIFO).
|
||||
2. **Filter by day.** If today is NOT Sunday UTC, skip any file with
|
||||
`priority: weekly`. On Sunday UTC, include weekly tasks alongside
|
||||
`quick`, `standard`, `deep`, and `marathon` tasks in the same FIFO
|
||||
order.
|
||||
3. **Select tasks for this run (priority-aware).**
|
||||
Priority resolution for the run:
|
||||
1. Count `marathon: true` tasks in queue. If ≥ 1 with a this-night
|
||||
priority, select the OLDEST by `submitted_at` as the sole task
|
||||
for tonight. **Phase B REM consolidation is SKIPPED this run.**
|
||||
All other pending tasks are deferred to the next night.
|
||||
2. Else: pool this-night tasks (`priority ∈ {quick, standard, deep}`)
|
||||
in FIFO order and **greedy-pack up to 480 minutes total** across
|
||||
at most 5 tasks. Skip any task whose `time_budget_minutes` would
|
||||
overflow the remaining budget; it stays pending for the next run.
|
||||
3. Weekly batch: only processed on Sunday UTC, counted toward the
|
||||
480-minute greedy-pack budget alongside `standard`/`deep` tasks.
|
||||
4. **Budget time per task.** Read `time_budget_minutes` from the task's
|
||||
frontmatter. Default 60 if absent or unparseable. Behavior:
|
||||
- If `marathon: true`: this task gets the entire night (max 480 min);
|
||||
other queue items skip this cycle; Phase B is skipped.
|
||||
- If `checkpoint_every_minutes > 0`: every N minutes, write partial
|
||||
result to `sleep-results/<uuid>.partial.md` AND commit + push, so
|
||||
if the run is cut short the user still has the partial.
|
||||
- If the budget is exhausted and the task is not done: write the
|
||||
partial with `[TIME-BOXED — <N>min budget exhausted]` at the top
|
||||
of the body, set `status: timed_out` in the queue-file frontmatter,
|
||||
and move it to `sleep-queue-failed/<uuid>.md`.
|
||||
- If `time_budget_minutes: null` (no-timeout): run until done or
|
||||
until the hard cloud-session cap is hit (still honor checkpointing
|
||||
so no work is lost).
|
||||
5. **Dispatch by type.** Read the `type` frontmatter and run the
|
||||
corresponding tool chain:
|
||||
- `deep` — ≥ 3 WebSearch queries + ≥ 2 WebFetch page reads +
|
||||
synthesis section. If the web is unreachable, mark result
|
||||
`[OFFLINE — web tools unavailable]` and fail the task (step 7).
|
||||
- `pipeline` — emit 5–7 phases, each with a one-line
|
||||
verify-criterion, followed by a tradeoffs matrix. Use the repo's
|
||||
past reports (`sync-repo/reports/*.md`) as context if relevant.
|
||||
- `pattern` — grep `sync-repo/reports/*.md` and `sync-repo/backlog.md`
|
||||
for recurring tokens related to the task text; extract 3–5 trends;
|
||||
propose one concrete action.
|
||||
- `compare` — produce a markdown table with the options the user
|
||||
listed as columns, weighted criteria as rows, and a weighted-score
|
||||
recommendation in the final row.
|
||||
- `custom` — follow the task text verbatim without any fixed
|
||||
dispatch. Write whatever the task asks for in the chosen format.
|
||||
6. **Write the result** to
|
||||
`sync-repo/sleep-results/<uuid>.md` in the chosen `format`:
|
||||
- `md` — `# Title` + sections + sources.
|
||||
- `adr` — `Context / Decision / Consequences`.
|
||||
- `checklist` — `- [ ] item` bullets only (plus a one-line preamble).
|
||||
- `table` — markdown table + short recommendation paragraph.
|
||||
Every result MUST end with a `## Sources` section listing the
|
||||
concrete URLs, files, or tool calls the agent used.
|
||||
7. **Mark the task done.** Move the queue file:
|
||||
`sync-repo/sleep-queue/<uuid>-*.md` → `sync-repo/sleep-queue-done/<uuid>.md`
|
||||
Also update the `status:` frontmatter line from `pending` to `done`.
|
||||
8. **On catastrophic failure** (tool error not fixable in the 15-min
|
||||
budget, missing dependency, corrupted frontmatter): move the file to
|
||||
`sync-repo/sleep-queue-failed/<uuid>.md`, update `status:` to
|
||||
`failed`, and append a `## Failure reason` block to the body. Continue
|
||||
with the next task.
|
||||
9. **Commit once after Phase A completes** (single commit regardless of
|
||||
how many tasks were processed). Commit message:
|
||||
`REM: incubation <YYYY-MM-DD> (<N> task(s))`
|
||||
10. **Then run Phase B** (see `sleep-trigger-prompt.md`). Phase B gets
|
||||
its own commit: `REM: consolidation <YYYY-MM-DD>`.
|
||||
|
||||
### Phase A time cap
|
||||
|
||||
Total wall-clock cap for Phase A is **dynamic**:
|
||||
- **Marathon run:** up to 480 minutes for the single selected task.
|
||||
Phase B is SKIPPED this cycle.
|
||||
- **Regular run:** greedy-pack up to 480 minutes total across at most
|
||||
5 tasks, driven by each task's `time_budget_minutes`.
|
||||
|
||||
If the cap is hit mid-task, commit partial progress (honoring the
|
||||
task's `checkpoint_every_minutes` cadence) and move the in-flight
|
||||
task to `sleep-queue-failed/` with reason `phase-a-time-cap`. The
|
||||
partial result (if any) stays in `sleep-results/<uuid>.partial.md`.
|
||||
|
||||
### Checkpointing (intermediate commits)
|
||||
|
||||
If a task's `checkpoint_every_minutes` is > 0, the agent commits
|
||||
partial progress at that cadence:
|
||||
|
||||
```
|
||||
git add sleep-results/<uuid>.partial.md
|
||||
git commit -m "sleep: checkpoint <uuid> at <N>min"
|
||||
git push
|
||||
```
|
||||
|
||||
A final "task done" commit rolls the partial into `<uuid>.md` and
|
||||
deletes the `.partial.md` file. If the run is cut short, the last
|
||||
partial persists in the repo and the user can read it on morning pull.
|
||||
|
||||
---
|
||||
|
||||
## Example queue file (input)
|
||||
|
||||
```
|
||||
---
|
||||
uuid: 8d4f3c1e-7b2a-4f1d-9c8e-0a1b2c3d4e5f
|
||||
submitted_at: 2026-04-22T14:03:17Z
|
||||
type: compare
|
||||
priority: standard
|
||||
format: table
|
||||
time_budget_minutes: 60
|
||||
checkpoint_every_minutes: 20
|
||||
marathon: false
|
||||
status: pending
|
||||
---
|
||||
|
||||
Compare SvelteKit, Astro, and Next.js App Router for the kit's landing
|
||||
page. Criteria: bundle size, SSR ergonomics, build time, ecosystem
|
||||
depth, hosting footprint.
|
||||
```
|
||||
|
||||
## Example result file (output)
|
||||
|
||||
```
|
||||
# Compare: SvelteKit / Astro / Next.js App Router
|
||||
|
||||
| Criterion (weight) | SvelteKit | Astro | Next.js App Router |
|
||||
|------------------------|-----------|--------|--------------------|
|
||||
| Bundle size (0.3) | 9/10 | 10/10 | 6/10 |
|
||||
| SSR ergonomics (0.2) | 8/10 | 7/10 | 9/10 |
|
||||
| Build time (0.2) | 8/10 | 9/10 | 6/10 |
|
||||
| Ecosystem depth (0.2) | 7/10 | 6/10 | 10/10 |
|
||||
| Hosting footprint (0.1)| 8/10 | 9/10 | 5/10 |
|
||||
| **Weighted score** | **8.1** | **8.2**| **7.3** |
|
||||
|
||||
**Recommendation:** Astro narrowly wins for a content-first kit landing
|
||||
page. Pick SvelteKit if the landing grows into an app; Next.js App
|
||||
Router only if you already ship a Next.js product suite.
|
||||
|
||||
## Sources
|
||||
- https://svelte.dev/docs/kit
|
||||
- https://docs.astro.build/en/concepts/why-astro/
|
||||
- https://nextjs.org/docs/app
|
||||
- (tool: WebSearch) "SvelteKit vs Astro 2026 bundle size benchmark"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exit reasons (per-task status)
|
||||
|
||||
Every task ends in exactly one of:
|
||||
|
||||
- `done` — full result in `sleep-results/<uuid>.md`, queue file moved
|
||||
to `sleep-queue-done/`.
|
||||
- `time_budget_exhausted` — partial in `sleep-results/<uuid>.partial.md`
|
||||
with `[TIME-BOXED — <N>min budget exhausted]` marker, queue file
|
||||
moved to `sleep-queue-failed/` with `status: timed_out`.
|
||||
- `checkpoint_saved` — intermediate state; the task is still pending
|
||||
but the latest `.partial.md` is committed and pushed. This is NOT a
|
||||
terminal status; it upgrades to `done` or `time_budget_exhausted`.
|
||||
- `failed` — tool error, missing dependency, genesis-scan hit, or
|
||||
other non-recoverable failure. Queue file moved to
|
||||
`sleep-queue-failed/` with a `## Failure reason` block.
|
||||
|
||||
## Invariants (MUST NOT violate)
|
||||
|
||||
- **Never modify or delete `traces/*.jsonl`.** Phase A only touches
|
||||
`sleep-queue/`, `sleep-queue-done/`, `sleep-queue-failed/`, and
|
||||
`sleep-results/`. Phase B touches `reports/` and `backlog.md`. Neither
|
||||
touches `traces/`.
|
||||
- **Checkpoint commits are mandatory** when `checkpoint_every_minutes
|
||||
> 0`. Skipping them loses user work on cloud-session eviction.
|
||||
- **Never delete files outside the queue trees.** Move within the
|
||||
sync-repo is the only mutation Phase A performs; `rm` is banned
|
||||
outside `sleep-queue*/` (and even there, only after a successful
|
||||
move to done/failed).
|
||||
- **Never paraphrase patent-sensitive terms into the result body.** The
|
||||
user-side `genesis-scan` gate is line of defense 1; the agent is line
|
||||
of defense 2. If the agent's best-effort `genesis-scan` run on the
|
||||
prompt returns non-zero (the scanner's forbidden-pattern list is
|
||||
embedded in the binary itself — use
|
||||
`genesis-scan list-patterns` if you need to inspect it), mark the
|
||||
task failed with reason `patent-term-detected` and skip it entirely
|
||||
— no partial result, no paraphrasing the matched token.
|
||||
- **No shell command writes outside `sync-repo/`.** Results land in the
|
||||
repo, nothing else.
|
||||
- **No session feedback loop (RULE 0.15).** Results are for the user to
|
||||
read the next morning. Nothing Phase A writes is auto-consumed by
|
||||
another Claude Code session.
|
||||
|
||||
---
|
||||
|
||||
## Failure handling (same as Phase B)
|
||||
|
||||
- Clone fails or repo is dirty → exit 1, let the next run retry.
|
||||
- `sleep-queue/` missing → create it empty + commit, then exit clean
|
||||
(first run on an older sync-repo).
|
||||
- Any single task fails → move to `sleep-queue-failed/`, continue.
|
||||
- Phase A overall fails (total time cap, unhandled tool error) → commit
|
||||
whatever partial state exists, THEN run Phase B normally. Phase B
|
||||
must not depend on Phase A succeeding.
|
||||
117
skills/sleep-on-it/SKILL.md
Normal file
117
skills/sleep-on-it/SKILL.md
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
name: sleep-on-it
|
||||
description: Defer a hard question, research task, or design comparison to the nightly remote agent (KeiSeiKit v0.12.0 incubation layer). Runs on top of the v0.11 sleep-sync pipeline — user fills one free-text field plus three clicks, task lands in sync-repo/sleep-queue/ and is processed before REM consolidation. Up to 5 tasks per night, 15 minutes each. Pure-click wizard except the single task-description field.
|
||||
argument-hint: (no arguments)
|
||||
---
|
||||
|
||||
# Sleep On It — Incubation Wizard (index)
|
||||
|
||||
Biological analog: the REM-sleep "sleep on it" effect — insight generation
|
||||
during incubation (Wagner et al. 2004, *Nature*). During the day the user
|
||||
submits open questions, research tasks, or design comparisons via this
|
||||
wizard; the nightly cloud agent processes the queue before its existing
|
||||
REM consolidation pass and writes results to `sync-repo/sleep-results/`.
|
||||
|
||||
This `SKILL.md` is the INDEX. Each phase lives in its own file and is
|
||||
executed in order. Never skip a phase. Never re-order phases.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites (hard fail fast if missing)
|
||||
|
||||
- v0.11 sleep-sync must be configured (`~/.claude/secrets/.env` contains
|
||||
`KEI_MEMORY_REPO_PATH`, `KEI_MEMORY_SSH_KEY`, and the sync-repo exists
|
||||
under that path with a `.git/` subdir).
|
||||
- `_primitives/kei-sleep-queue.sh` exists at
|
||||
`~/.claude/agents/_primitives/kei-sleep-queue.sh` and is executable.
|
||||
|
||||
If either is missing, print the single line
|
||||
|
||||
```
|
||||
v0.11 sleep-sync not configured — run `/sleep-setup` first, then retry.
|
||||
```
|
||||
|
||||
and exit the wizard. Do not attempt to queue anything offline.
|
||||
|
||||
---
|
||||
|
||||
## Pipeline overview (6 phases, 5+ AskUserQuestion)
|
||||
|
||||
| Phase | File | Purpose | AskUserQuestion |
|
||||
|---|---|---|---|
|
||||
| 1 | [phase-1-intake.md](phase-1-intake.md) | One free-text field: the question / task | 0 (prompt, non-empty validate) |
|
||||
| 2 | [phase-2-type.md](phase-2-type.md) | Task type: deep / pipeline / pattern / compare / custom | 1 (click) |
|
||||
| 3 | [phase-3-priority.md](phase-3-priority.md) | Priority: tonight / FIFO / weekly | 1 (click) |
|
||||
| 4 | [phase-4-format.md](phase-4-format.md) | Output format: markdown / ADR / checklist / table | 1 (click) |
|
||||
| 5 | [phase-5-submit.md](phase-5-submit.md) | Preview frontmatter + body, submit / edit / abort | 1 (click) |
|
||||
| 6 | [phase-6-ack.md](phase-6-ack.md) | Acknowledgment with UUID + queue path + run ETA | 1 (click) |
|
||||
|
||||
**Minimum AskUserQuestion count: 5.** All clicks except the single
|
||||
free-text task description in Phase 1.
|
||||
|
||||
---
|
||||
|
||||
## Variables the pipeline produces
|
||||
|
||||
| Name | Set in | Meaning |
|
||||
|---|---|---|
|
||||
| `TASK_TEXT` | Phase 1 | Free-text task description (non-empty) |
|
||||
| `TASK_TYPE` | Phase 2 | `deep` / `pipeline` / `pattern` / `compare` / `custom` |
|
||||
| `PRIORITY` | Phase 3 | `night` / `fifo` / `weekly` |
|
||||
| `FORMAT` | Phase 4 | `md` / `adr` / `checklist` / `table` |
|
||||
| `SUBMIT_ACTION` | Phase 5 | `submit` / `edit` / `abort` |
|
||||
| `QUEUE_PATH` | Phase 5 | Path of the queue file written by `kei-sleep-queue.sh add` |
|
||||
| `UUID` | Phase 5 | UUID assigned by the helper |
|
||||
|
||||
---
|
||||
|
||||
## Final report (emit after Phase 6)
|
||||
|
||||
```
|
||||
=== SLEEP-ON-IT REPORT ===
|
||||
UUID: <UUID>
|
||||
Queue file: <QUEUE_PATH>
|
||||
Task type: <TASK_TYPE>
|
||||
Priority: <PRIORITY>
|
||||
Output format: <FORMAT>
|
||||
Next run ETA: <UTC cron time from .keisei-sync.toml>
|
||||
Results land: sync-repo/sleep-results/<UUID>.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rules (apply throughout — enforced at every phase)
|
||||
|
||||
- **Pure-click contract.** Only Phase 1 asks for free text; every other
|
||||
decision is an `AskUserQuestion`. No `freeText` outside Phase 1.
|
||||
- **Idempotent.** Re-running the wizard while a previous task is still
|
||||
pending is fine — each submission gets its own UUID and its own queue
|
||||
file. No "one pending at a time" constraint.
|
||||
- **Genesis-scan on submit (RULE 0.1 second line of defense).** The
|
||||
helper `kei-sleep-queue.sh add` pipes the task text through
|
||||
`genesis-scan --stdin --exit-on-hit` when the binary exists. On hit
|
||||
the submission is rejected with the scanner's stderr surfaced to chat.
|
||||
Bypass only via `KEI_SLEEP_GENESIS_BYPASS=1` with a visible note.
|
||||
- **NO DOWNGRADE (RULE -1).** If the helper rejects (genesis hit, invalid
|
||||
flag, sync push fails), surface 2-3 constructive fix paths — never
|
||||
"cannot submit".
|
||||
- **NO HALLUCINATION (RULE 0.4).** Never fabricate a UUID, queue path,
|
||||
or ETA — always echo the real helper output.
|
||||
- **RULE 0.8 secrets.** Queue files never embed tokens; env refs live in
|
||||
`~/.claude/secrets/.env` only.
|
||||
- **Silent failure (RULE 0.15).** If the post-submit sync push fails,
|
||||
the queue file still lives locally and will be pushed on the next
|
||||
session-end dump. The wizard must NOT block on push failure.
|
||||
- **Constructor Pattern (RULE ZERO).** Every phase file < 100 LOC.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- `~/.claude/rules/sleep-layer.md` — RULE 0.15 full text (Phase A added v0.12.0)
|
||||
- `_primitives/kei-sleep-queue.sh` — the queue CRUD helper
|
||||
- `_primitives/kei-sleep-sync.sh` — the session-end-dump callback (also
|
||||
invoked by `kei-sleep-queue.sh add` after write)
|
||||
- `_primitives/templates/sleep-incubation-prompt.md` — cloud agent Phase A
|
||||
- `_primitives/templates/sleep-trigger-prompt.md` — cloud agent Phase B
|
||||
- `skills/sleep-setup/` — v0.11 one-time sync-repo wizard (prerequisite)
|
||||
58
skills/sleep-on-it/phase-1-intake.md
Normal file
58
skills/sleep-on-it/phase-1-intake.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Phase 1 — Task intake (one free-text field)
|
||||
|
||||
The single free-text field in the wizard. Everything else is a click.
|
||||
|
||||
## 1a — Prerequisite check
|
||||
|
||||
Before prompting, verify the v0.11 pipeline is live:
|
||||
|
||||
```bash
|
||||
# Resolve sync-repo path from env or secrets file.
|
||||
# shellcheck disable=SC1091
|
||||
[ -f "${HOME}/.claude/secrets/.env" ] && . "${HOME}/.claude/secrets/.env"
|
||||
REPO_PATH="${KEI_MEMORY_REPO_PATH:-}"
|
||||
QUEUE_SH="${HOME}/.claude/agents/_primitives/kei-sleep-queue.sh"
|
||||
|
||||
if [ -z "$REPO_PATH" ] || [ ! -d "$REPO_PATH/.git" ] || [ ! -x "$QUEUE_SH" ]; then
|
||||
printf 'v0.11 sleep-sync not configured — run `/sleep-setup` first, then retry.\n'
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
If either check fails, exit the wizard. Do not offer offline queue mode.
|
||||
|
||||
## 1b — Free-text prompt
|
||||
|
||||
Emit a plain chat message (NOT `AskUserQuestion` — a free-text message
|
||||
is fine when it is the only typed field and has a trivial non-empty
|
||||
validator):
|
||||
|
||||
> What are you sleeping on? One or two sentences — the nightly agent
|
||||
> will read this verbatim. Examples:
|
||||
>
|
||||
> - "Should I pick CfC or a small transformer as the memory re-ranker?"
|
||||
> - "Compare SvelteKit, Astro, and Next.js App Router for the kit's landing page."
|
||||
> - "What pattern in recent audit-backlog entries has the highest fix-value-per-effort?"
|
||||
|
||||
Store the reply as `TASK_TEXT`.
|
||||
|
||||
## 1c — Validate
|
||||
|
||||
- Reject if `TASK_TEXT` is empty or only whitespace.
|
||||
- Reject if `TASK_TEXT` > 4000 characters (keep queue files small; the
|
||||
agent has 15 minutes wall-clock anyway — a novella does not help).
|
||||
|
||||
On reject, print
|
||||
|
||||
```
|
||||
Task description must be non-empty and <= 4000 chars. Try again?
|
||||
```
|
||||
|
||||
and re-prompt. Up to 3 attempts; on the 3rd empty submission abort the
|
||||
wizard with a short "try again with `/sleep-on-it`" message.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- `TASK_TEXT` is non-empty and <= 4000 chars.
|
||||
- The v0.11 sync pipeline is wired (REPO_PATH + QUEUE_SH exist).
|
||||
- Exactly ZERO `AskUserQuestion` in this phase (free-text message only).
|
||||
71
skills/sleep-on-it/phase-2-type.md
Normal file
71
skills/sleep-on-it/phase-2-type.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Phase 2 — Task type (click)
|
||||
|
||||
Map the free-text task to one of five dispatch categories. The remote
|
||||
agent's Phase A uses this to pick the right tool chain.
|
||||
|
||||
## 2a — Click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "How should the nightly agent approach this task?",
|
||||
"header": "Type",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{
|
||||
"label": "Deep research",
|
||||
"description": "WebSearch + WebFetch + synthesis — 3+ searches, 2+ page fetches, structured report"
|
||||
},
|
||||
{
|
||||
"label": "Pipeline design",
|
||||
"description": "Architect + critic sequence — 5-7 phases with verify-criteria and tradeoffs"
|
||||
},
|
||||
{
|
||||
"label": "Pattern analysis",
|
||||
"description": "Query kei-memory + past reports — extract trends across sessions, propose action"
|
||||
},
|
||||
{
|
||||
"label": "Comparative study",
|
||||
"description": "Pros/cons matrix across N options the user lists — weighted recommendation"
|
||||
},
|
||||
{
|
||||
"label": "Custom",
|
||||
"description": "Follow the task text verbatim — no dispatch tool, free-form response"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 2b — Normalise
|
||||
|
||||
Map the clicked label to a compact token the queue file stores:
|
||||
|
||||
| Label | Token |
|
||||
|---|---|
|
||||
| Deep research | `deep` |
|
||||
| Pipeline design | `pipeline` |
|
||||
| Pattern analysis | `pattern` |
|
||||
| Comparative study | `compare` |
|
||||
| Custom | `custom` |
|
||||
|
||||
Store as `TASK_TYPE`.
|
||||
|
||||
## 2c — Soft nudge on mismatch
|
||||
|
||||
If `TASK_TYPE == "custom"` AND `TASK_TEXT` contains any of
|
||||
`should I | compare | trade[- ]off | which is better`, print a soft hint:
|
||||
|
||||
> Your task text looks like a comparison — `compare` or `deep` usually
|
||||
> produce a stronger result than `custom`. Proceeding anyway.
|
||||
|
||||
Do NOT re-ask. One nudge, user keeps control.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- `TASK_TYPE ∈ {deep, pipeline, pattern, compare, custom}`.
|
||||
- Exactly ONE `AskUserQuestion` in this phase.
|
||||
130
skills/sleep-on-it/phase-3-priority.md
Normal file
130
skills/sleep-on-it/phase-3-priority.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# Phase 3 — Priority & time budget (click)
|
||||
|
||||
Decide how much night-time the remote agent spends on this task.
|
||||
Priority maps to a wall-clock budget; the pipeline reads the budget
|
||||
from frontmatter, so a hard equation can own the whole night while a
|
||||
quick lookup is boxed to 15 minutes.
|
||||
|
||||
## 3a — Click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "How much night-time should this task get?",
|
||||
"header": "Priority",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{
|
||||
"label": "Quick",
|
||||
"description": "15 min, this night — simple questions, fast lookups"
|
||||
},
|
||||
{
|
||||
"label": "Standard",
|
||||
"description": "60 min, this night — default, medium research"
|
||||
},
|
||||
{
|
||||
"label": "Deep",
|
||||
"description": "4 hour, this night — serious derivations, thorough prior-art"
|
||||
},
|
||||
{
|
||||
"label": "Marathon",
|
||||
"description": "Full night, 1 task only — hard equations, full autonomy; Phase B REM skipped this night"
|
||||
},
|
||||
{
|
||||
"label": "Weekly batch",
|
||||
"description": "60 min, processed next Sunday UTC — non-urgent research"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3b — Marathon confirmation
|
||||
|
||||
If `LABEL == "Marathon"`, emit ONE more `AskUserQuestion` so the user
|
||||
consciously accepts the cost:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "Marathon = this task owns the whole night. Phase B REM consolidation is skipped. Other queue tasks deferred to next night. Confirm?",
|
||||
"header": "Marathon",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Yes, marathon", "description": "Take the full night; defer everything else"},
|
||||
{"label": "No, downgrade to Deep (4 hour)", "description": "Still a long run but Phase B and other tasks proceed"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If the user picks "No, downgrade to Deep (4 hour)", treat the effective
|
||||
label as `Deep` for the rest of the pipeline.
|
||||
|
||||
## 3c — Normalise
|
||||
|
||||
Map the final label (after any marathon downgrade) to four variables:
|
||||
|
||||
| Label | `PRIORITY_LABEL` | `TIME_BUDGET_MINUTES` | `CHECKPOINT_EVERY_MINUTES` | `MARATHON` |
|
||||
|---------------|------------------|-----------------------|----------------------------|------------|
|
||||
| Quick | `quick` | 15 | 0 (off) | `false` |
|
||||
| Standard | `standard` | 60 | 20 | `false` |
|
||||
| Deep | `deep` | 240 | 30 | `false` |
|
||||
| Marathon | `marathon` | 480 | 30 | `true` |
|
||||
| Weekly batch | `weekly` | 60 | 20 | `false` |
|
||||
|
||||
Store all four as phase-scoped variables. They flow to Phase 5, which
|
||||
passes them to `kei-sleep-queue.sh add`.
|
||||
|
||||
## 3d — Cap check (informational)
|
||||
|
||||
If `PRIORITY_LABEL ∈ {quick, standard, deep, marathon}` (i.e. this
|
||||
night), count current this-night pending tasks:
|
||||
|
||||
```bash
|
||||
~/.claude/agents/_primitives/kei-sleep-queue.sh list --pending \
|
||||
| awk '$4 ~ /^(quick|standard|deep|marathon)$/' \
|
||||
| wc -l
|
||||
```
|
||||
|
||||
Informational messages:
|
||||
|
||||
- **Marathon already queued this night:**
|
||||
> A marathon task is already pending for tonight. Submitting a second
|
||||
> marathon — or any this-night task — will be deferred to the next
|
||||
> night, because the marathon owns the whole window.
|
||||
|
||||
- **Greedy-pack near-full (≥ 480 min queued this-night):**
|
||||
> Tonight's this-night budget is nearly full (≥ 8 hours queued). New
|
||||
> this-night tasks will still be accepted but may be deferred to the
|
||||
> next night by the greedy-packing scheduler.
|
||||
|
||||
Do NOT re-prompt; the user may explicitly want overflow.
|
||||
|
||||
## 3e — Advanced overrides (informational)
|
||||
|
||||
After Phase 5 preview, explicit flags override the priority defaults:
|
||||
|
||||
```
|
||||
kei-sleep-queue add --time-budget <N>m # e.g. --time-budget 90m
|
||||
--checkpoint-every <M>m # e.g. --checkpoint-every 15m
|
||||
--no-timeout # time_budget_minutes: null
|
||||
--marathon # explicit marathon flag
|
||||
```
|
||||
|
||||
The wizard does not emit these flags itself; they exist for power users
|
||||
who call the helper directly.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- `PRIORITY_LABEL ∈ {quick, standard, deep, marathon, weekly}`.
|
||||
- `TIME_BUDGET_MINUTES ∈ {15, 60, 240, 480}` per the table.
|
||||
- `CHECKPOINT_EVERY_MINUTES ∈ {0, 20, 30}` per the table.
|
||||
- `MARATHON` is boolean and `true` iff `PRIORITY_LABEL == "marathon"`.
|
||||
- At most TWO `AskUserQuestion` calls (second only on marathon path).
|
||||
63
skills/sleep-on-it/phase-4-format.md
Normal file
63
skills/sleep-on-it/phase-4-format.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Phase 4 — Output format (click)
|
||||
|
||||
Tell the remote agent how to shape `sync-repo/sleep-results/<uuid>.md`.
|
||||
|
||||
## 4a — Click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "What format should the result use?",
|
||||
"header": "Format",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{
|
||||
"label": "Structured markdown report",
|
||||
"description": "Sections + findings + sources — default, best for research / pattern analysis"
|
||||
},
|
||||
{
|
||||
"label": "ADR-style decision record",
|
||||
"description": "Context / Decision / Consequences — best for pipeline-design output"
|
||||
},
|
||||
{
|
||||
"label": "Checklist / action items",
|
||||
"description": "`- [ ] item` list — best when you want a morning TODO"
|
||||
},
|
||||
{
|
||||
"label": "Pros/cons table",
|
||||
"description": "Markdown table with weighted criteria — best for comparative study"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 4b — Normalise
|
||||
|
||||
| Label | Token |
|
||||
|---|---|
|
||||
| Structured markdown report | `md` |
|
||||
| ADR-style decision record | `adr` |
|
||||
| Checklist / action items | `checklist` |
|
||||
| Pros/cons table | `table` |
|
||||
|
||||
Store as `FORMAT`.
|
||||
|
||||
## 4c — Coherence hint
|
||||
|
||||
Soft hint only (no re-ask), when type and format drift apart:
|
||||
|
||||
- `TASK_TYPE == compare` and `FORMAT != table` → hint that `table` is the usual pick.
|
||||
- `TASK_TYPE == pipeline` and `FORMAT != adr` → hint that `adr` is the usual pick.
|
||||
- `TASK_TYPE == pattern` and `FORMAT != checklist` → hint that `checklist` often reads best.
|
||||
|
||||
Format: single line, does not block.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- `FORMAT ∈ {md, adr, checklist, table}`.
|
||||
- Exactly ONE `AskUserQuestion` in this phase.
|
||||
121
skills/sleep-on-it/phase-5-submit.md
Normal file
121
skills/sleep-on-it/phase-5-submit.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# Phase 5 — Preview and submit (click)
|
||||
|
||||
Show the user exactly what will be written, then submit via the helper.
|
||||
|
||||
## 5a — Render preview
|
||||
|
||||
Print a fenced block with the frontmatter + body preview:
|
||||
|
||||
```
|
||||
---
|
||||
uuid: <generated-after-submit>
|
||||
submitted_at: <generated-after-submit>
|
||||
type: <TASK_TYPE>
|
||||
priority: <PRIORITY_LABEL>
|
||||
format: <FORMAT>
|
||||
time_budget_minutes: <TIME_BUDGET_MINUTES>
|
||||
checkpoint_every_minutes: <CHECKPOINT_EVERY_MINUTES>
|
||||
marathon: <MARATHON>
|
||||
status: pending
|
||||
---
|
||||
|
||||
<TASK_TEXT>
|
||||
```
|
||||
|
||||
Then print a one-line wall-clock estimate:
|
||||
|
||||
```
|
||||
estimated wall-clock: <TIME_BUDGET_MINUTES> min
|
||||
```
|
||||
|
||||
If `MARATHON == true`, append an explicit warning line beneath the
|
||||
estimate:
|
||||
|
||||
```
|
||||
marathon: Phase B REM consolidation will be SKIPPED the night this
|
||||
task runs, and other queue items will be deferred to the next night.
|
||||
```
|
||||
|
||||
Tell the user the `uuid` and `submitted_at` fields are assigned by the
|
||||
helper on submit — the preview leaves them as placeholders.
|
||||
|
||||
## 5b — Click
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "Submit this to the nightly queue?",
|
||||
"header": "Submit",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Submit", "description": "Write queue file + push to memory-repo"},
|
||||
{"label": "Edit", "description": "Go back to Phase 1 and re-enter the task text"},
|
||||
{"label": "Abort", "description": "Drop the draft; nothing is written"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Store the pick as `SUBMIT_ACTION`.
|
||||
|
||||
## 5c — Dispatch
|
||||
|
||||
- `SUBMIT_ACTION == "Edit"` → restart from Phase 1 (clears all variables).
|
||||
- `SUBMIT_ACTION == "Abort"` → print `submission cancelled` and exit.
|
||||
- `SUBMIT_ACTION == "Submit"` → call the helper (see 5d).
|
||||
|
||||
## 5d — Invoke `kei-sleep-queue.sh add`
|
||||
|
||||
Write the task text to a temp file, then:
|
||||
|
||||
```bash
|
||||
PROMPT_FILE="$(mktemp)"
|
||||
printf '%s\n' "$TASK_TEXT" > "$PROMPT_FILE"
|
||||
MARATHON_FLAG=""
|
||||
[ "$MARATHON" = "true" ] && MARATHON_FLAG="--marathon"
|
||||
OUTPUT="$(
|
||||
~/.claude/agents/_primitives/kei-sleep-queue.sh add \
|
||||
--type "$TASK_TYPE" \
|
||||
--priority "$PRIORITY_LABEL" \
|
||||
--format "$FORMAT" \
|
||||
--time-budget "${TIME_BUDGET_MINUTES}m" \
|
||||
--checkpoint-every "${CHECKPOINT_EVERY_MINUTES}m" \
|
||||
$MARATHON_FLAG \
|
||||
--prompt-file "$PROMPT_FILE" 2>&1
|
||||
)"
|
||||
STATUS=$?
|
||||
rm -f "$PROMPT_FILE"
|
||||
```
|
||||
|
||||
The helper prints two lines on success:
|
||||
|
||||
```
|
||||
<uuid>
|
||||
<absolute path of queue file>
|
||||
```
|
||||
|
||||
Capture `UUID = first line`, `QUEUE_PATH = second line`.
|
||||
|
||||
On non-zero exit, surface stderr verbatim. Common causes:
|
||||
|
||||
- **genesis-scan hit** → task text contains a patent-sensitive term.
|
||||
Constructive paths (RULE -1):
|
||||
1. Rewrite the task generically ("pick between <redacted>"
|
||||
becomes "pick between technology A and B").
|
||||
2. Set `KEI_SLEEP_GENESIS_BYPASS=1` and re-run if the term is a
|
||||
false positive (cite the specific word).
|
||||
3. Abort; keep the question local.
|
||||
- **write failed** (disk / permissions) → print the error; exit.
|
||||
- **sync push failed after local write succeeded** → not an error; the
|
||||
queue file IS committed locally and will push on next session end.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- `SUBMIT_ACTION ∈ {Submit, Edit, Abort}`.
|
||||
- If `Submit`, `UUID` is a non-empty string and `QUEUE_PATH` ends in
|
||||
`.md` under `sync-repo/sleep-queue/`.
|
||||
- Exactly ONE `AskUserQuestion` in this phase.
|
||||
74
skills/sleep-on-it/phase-6-ack.md
Normal file
74
skills/sleep-on-it/phase-6-ack.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Phase 6 — Acknowledgment (click)
|
||||
|
||||
Show the user what was written, how to inspect it, and when the agent
|
||||
picks it up.
|
||||
|
||||
## 6a — Resolve next-run ETA
|
||||
|
||||
Read the cron expression the `/schedule` trigger uses. The helper keeps
|
||||
the URL in `sync-repo/.keisei-sync.toml`; the cron target itself is in
|
||||
the user's Claude Code `/schedule list`. We do not parse the scheduler
|
||||
from this skill (no portable API) — instead, print the canonical line:
|
||||
|
||||
```bash
|
||||
grep -E '^schedule_utc_cron' "$REPO_PATH/.keisei-sync.toml" 2>/dev/null \
|
||||
|| printf 'schedule_utc_cron = "unknown — run /schedule list to verify"\n'
|
||||
```
|
||||
|
||||
If the key is absent (older sync-repo), print the fallback line
|
||||
verbatim. Do not fabricate a time (RULE 0.4).
|
||||
|
||||
## 6b — Print acknowledgment block
|
||||
|
||||
Emit this block to chat:
|
||||
|
||||
```
|
||||
Queued.
|
||||
|
||||
UUID: <UUID>
|
||||
File: <QUEUE_PATH>
|
||||
Type: <TASK_TYPE>
|
||||
Priority: <PRIORITY>
|
||||
Format: <FORMAT>
|
||||
Next run: <cron line from 6a>
|
||||
Results at: <REPO_PATH>/sleep-results/<UUID>.md (after the next run)
|
||||
|
||||
Inspect: `kei-sleep-queue show <UUID>`
|
||||
List all: `kei-sleep-queue list --pending`
|
||||
Cancel: delete the file at the path above before the next run
|
||||
```
|
||||
|
||||
## 6c — Click (final)
|
||||
|
||||
Emit ONE `AskUserQuestion`:
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "What now?",
|
||||
"header": "Done",
|
||||
"multiSelect": false,
|
||||
"options": [
|
||||
{"label": "Show queue", "description": "Run `kei-sleep-queue list --pending`"},
|
||||
{"label": "Submit another", "description": "Restart the wizard"},
|
||||
{"label": "Done", "description": "Close the wizard"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Handle each option:
|
||||
|
||||
- `Show queue` → shell out to `kei-sleep-queue list --pending` and
|
||||
print the table; then re-emit this click.
|
||||
- `Submit another` → restart the wizard from Phase 1.
|
||||
- `Done` → emit the final report from `SKILL.md` and exit.
|
||||
|
||||
## Verify-criterion
|
||||
|
||||
- The acknowledgment block in 6b was printed with real values (no `<UUID>`
|
||||
placeholders left).
|
||||
- Exactly ONE `AskUserQuestion` in this phase (plus loops on "Show queue").
|
||||
- The final report block from `SKILL.md` was emitted on `Done`.
|
||||
Loading…
Reference in a new issue