From 27663d48e9bf5bec4c052ff40b36cdbc93602dd8 Mon Sep 17 00:00:00 2001 From: Gabriel Tavares Date: Sat, 23 May 2026 19:12:43 +0100 Subject: [PATCH 1/3] claude-code-hermit: bypass /loop cloud-schedule prompt in heartbeat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC 2.1.150 added an operator-facing "Cloud schedule vs session-only" modal to /loop, blocking the always-on bootstrap. Switch heartbeat start/stop/status to use CronCreate/CronList/CronDelete directly — same local in-session semantics, no prompt. Cron expressions use off-minutes to avoid fleet clustering. --- plugins/claude-code-hermit/CHANGELOG.md | 6 ++++++ .../skills/heartbeat/SKILL.md | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/claude-code-hermit/CHANGELOG.md b/plugins/claude-code-hermit/CHANGELOG.md index c17c009c..30e182a8 100644 --- a/plugins/claude-code-hermit/CHANGELOG.md +++ b/plugins/claude-code-hermit/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Fixed + +- **heartbeat: schedule via `CronCreate` instead of `/loop`** — Claude Code 2.1.150's new "Cloud schedule" prompt inside `/loop` was blocking always-on bootstrap. + ## [1.1.3] - 2026-05-23 ### Fixed diff --git a/plugins/claude-code-hermit/skills/heartbeat/SKILL.md b/plugins/claude-code-hermit/skills/heartbeat/SKILL.md index 85dd9d40..72655fdd 100644 --- a/plugins/claude-code-hermit/skills/heartbeat/SKILL.md +++ b/plugins/claude-code-hermit/skills/heartbeat/SKILL.md @@ -51,22 +51,29 @@ Execute one heartbeat tick. ### start -Start a recurring heartbeat loop using `/loop`. +Start a recurring heartbeat tick using `CronCreate`. 1. Read `heartbeat.every` from config (default: `"2h"`). -2. Invoke `/loop /claude-code-hermit:heartbeat run`. +2. Convert the interval to a 5-field cron expression using an off-minute (never `:00`, never `:30`) so a fleet of hermits doesn't cluster on the same wall-clock moment: + - `30m` → `7,37 * * * *` + - `Nh` (N≥1) → `7 */N * * *` (e.g. `1h` → `7 * * * *`, `2h` → `7 */2 * * *`) + - `Nd` → `7 4 */N * *` + - Any other `Nm` value: use `*/N * * * *` and proceed — `CronCreate` accepts non-clean steps without error. +3. Call `CronList` and delete any existing task whose prompt is `/claude-code-hermit:heartbeat run` (via `CronDelete`). Idempotent: safe to re-run from `heartbeat-restart` to reset the 7-day expiry. +4. Call `CronCreate` with `cron` set to the expression from step 2, `prompt` set to `/claude-code-hermit:heartbeat run`, and `recurring: true`. +5. Append to SHELL.md Monitoring: `[HH:MM] Heartbeat: started (every , cron , task )`. -If a heartbeat loop is already running, cancel it first then start fresh. Safe to call from a routine — resets the 3-day `/loop` expiry without losing any state. +We use `CronCreate` directly rather than `/loop` because Claude Code 2.1.150 added an operator-facing "Cloud schedule vs This session only" prompt inside `/loop` that blocks the always-on bootstrap. `CronCreate` is the same local in-session scheduler `/loop` wraps — same runtime semantics, no prompt. ### stop -1. Stop the active `/loop`. +1. Call `CronList`. Delete every task whose prompt is `/claude-code-hermit:heartbeat run` via `CronDelete`. 2. Append to SHELL.md Monitoring: `[HH:MM] Heartbeat: stopped`. ### status Report current heartbeat state: -- Loop running (yes/no), configured interval, active hours window, last tick time and result, show_ok setting. +- Call `CronList` and find the task whose prompt is `/claude-code-hermit:heartbeat run`. Report: running (yes/no), cron expression, task ID, configured interval, active hours window, last tick time and result, show_ok setting. ### edit From ed9009e61add8b66ae235bd0384bceba9a00f75f Mon Sep 17 00:00:00 2001 From: Gabriel Tavares Date: Sat, 23 May 2026 19:19:30 +0100 Subject: [PATCH 2/3] chore: add simplify skill, update commit skill --- .claude/skills/commit/SKILL.md | 4 +- .claude/skills/simplify/SKILL.md | 183 +++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 .claude/skills/simplify/SKILL.md diff --git a/.claude/skills/commit/SKILL.md b/.claude/skills/commit/SKILL.md index f7c3f366..27df23b4 100644 --- a/.claude/skills/commit/SKILL.md +++ b/.claude/skills/commit/SKILL.md @@ -1,11 +1,11 @@ --- name: commit -description: Changelog and commit — lightweight motion for day-to-day plugin dev work in the monorepo. One commit per plugin scope; CHANGELOG and staging routed by detected slug. Code-review is handled upstream by /dev-quality before committing. Trigger when the user says "commit", "commit this", "save this", "wrap this up", "let's commit", or finishes a change and wants to capture it. NOT for releases, version bumps, or pushing — defer to /release for those. Always run this before the user can walk away from an incomplete change. +description: Changelog and commit — lightweight motion for day-to-day plugin dev work in the monorepo. One commit per plugin scope; CHANGELOG and staging routed by detected slug. Trigger when the user says "commit", "commit this", "save this", "wrap this up", "let's commit", or finishes a change and wants to capture it. NOT for releases, version bumps, or pushing — defer to /release for those. Always run this before the user can walk away from an incomplete change. --- # Commit -Detect which plugin's scope this change belongs to, append a changelog line in that plugin's CHANGELOG, then commit. No push, no tag, no version bump — that's `/release`'s job. Code-review runs upstream via `/dev-quality` before commit. +Detect which plugin's scope this change belongs to, append a changelog line in that plugin's CHANGELOG, then commit. No push, no tag, no version bump — that's `/release`'s job. ## Guardrails (check before starting) diff --git a/.claude/skills/simplify/SKILL.md b/.claude/skills/simplify/SKILL.md new file mode 100644 index 00000000..4f3ae108 --- /dev/null +++ b/.claude/skills/simplify/SKILL.md @@ -0,0 +1,183 @@ +--- +name: simplify +description: Parallel code review and cleanup of recent changes. Replicates the original bundled /simplify command that was removed in CC v2.1.146. +argument-hint: [optional focus, e.g. "memory efficiency"] +--- + +# Simplify — parallel review, sequential apply + +Spawns three focused review subagents in parallel, each returning a list of suggested edits. The main agent then applies them sequentially, resolving conflicts deterministically using the Principles below. The skill is **non-interactive**: it never stops to ask the user. When the Principles don't pick a clear winner, the finding is logged as "not applied" rather than surfaced as a question — the user reads the final report and re-engages if they want one of the rejected proposals. + +**Why review-then-apply and not apply-in-parallel:** if three subagents Edit the same files concurrently, they race. Late writers either fail loudly (Edit's `old_string` mismatch) or — worse — see "my change is already there" and silently skip, so most findings get dropped. Parallelizing the slow part (analysis) while serializing writes gives both speed and correctness. + +If `$ARGUMENTS` contains a focus hint (e.g. "memory efficiency"), pass it through to all three subagents so they weight that dimension. + +## Principles every reviewer must follow + +These are cross-cutting — they apply to every finding regardless of reviewer category. The main agent passes them into each reviewer's prompt. + +1. **Preserve behavior.** Any change to return values, exceptions, edge-case handling, or observable side effects is a *behavior change*, not a simplification. If a refactor alters output for any input — even malformed or "invalid" input the original happened to accept — it's not a cleanup, it's a redesign. Don't propose those as simplifications; if you spot one and think it's worth doing, label it explicitly so the user can decide. + +2. **Clarity over brevity.** Fewer lines is not the goal; a reader understanding the code at a glance is. Avoid nested ternaries when `if/elif/else` reads clearer. Don't wrap trivial operations in named helpers just to "reuse" something — `multiplyTwoNumbers(n, n)` is worse than `n * n`, not better. A short dense one-liner is worse than a two-line form a reader can follow on first pass. + +3. **Respect house conventions.** If the session's `CLAUDE.md` documents style rules or idiom preferences, they win over generic idioms — a reviewer suggesting f-strings is wrong if `CLAUDE.md` says use `.format()`. CLAUDE.md is the canonical place for project rules; subagents already see it in context, so no manual passthrough is needed. + +## Phase 1 — Capture changes + +1. Run `git status --short` to surface untracked new files (no diff command shows these). +2. Run `git diff HEAD` to capture unstaged + staged changes to tracked files. +3. If the diff is empty, run `git diff` against the merge-base. If still empty, fall back to recently modified or mentioned files in the current session. +4. For each untracked file from step 1, read its full contents and append as a synthetic `+++` block so the subagents see new files as additions. +5. If the combined diff is under ~20 lines AND covers a single concern, skip Phase 2 and just dispatch the most relevant single reviewer. Three reviewers on a six-line diff is overkill and produces noise. + +## Phase 2 — Launch THREE reviewers IN PARALLEL + +Dispatch three `Agent` tool calls in a single message. All use: + +- `subagent_type: "general-purpose"` +- `model: "sonnet"` — pin so cost stays predictable across parent session models + +Each reviewer receives the full diff and the focus hint (if any), and returns proposed edits as JSON. **Reviewers do NOT call Edit.** They report; the main agent applies. + +### Required return format + +Each reviewer must end its response with a fenced JSON block: + +```json +{ + "findings": [ + { + "file": "absolute/path/to/file", + "old_string": "exact text to replace (must be unique in the file or include enough context to be unique)", + "new_string": "replacement text", + "rationale": "one short sentence — what was wrong, why this is better", + "confidence": "high" | "medium" | "low" + } + ] +} +``` + +Empty findings → `{"findings": []}`. Don't pad. + +### 1 — Code Reuse Reviewer + +> Review the following diff for missed reuse opportunities. Search the WIDER codebase (not just the diff) using Grep to find: +> - New functions that duplicate existing utilities or helpers +> - Inline logic that should use existing utilities: hand-rolled string manipulation, manual path handling, custom env checks, ad-hoc type guards +> - Similar patterns in utility directories, shared modules, and adjacent files +> +> **Principles** (apply to every finding): +> - **Preserve behavior.** If your proposed change alters return values, exceptions, side effects, or edge-case handling for *any* input — including malformed input — it's a redesign, not a simplification. Don't propose it as a cleanup; if worth doing, flag it as a behavior change. +> - **Clarity > brevity.** Wrapping `n * n` in `multiplyTwoNumbers(n, n)` is not reuse, it's worse code. Only propose reuse when the existing helper genuinely captures a non-trivial pattern. +> - **Respect house conventions.** Project conventions (below) override generic idioms. +> +> **Do NOT use Edit, Write, or any file-modification tool.** Your job is to propose edits, not apply them. Return findings as a JSON block per the schema above. Skip false positives silently. +> +> FOCUS HINT: {hint or "none"} +> +> DIFF: +> {paste full diff here} + +### 2 — Code Quality Reviewer + +> Review the following diff for quality issues. Look for: +> - Redundant state: state duplicating existing state, cached values that could be derived, observers/effects that could be direct calls +> - Parameter sprawl: new parameters added instead of generalizing or restructuring +> - Copy-paste with slight variation: near-duplicate blocks that should share an abstraction +> - Leaky abstractions: exposing internals, breaking existing abstraction boundaries +> - Stringly-typed code: raw strings where constants, enums, or branded types already exist +> - Verbose patterns: unnecessary intermediate variables, `== true`/`== false`, redundant else after return, multi-check null guards that collapse to one expression +> +> **Principles** (apply to every finding): +> - **Preserve behavior.** If your refactor alters return values, exceptions, side effects, or edge-case handling for *any* input, it's a behavior change — flag it as such, don't dress it up as a cleanup. +> - **Clarity > brevity.** Don't trade readability for fewer lines. Nested ternaries, dense one-liners, chained mystery operators are not improvements over clear `if/else` blocks. A reader should grok the rewrite faster than the original. +> - **Respect house conventions.** Project conventions (below) override generic idioms. +> +> **Do NOT use Edit, Write, or any file-modification tool.** Your job is to propose edits, not apply them. Return findings as a JSON block per the schema above. +> +> FOCUS HINT: {hint or "none"} +> +> DIFF: +> {paste full diff here} + +### 3 — Efficiency Reviewer + +> Review the following diff for efficiency issues. NO premature optimization. Look for: +> - Unnecessary work: redundant computations, repeated file reads, duplicate API calls, N+1 patterns +> - Missed concurrency: independent operations run sequentially +> - Hot-path bloat: blocking work on startup or per-request paths +> - TOCTOU anti-patterns: pre-checking existence before operating instead of operating and handling errors +> - Memory: unbounded data structures, missing cleanup, event listener leaks +> - Overly broad operations: reading entire files when only portions needed +> +> **Principles** (apply to every finding): +> - **Preserve behavior.** A faster algorithm that handles edge cases differently is a behavior change, not an optimization. Flag it as such. +> - **Clarity > brevity.** A single-pass loop is only better than two-pass if it stays readable. If merging passes makes the control flow harder to follow, the two-pass version was fine. +> - **Respect house conventions.** Project conventions (below) override generic idioms. +> +> **Do NOT use Edit, Write, or any file-modification tool.** Your job is to propose edits, not apply them. Return findings as a JSON block per the schema above. +> +> FOCUS HINT: {hint or "none"} +> +> DIFF: +> {paste full diff here} + +## Phase 3 — Merge and apply (sequential) + +Once all three reviewers return: + +### 3a. Collect, group, and resolve same-region findings + +Parse the JSON blocks. If one reviewer's block fails to parse, log it and continue with the others — don't abort the whole run. Group findings by `(file, old_string)`. For each group with more than one finding, compare the `new_string` values: + +- **Byte-identical or semantically equivalent `new_string`** (e.g., trivial renames like `u for u in ...` vs `user for user in ...`) → silent dedupe. Keep the one with higher confidence; if tied, Code Quality > Code Reuse > Efficiency (Quality fixes tend to be the most local and least risky). If you're unsure whether two rewrites are equivalent, treat them as different and escalate. +- **Meaningfully different `new_string`** (different algorithm, different control flow, different intermediates) → check the **Principles** against each variant: + - Does one variant respect *preserve behavior* while the other changes output on some input? → pick the behavior-preserving one. Log the rejected variant + its behavior change in the report (under "Noticed but not applied") so the user can opt in later if they want. + - Does one variant respect *clarity > brevity* while the other is denser/cleverer for no gain? → pick the clearer one. Log the rejected variant. + - Does one variant respect *house conventions* (per CLAUDE.md) while the other doesn't? → pick the conforming one. + + If the principles **don't discriminate** — both options preserve behavior identically, both are equally clear, both respect conventions — apply **neither**. Log them under "Noticed but not applied: principles couldn't decide" so the user can pick if they care. + + Never stop to ask. Surfacing a style call to the user mid-run defeats the point of having principles. The user invoked `/simplify` to clean code, not to answer a quiz. + +### 3b. Group by file, sort by file order + +For each file, read it once and locate each finding's `old_string`. Sort findings by their offset so edits happen top-to-bottom (helps the user follow the diff in review). + +### 3c. Detect overlaps + +Two findings on the same file *overlap* if their `old_string` regions intersect. For each overlap group: +- If one strictly contains another, the broader rewrite usually subsumes the narrower one — but **check first** whether the broader version respects the Principles as well as the narrower. If the broader rewrite trades clarity for brevity, or quietly changes behavior on an edge case the narrower one preserves, pick the narrower (and log the broader as rejected on principle grounds). +- If they intersect but neither contains the other, apply the same Principles check from 3a. If principles discriminate, pick the principle-aligned one and log the rejected variant. If they don't, apply **neither** and log both under "Noticed but not applied". + +### 3d. Apply, top to bottom + +For each non-conflicting finding, in file order: +1. Re-read the target file (or the relevant section) to confirm `old_string` still appears verbatim. Earlier edits or external changes can shift content; checking proactively is cheaper than recovering from an Edit failure. +2. If it still matches, call `Edit` with the `old_string` and `new_string`. +3. If it no longer matches, skip silently — the finding was subsumed by an earlier edit. +4. If `Edit` fails for any other reason, surface the error and stop on that file. + +### 3e. Report + +Print a concise per-file summary, then "noticed but not applied", then totals: + +``` +path/to/file.ts + ✓ [Quality] removed `=== true` comparison on line 31 + ✓ [Reuse] replaced manual loop with `.reduce(...)` on line 53 + ⊘ [Efficiency] skipped — old_string no longer matches (subsumed) + +Noticed but not applied: + ⚠ [Quality] proposed compound rewrite of parse_user_input (lines 70-74) — + rejected on principle "clarity > brevity" (denser, calls strip() twice). + To apply: ask explicitly. + ⚠ [Reuse] proposed `email.partition("@")` with None return for no-@ inputs + (lines 95-99) — behavior change vs original (`""`). To apply: ask explicitly. + +Totals: applied N · deduped M · principle-rejected K · stale-anchor skips L · parse failures P +``` + +The "Noticed but not applied" section is how the user discovers rejected proposals and can opt in. The skill never blocks waiting for an answer. + +No essays. Just what changed, what didn't, and why. From 9a59479777ac8ad58fb533dcc0e164a5cbc9e814 Mon Sep 17 00:00:00 2001 From: Gabriel Tavares Date: Sat, 23 May 2026 19:43:10 +0100 Subject: [PATCH 3/3] claude-code-hermit: replace stale /loop references in docs and skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up doc sweep after the heartbeat CronCreate migration (27663d4). Updates architecture.md, always-on.md, always-on-ops.md (table + prose), heartbeat/SKILL.md Usage block, and session-close/SKILL.md to use "tick" / "CronCreate" / "7-day expiry" consistently. No operator behavior change — existing [Unreleased] bullet covers the user-facing aspect of the migration. --- plugins/claude-code-hermit/docs/always-on-ops.md | 4 ++-- plugins/claude-code-hermit/docs/always-on.md | 2 +- plugins/claude-code-hermit/docs/architecture.md | 2 +- plugins/claude-code-hermit/skills/heartbeat/SKILL.md | 6 +++--- plugins/claude-code-hermit/skills/session-close/SKILL.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/claude-code-hermit/docs/always-on-ops.md b/plugins/claude-code-hermit/docs/always-on-ops.md index b0b68791..9b94de28 100644 --- a/plugins/claude-code-hermit/docs/always-on-ops.md +++ b/plugins/claude-code-hermit/docs/always-on-ops.md @@ -183,7 +183,7 @@ Manage with `/claude-code-hermit:hermit-settings routines`. Changes take effect CronCreate fires only between REPL turns — never mid-task. There is no queue: if Claude is mid-task when the cron time hits, the fire is deferred (not dropped) until idle. `run_during_waiting: false` routines additionally check `runtime.json` and self-suppress with a `skipped-waiting` event when `session_state == "waiting"`. -**`heartbeat-restart`** fires at 4am daily and restarts both the heartbeat `/loop` and the routine registrations (CronCreate auto-expires after 7 days; daily re-arm keeps everything fresh). +**`heartbeat-restart`** fires at 4am daily and restarts both the heartbeat tick and the routine registrations (CronCreate auto-expires after 7 days; daily re-arm keeps everything fresh). Inspect live registrations with `/claude-code-hermit:hermit-routines status` (calls `CronList` under the hood). Inspect fire history with `tail .claude-code-hermit/state/routine-metrics.jsonl`. @@ -194,7 +194,7 @@ Inspect live registrations with `/claude-code-hermit:hermit-routines status` (ca | Timing | Exact cron schedule | Every N minutes | Event-driven or interval | | Engine | CronCreate (idle-gated) | LLM evaluation | Monitor tool (OS subprocess) | | Cost | Zero tokens until fire | Checklist evaluation per tick | Zero tokens when quiet | -| Survives exit | No (re-registered on launch) | No (3-day `/loop` expiry) | No (session-scoped) | +| Survives exit | No (re-registered on launch) | No (7-day CronCreate expiry) | No (session-scoped) | | Mid-task fire | Deferred until idle | N/A | Yes (interrupts) | | Use for | Scheduled tasks (briefs, audits) | Continuous monitoring | Reactive watching / quiet polling | diff --git a/plugins/claude-code-hermit/docs/always-on.md b/plugins/claude-code-hermit/docs/always-on.md index c80f1f54..08540f4a 100644 --- a/plugins/claude-code-hermit/docs/always-on.md +++ b/plugins/claude-code-hermit/docs/always-on.md @@ -192,7 +192,7 @@ Heartbeat archives the session when SHELL.md has been idle for more than 12 hour 1. `heartbeat-precheck.js` checks SHELL.md mtime on each tick 2. If idle >12h, returns the `AUTO_CLOSE` verdict 3. Heartbeat appends `[HH:MM] Heartbeat: auto-closed after 12h quiet.` to SHELL.md Monitoring (so the trace lands in the archived report, not the next session) -4. Invokes `/session-close --auto` — bypasses the operator-summary prompt and skips reflect; the heartbeat loop continues +4. Invokes `/session-close --auto` — bypasses the operator-summary prompt and skips reflect; the heartbeat tick continues 5. The report is archived with frontmatter `closed_via: auto` `weekly-review` includes auto-archived sessions in cost/session totals but excludes them from the autonomy-rate denominator (with an inline "(N auto-archived excluded)" note). Reflect skips them when scanning archive evidence, preventing mtime-triggered false compute-phase runs. No configuration is needed — the trigger is fixed at 12h. diff --git a/plugins/claude-code-hermit/docs/architecture.md b/plugins/claude-code-hermit/docs/architecture.md index dc9914df..5f9efa3d 100644 --- a/plugins/claude-code-hermit/docs/architecture.md +++ b/plugins/claude-code-hermit/docs/architecture.md @@ -275,7 +275,7 @@ Hermit provides the **timing infrastructure** (when to reflect), the **proposal Morning routine (configurable time, default: active hours start + 30m): brief, proposal review, priority check, pending micro-proposals surfaced. Evening routine (configurable time, default: active hours end - 30m): daily journal archived as S-NNN, reflection, preparation for tomorrow. -Both are managed by `/claude-code-hermit:hermit-routines` (per-session CronCreate registrations). Fire at exact cron times. CronCreate is idle-gated: routines that come due during `in_progress` are deferred until the REPL is between turns — never dropped, never interrupting mid-task. A daily 4am `heartbeat-restart` routine re-runs `/claude-code-hermit:hermit-routines load` to re-arm both the heartbeat `/loop` (3-day expiry) and the routine CronCreates (7-day expiry). +Both are managed by `/claude-code-hermit:hermit-routines` (per-session CronCreate registrations). Fire at exact cron times. CronCreate is idle-gated: routines that come due during `in_progress` are deferred until the REPL is between turns — never dropped, never interrupting mid-task. A daily 4am `heartbeat-restart` routine re-runs `/claude-code-hermit:hermit-routines load` to re-arm both the heartbeat tick and the routine CronCreates (both 7-day expiry). --- diff --git a/plugins/claude-code-hermit/skills/heartbeat/SKILL.md b/plugins/claude-code-hermit/skills/heartbeat/SKILL.md index 72655fdd..042a9933 100644 --- a/plugins/claude-code-hermit/skills/heartbeat/SKILL.md +++ b/plugins/claude-code-hermit/skills/heartbeat/SKILL.md @@ -10,9 +10,9 @@ Background health checker that periodically evaluates a checklist and surfaces a ``` /claude-code-hermit:heartbeat run — execute one tick immediately -/claude-code-hermit:heartbeat start — start the recurring loop -/claude-code-hermit:heartbeat stop — stop the recurring loop -/claude-code-hermit:heartbeat status — show last result and loop state +/claude-code-hermit:heartbeat start — start the recurring tick +/claude-code-hermit:heartbeat stop — stop the recurring tick +/claude-code-hermit:heartbeat status — show last result and schedule state /claude-code-hermit:heartbeat edit — modify the checklist ``` diff --git a/plugins/claude-code-hermit/skills/session-close/SKILL.md b/plugins/claude-code-hermit/skills/session-close/SKILL.md index c0023df4..a8842edb 100644 --- a/plugins/claude-code-hermit/skills/session-close/SKILL.md +++ b/plugins/claude-code-hermit/skills/session-close/SKILL.md @@ -12,7 +12,7 @@ When invoked with `--auto` by heartbeat (after 12h SHELL.md inactivity), the ope Idle transitions happen automatically at task boundaries (handled by the `session` skill). By the time the operator runs `/session-close`, they want out. -If heartbeat is running, stop it before archiving. **Skip on `--auto`** — heartbeat is the caller; stopping its `/loop` would prevent all future ticks. +If heartbeat is running, stop it before archiving. **Skip on `--auto`** — heartbeat is the caller; stopping its CronCreate would prevent all future ticks. If watches are registered (`state/monitors.runtime.json` has entries), stop all watches before archiving — invoke `/claude-code-hermit:watch stop --all`. session-mgr handles updating both SHELL.md (cosmetic) and `state/runtime.json` (lifecycle truth) during archiving. For full shutdown, session-mgr sets `shutdown_completed_at` in runtime.json.