claude-slack-channel-bots 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,8 +47,8 @@ A single HTTP MCP server that runs several independent Claude Code bots, called
47
47
  ## Prerequisites
48
48
 
49
49
  - [Bun](https://bun.sh) `>= 1.0.21` (agent-director minimum)
50
- - [Claude Code](https://claude.ai/code) 2.1.280 or later for the workers, installed and authenticated: the minimum agent-director states for its exec-form hooks, and the version the fleet runs. A Claude Code too old for exec-form hooks (older than 2.1.139) has its hooks ignored, so every launch stays `pending`. The `claude` on the workers' `PATH` must be Claude Code's native binary or a wrapper that `exec`s it: a launcher or shim that runs Claude Code as a child process makes agent-director ignore every hook (`ad.hook.ignored` with the reason `pid_mismatch`), so every launch stays `pending`.
51
- - [`agent-director`](https://github.com/gabemahoney/agent-director) Phase 1 or later, **installed system-wide** as a prerequisite, like `git` or `docker`. The npm `agent-director` package CSCB depends on is the client only: it finds the system-installed binary at startup and refuses to start when the binary is missing, too old, or unreachable. This release requires agent-director Phase 1, installed on the host together with it, and the two are rolled back together; CSCB changes no agent-director code (see "Switching over to agent-director Phase 1" in [Migration](docs/migration.md#migration)). Every agent on the host, with every long-running agent-director process (`agent-director serve` included), is stopped before either binary change (the Phase 1 install, and the rollback's restore of the previous binary) and started again after it. The startup gate refuses a binary below the client's own minimum (`min_binary_version` in the client's `dist/version-floor.json`) or below agent-director Phase 1, and names the required version (see [Startup errors](docs/troubleshooting.md#startup-errors)). To check the install before you start, run `bun run install-check` (see [Checking your agent-director install](docs/agent-director.md#checking-your-agent-director-install)).
50
+ - [Claude Code](https://claude.ai/code) 2.1.296 or later for the workers, installed and authenticated: the minimum agent-director states for its exec-form hooks, and the version the fleet runs. A Claude Code too old for exec-form hooks (older than 2.1.139) has its hooks ignored, so every launch stays `pending`. The `claude` on the workers' `PATH` must be Claude Code's native binary or a wrapper that `exec`s it: a launcher or shim that runs Claude Code as a child process makes agent-director ignore every hook (`ad.hook.ignored` with the reason `pid_mismatch`), so every launch stays `pending`.
51
+ - [`agent-director`](https://github.com/gabemahoney/agent-director) 0.12.0 or later, **installed system-wide** as a prerequisite, like `git` or `docker`. The npm `agent-director` package CSCB depends on is the client only: it finds the system-installed binary at startup and refuses to start when the binary is missing, too old, or unreachable. This release pins the client to `agent-director@0.12.0` and requires the CLI at 0.12.0 or later, installed on the host together with it, and the two are rolled back together; CSCB changes no agent-director code (see "Switching over to agent-director Phase 1" in [Migration](docs/migration.md#migration)). Every agent on the host, with every long-running agent-director process (`agent-director serve` included), is stopped before either binary change (the Phase 1 install, and the rollback's restore of the previous binary) and started again after it. The startup gate refuses a binary below the client's own minimum (`min_binary_version` in the client's `dist/version-floor.json`) or below 0.12.0, and names the required version (see [Startup errors](docs/troubleshooting.md#startup-errors)). To check the install before you start, run `bun run install-check` (see [Checking your agent-director install](docs/agent-director.md#checking-your-agent-director-install)).
52
52
  - [tmux](https://github.com/tmux/tmux) 3.2 or later, with `remain-on-exit` off. agent-director runs it; CSCB starts no tmux process. agent-director does not depend on tmux's `base-index` or `pane-base-index`, so their values don't matter.
53
53
  - Slack workspace admin access (to create and configure one Slack app per persona)
54
54
  - **cozempic** (optional) — Python 3.10+ and `pip install cozempic` — used by JSONL path resolution helpers retained for downstream callers.
@@ -1,5 +1,7 @@
1
1
  # agent-director
2
2
 
3
+ CSCB requires agent-director CLI 0.12.0 or later and pins its TypeScript client to `agent-director@0.12.0`. Workers require Claude Code 2.1.296 or later. Upgrade the CLI with its released installer before upgrading CSCB; see [Migration](migration.md#migration).
4
+
3
5
  CSCB runs every bot through [agent-director](https://github.com/gabemahoney/agent-director), installed system-wide. This page covers checking that install, the `install-cscb` skill that walks you through a failed check, and the agent-director settings CSCB reads.
4
6
 
5
7
  ## Checking your agent-director install
@@ -10,10 +12,10 @@ Before starting the server, you can confirm `agent-director` is installed system
10
12
  bun run install-check
11
13
  ```
12
14
 
13
- The script finds the system-installed agent-director binary and reads its version, then judges it in two steps: first against the agent-director client's own minimum (`min_binary_version` in the installed client's `dist/version-floor.json`), then against CSCB's Phase 1 floor. `/publish`'s preflight check makes the same decision. There are three outcomes:
15
+ The script finds the system-installed agent-director binary and reads its version, then judges it in two steps: first against the agent-director client's own minimum (`min_binary_version` in the installed client's `dist/version-floor.json`), then against CSCB's Phase 1 floor. `/publish`'s preflight check makes the same decision. The released client's minimum is 0.12.0. There are three outcomes:
14
16
 
15
17
  - **Pass.** The binary meets both. The script exits 0 and prints one block naming `agent-director`, the resolved binary path, the version found and, on the line labelled `floor:`, the agent-director client's minimum (not CSCB's Phase 1 floor).
16
- - **Pass with a note.** The binary meets the client's minimum but is below CSCB's Phase 1 floor (the development placeholder `0.0.0-dev` included). The script still exits 0 and prints the same block, followed by a note: the server will not start on this binary until agent-director Phase 1 is installed; see the docs/migration.md section "Switching over to agent-director Phase 1".
18
+ - **Pass with a note.** The released client admits the development placeholder `0.0.0-dev` above any minimum, but CSCB's numeric Phase 1 floor refuses it. Ordinary older releases and 0.12.0 release candidates fail the client's minimum first. The script still exits 0 and prints the same block, followed by a note: the server will not start on this binary until agent-director Phase 1 is installed; see the docs/migration.md section "Switching over to agent-director Phase 1".
17
19
  - **Fail.** The script writes one of the class labels below, with its message, to stderr and exits non-zero:
18
20
  - `ad-system-install-not-found` — no agent-director on PATH or at the standard install path. The message points to the block ["The publishing host"](migration.md#the-publishing-host) in the docs/migration.md section "Switching over to agent-director Phase 1", which covers a host with no agent-director. The stderr also points at the install-cscb skill.
19
21
  - `ad-system-install-too-old` — the binary is below the agent-director client's own minimum. The message names the version found, the version the client requires, the binary path and the docs/migration.md section "Switching over to agent-director Phase 1", which is how agent-director is installed for this CSCB release. The stderr points at the skill.
@@ -64,7 +66,7 @@ The skill first states that this CSCB release and agent-director Phase 1
64
66
  are installed, and rolled back, together, by the docs/migration.md sections
65
67
  "Switching over to agent-director Phase 1" and "Rolling back the
66
68
  switch-over", which it points to by title. It names the agent-director
67
- client CSCB pins, `agent-director@0.11.0`, which comes installed with
69
+ client CSCB pins, `agent-director@0.12.0`, which comes installed with
68
70
  CSCB; the skill runs no install or upgrade of the client or the binary.
69
71
 
70
72
  The skill calls `bun run install-check` on each iteration. On a pass that
@@ -95,7 +97,7 @@ who haven't yet installed CSCB at all.
95
97
 
96
98
  ## agent-director's timing settings
97
99
 
98
- agent-director keeps its timing settings in the `[tmux]` table of `~/.agent-director/config.toml`. They are agent-director's settings, not CSCB configuration: CSCB only reads them, from the file under the HOME the server runs in.
100
+ agent-director keeps its timing settings in the `[tmux]` table of `~/.config/agent-director/config.toml`. They are agent-director's settings, not CSCB configuration: CSCB only reads them, from the file under the HOME the server runs in. Set HOME to a nonempty absolute path: CSCB refuses to read settings through an unset, empty or relative HOME, and the agent-director installer and uninstaller exit 2 for those values.
99
101
 
100
102
  The server reads the table when it starts and every 120 s after that, whatever `health_check_interval` is (`0` included), so a change on the host is used within about 120 s (each read follows the binary re-check, which runs 120 s after the previous one ends and can take up to its 30 s time limit). It applies agent-director's own rule: a missing file, a missing key or `0` means agent-director's default. At the first read it accepts, and at each later accepted read that changes any of them, the server writes one line to `server.log` with the nine `[tmux]` values in effect; a read that changes only `[pause] timeout_seconds` (see [Sizing the agent-director call timeout](#sizing-the-agent-director-call-timeout)) writes none.
101
103
 
@@ -169,3 +171,9 @@ Either way the server still starts, and no value changes. The check runs only at
169
171
  To fix it, raise `agent_director_call_timeout_ms` in `config.json` above the need and confirm the change (see [Reload](reload.md#reload)). `stop --stop-bots` and `clean_restart` use the new value at once; the server uses it from its next start.
170
172
 
171
173
  For the second line, give `[pause] timeout_seconds` a positive whole number, or remove it for the 30 s default; the next start checks the setting against the need that gives.
174
+
175
+
176
+ AD 0.12 keeps archived sessions in `prior_sessions` across same-agent reuse. A fresh spawn uses a new current session; archived sessions are available for inspection and are never resumed as a fallback. A launch with no recorded tmux server or pane can leave an abandoned session: AD reports it unavailable until the starting-session bound, then refuses it as an own-id conflict. CSCB latches that conflict for human review and retries only through the existing own-id recheck policy.
177
+
178
+
179
+ An unmessaged conversation created by same-agent reuse can report `transcript_status: rotated`: its current transcript has not been written, but earlier sessions remain inspectable. A resume with missing current-session paths then returns `ErrJsonlMissing`. `ErrJsonlNeverWritten` requires both an empty current recorded path and empty visible prior history. CSCB still diagnoses missing transcripts before reusing the row, reports uncertainty when evidence is insufficient, and never resumes an archived session as a fallback.
package/docs/bots.md CHANGED
@@ -90,8 +90,9 @@ When Claude Code requires tool approval, the permission relay surfaces an intera
90
90
 
91
91
  1. When a bot's Claude Code asks for a tool permission, agent-director holds the request, and CSCB picks it up within `agent_director_poll_interval_ms` (default 1000 ms).
92
92
  2. CSCB posts the prompt as the persona that owns the bot, to its destination: its destination channel (its `permission_prompts` in declarative mode, its `invited.permission_prompts` in fungible mode), or, for a `"dm"` destination, a DM from the persona's app to its `dm.contact`. The server opens that DM if none exists yet, so a DM-only persona's first prompt is delivered too. Each persona follows its own setting.
93
- 3. The operator clicks Allow / Deny in Slack, in the channel or the DM; the buttons work the same in both. CSCB passes the decision to that bot through the same persona and, as that persona, updates the message to "*Permission* — Allowed" or "*Permission* — Denied by operator".
94
- 4. If a prompt closes for any reason other than a Slack click, the next check replaces the buttons with the verdict: "⏱ *Permission* — Timed out", "🪦 *Permission* — Session ended", "*Permission* — Allowed", "*Permission* — Denied by operator", or "*Permission* — Denied (closed)" when the reason is unknown.
93
+ 3. The operator clicks Allow / Deny in Slack, in the channel or the DM; the buttons work the same in both. CSCB passes the decision through agent-director. It shows Allowed or Denied only after delivery is confirmed. When delivery is not yet confirmed, the message stays pending while CSCB polls for delivery or closure.
94
+ 4. A request that falls back to the tmux pane gets a notice without Slack buttons. Read the pane and follow the [permission recovery entry](../skills/debug-slack-channel-bots/SKILL.md#agent-director-permission-recovery); CSCB does not answer it automatically.
95
+ 5. If a prompt closes for any reason other than a Slack click, the next check replaces the buttons with the verdict: "⏱ *Permission* — Timed out", "🪦 *Permission* — Session ended", "*Permission* — Allowed", "*Permission* — Denied by operator", or "*Permission* — Denied (closed)" when the reason is unknown.
95
96
 
96
97
  The prompt names the tool and shows its input: Bash's command, Edit's or Write's file path, or for any other tool the input as JSON, up to 500 characters. An input too long for Slack's 3000-character limit on a message section is cut to fit and followed by `… (truncated, <shown> of <total> chars shown)`; the rest is not shown in Slack.
97
98
 
package/docs/cli.md CHANGED
@@ -14,7 +14,7 @@ missing prerequisite: config.json not found at <path>, and no config.json.last-a
14
14
 
15
15
  `start` reads no Slack token. The server checks the configuration, and each persona's credentials file, once it runs.
16
16
 
17
- The server first runs the agent-director startup gate: it imports `agent-director`, constructs the singleton Client (which probes the system binary's version and refuses a binary below the client's own minimum), reads the binary's version from the built client and refuses a binary below CSCB's Phase 1 floor, and verifies `~/.agent-director/state.db` is owned by the current user. Failures land in `startup-errors.log` (see [Startup errors](troubleshooting.md#startup-errors)). agent-director enforces tmux availability at spawn time. The server then loads the configuration and starts listening. With a last-applied record it runs the record; without one it checks `config.json`, records it and applies it (see [Reload](reload.md#reload)). Only then does it write its PID to `STATE_DIR/server.pid`. Conversation context is preserved across server restarts when possible.
17
+ The server first runs the agent-director startup gate: it imports `agent-director`, constructs the singleton Client (which probes the system binary's version and refuses a binary below the client's own minimum), reads the binary's version from the built client and refuses a binary below CSCB's Phase 1 floor, and verifies `~/.config/agent-director/state.db` is owned by the current user. Failures land in `startup-errors.log` (see [Startup errors](troubleshooting.md#startup-errors)). agent-director enforces tmux availability at spawn time. The server then loads the configuration and starts listening. With a last-applied record it runs the record; without one it checks `config.json`, records it and applies it (see [Reload](reload.md#reload)). Only then does it write its PID to `STATE_DIR/server.pid`. Conversation context is preserved across server restarts when possible.
18
18
 
19
19
  `start` waits up to 30 seconds for that PID file. The outcomes are:
20
20
 
@@ -60,7 +60,7 @@ Plain `stop` leaves the managed bots running — they are meant to survive a ser
60
60
  claude-slack-channel-bots stop --stop-bots
61
61
  ```
62
62
 
63
- This follows `clean_restart`'s order. It runs the [precheck](#precheck-before-stopping-bots) first, and stops nothing if it fails. When it passes, the server is stopped first, so it can't relaunch a just-exited bot mid-teardown and lose that bot's `ended` row and history, then the bot teardown runs for each persona in the last-applied record (or in `config.json` when there is no record), addressing its instance as `cscb_<key>`: pause the bot, poll until it exits (or up to `exit_timeout` seconds), then force-kill on timeout. Teardown kills but never deletes each row, preserving its `claude_session_id` so the bots can resume their conversation history on the next start. Use it when you want a clean, flushed shutdown of the bots (for example before a host reboot).
63
+ This follows `clean_restart`'s order. It runs the [precheck](#precheck-before-stopping-bots) first, and stops nothing if it fails. When it passes, the server is stopped first, so it can't relaunch a just-exited bot mid-teardown and lose that bot's `ended` row and history, then the bot teardown runs for each persona in the last-applied record (or in `config.json` when there is no record), addressing its instance as `cscb_<key>`: pause the bot, poll until it exits (or up to `exit_timeout` seconds), then force-kill on timeout. Teardown kills but never deletes each row, preserving its `session_id` so the bots can resume their conversation history on the next start. Use it when you want a clean, flushed shutdown of the bots (for example before a host reboot).
64
64
 
65
65
  If a persona can't be stopped after a passed precheck, the command prints a line naming it, its session and why, ends with a line counting the personas it could not stop, and exits 1 rather than silently reporting a clean stop (see [What the command prints when a bot can't be stopped](#what-the-command-prints-when-a-bot-cant-be-stopped)). `stop --stop-bots` never starts the server: after a failure the server stays stopped, unlike [`clean_restart`](#when-clean_restart-cant-stop-a-bot). When every persona is stopped, one stopped with a *Process outlived kill* text included, it exits with the server stop's status.
66
66
 
@@ -76,7 +76,7 @@ Runs the [precheck](#precheck-before-stopping-bots), then stops the server, grac
76
76
  claude-slack-channel-bots clean_restart
77
77
  ```
78
78
 
79
- For each persona in the last-applied record (or in `config.json` when there is no record), it pauses the persona's instance, `cscb_<key>`, through agent-director and waits until the instance has ended or is missing, or its row is gone. A bot that has not exited within `exit_timeout` seconds (default 120s) is force-killed. Teardown kills but never deletes each row, preserving its `claude_session_id` so bots resume their conversation history on the next start. All personas are processed in parallel. After the server restarts, it decides for each persona whether to resume its conversation or start a fresh one.
79
+ For each persona in the last-applied record (or in `config.json` when there is no record), it pauses the persona's instance, `cscb_<key>`, through agent-director and waits until the instance has ended or is missing, or its row is gone. A bot that has not exited within `exit_timeout` seconds (default 120s) is force-killed. Teardown kills but never deletes each row, preserving its `session_id` so bots resume their conversation history on the next start. All personas are processed in parallel. After the server restarts, it decides for each persona whether to resume its conversation or start a fresh one.
80
80
 
81
81
  `clean_restart` logs its progress to `STATE_DIR/clean_restart.log`. The lines that end it with an error (config load failure, agent-director initialization failure, a failed precheck, a persona it could not stop, the alert that the server was not started, start failure) are also printed to the terminal, and so is any *Process outlived kill* text from the teardown, even on a run that goes on to start the server.
82
82
 
@@ -129,7 +129,7 @@ Either way the last line follows, and the command exits 1. Rows are never delete
129
129
 
130
130
  Before `stop --stop-bots` or `clean_restart` stops anything, it checks that agent-director answers and that each persona's instance can be read:
131
131
 
132
- 1. It connects to agent-director. `clean_restart` makes the same version checks as the server's start. `stop --stop-bots` makes no version check of its own: any agent-director the client accepts passes, one older than CSCB's Phase 1 floor included.
132
+ 1. It connects to agent-director. `clean_restart` makes the same version checks as the server's start. `stop --stop-bots` makes no version check of its own: any agent-director the client accepts passes, including the unsupported development placeholder `0.0.0-dev`. Ordinary 0.10.0/0.11.0 releases and 0.12.0 release candidates are refused by the client before teardown.
133
133
  2. For each persona in the last-applied record (or in `config.json` when there is no record), it reads the persona's instance, `cscb_<key>`. A persona with no instance, or whose instance has finished, is skipped. A running instance has one line of its screen read.
134
134
 
135
135
  A failed precheck prints one line per persona it could not check, naming its class (such as `CONFLICT`, `UNAVAILABLE` or `CONFIG`), then a closing line, and exits 1:
@@ -199,7 +199,7 @@ Use `--console` instead of `--claudeai` for a Console account. When neither the
199
199
 
200
200
  - A directory that doesn't exist yet is fine when its parent exists.
201
201
  - A persona whose `claude_config_dir` can't be resolved to a real path (for example, a symlink on its path that points to nothing) is held: it closes or never opens its Slack connection and receives nothing, and `server.log` shows `persona-config-dir-unresolvable`. It reconnects once the directory resolves, with no restart.
202
- - A confirmed change to a persona's effective `claude_config_dir` makes it start a new conversation on the same instance at its next launch. Nothing is deleted: the old conversation stays in the old directory, kept as an earlier life of that instance.
202
+ - A confirmed change to a persona's effective `claude_config_dir` makes it start a new conversation on the same instance at its next launch. Nothing is deleted: the old conversation stays in the old directory, with prior sessions retained in the same AD life.
203
203
  - The Slack Reply Guard never installs its hook in `~/.claude`, so a persona whose directory resolves there gets no reminder (see the debugging skill's [How the guard is installed and what it reads](../skills/debug-slack-channel-bots/SKILL.md#how-the-guard-is-installed-and-what-it-reads)).
204
204
 
205
205
  **`stop_hook_bootstrap`** turns the [Slack Reply Guard](bots.md#slack-reply-guard-stop-hook) reminder on (`true`, the default) or off for the persona. It applies to that persona alone, even when it shares its `claude_config_dir` with other personas. A running bot keeps the value it was launched with until it is relaunched. See [Personas that answer without `reply`](bots.md#personas-that-answer-without-reply--opt-them-out) for when to turn it off.
package/docs/migration.md CHANGED
@@ -4,12 +4,12 @@ How to install this release over an earlier one, and how to go back. What change
4
4
 
5
5
  ## Migration
6
6
 
7
- This release and agent-director Phase 1 are installed together, and rolled back together, by the two runbooks below. Beside them:
7
+ This release requires agent-director 0.12.0 or later. Its 0.12.0 client and the host CLI are upgraded together, and rolled back together, by the two runbooks below. Beside them:
8
8
 
9
- 1. **Install the new CSCB together with agent-director Phase 1**: installing the npm package is not the whole install. This release needs agent-director Phase 1 on the host, installed with it by the runbook in [Switching over to agent-director Phase 1](#switching-over-to-agent-director-phase-1), which also says when to install the package. The npm `agent-director` package CSCB pulls in is only the client; the binary is installed system-wide (see [Prerequisites](../README.md#prerequisites)).
9
+ 1. **Install the new CSCB together with agent-director 0.12.0**: installing the npm package is not the whole install. This release needs agent-director Phase 1 on the host, installed with it by the runbook in [Switching over to agent-director Phase 1](#switching-over-to-agent-director-phase-1), which also says when to install the package. The npm `agent-director` package CSCB pulls in is only the client; the binary is installed system-wide (see [Prerequisites](../README.md#prerequisites)).
10
10
  2. **(Optional) Run a periodic agent-director `find-missing` sweep**. CSCB itself runs `find-missing` once before a resume during dead-session recovery, so a bot that died on reboot comes back with its history intact. If agent-director doesn't answer that sweep (it times out or reports tmux not answering), CSCB stops the recovery there, with no resume and no fresh session, and retries it later. CSCB's own recovery needs no periodic sweep; one reconciles the rows of agent-director's other users on a cadence. A host that follows the switch-over runbook runs one: [switch-over step 11](#step-11-schedule-the-daily-expire) adds it where the host has none. Run it as a cron entry, a systemd timer or a loop script (the switch-over runbook calls this the host's sweep schedule). As a cron entry:
11
11
  ```cron
12
- * * * * * $HOME/.agent-director/bin/agent-director find-missing
12
+ * * * * * $HOME/.config/agent-director/bin/agent-director find-missing
13
13
  ```
14
14
  The job must run as the workers' user and resolve their tmux socket, the socket switch-over step 1 pins.
15
15
  3. **Install the startup-errors logrotate snippet** so `~/.config/claude-slack-channel-bots/startup-errors.log` doesn't grow unboundedly. Edit `USER` in the file to match the OS account running CSCB:
@@ -24,42 +24,26 @@ This release and agent-director Phase 1 are installed together, and rolled back
24
24
  This release requires agent-director Phase 1, installed on the host together with it; CSCB changes no agent-director code. The two are installed together, by this runbook, and [rolled back together, by "Rolling back the switch-over"](#rolling-back-the-switch-over).
25
25
 
26
26
  - Every agent on the host, with every long-running agent-director process (`agent-director serve` included), is stopped before either binary change and started again after it.
27
- - The old CSCB never runs against Phase 1, and the new one never against 0.10.0: no bot server runs between step 3 and step 10, and no side-by-side install under another path is used.
27
+ - The previous CSCB runs only with its compatible CLI, and this release runs only with 0.12.0 or later: no bot server runs between step 3 and step 10, and no side-by-side install under another path is used.
28
28
  - The switch-over log is your own record, kept wherever you choose until rollback is no longer wanted. Each step says what to record in it.
29
29
  - Run every command as the workers' user (the user the bot server launches workers as), in the tmux environment step 1 pins.
30
30
  - Steps marked "operator action" are done by a human on the host; nothing in CSCB does them.
31
31
 
32
32
  #### Arrived here from a startup refusal?
33
33
 
34
- Your new CSCB is installed and its server refused to start with `ad-below-phase1-floor` or `ad-system-install-too-old`. The refusal launched, killed and deleted nothing, and left your bots as they were.
34
+ Your new CSCB is installed and its server refused to start with `ad-below-phase1-floor` or `ad-system-install-too-old`. Ordinary 0.10.0/0.11.0 releases and 0.12.0 release candidates produce `ad-system-install-too-old`; the unsupported development placeholder `0.0.0-dev` produces `ad-below-phase1-floor`. The refusal launched, killed and deleted nothing, and left your bots as they were.
35
35
 
36
- This block covers only a refusal before agent-director Phase 1 is installed on the host, by step 8 or otherwise. Act in this order:
36
+ Check the resolved CLI path and version as the workers' user in the server launcher's environment. CSCB finds `$HOME/.config/agent-director/bin/agent-director` before PATH. A missing CLI, a CLI older than 0.12.0 or an unmigrated old layout must be fixed with the released installer before starting CSCB.
37
37
 
38
- 1. If `config.json` is in persona form and no pre-persona copy of it exists, neither step 1's copy nor one you kept elsewhere, stop here and change nothing: no reinstall, no start and no step 1. Rebuild the pre-persona `config.json` by hand, as [step 8 of "Rolling back the switch-over"](#step-8-reinstall-the-previous-cscb) says. Once you have rebuilt it, follow this block again from its start.
39
- 2. Otherwise, first rebuild by hand to their pre-persona form, as [step 8 of "Rolling back the switch-over"](#step-8-reinstall-the-previous-cscb) says, the crontable targets and `/interject` callers that the conversion to personas left in persona form where step 1's copy of them is missing. Then reinstall the previous CSCB, the version step 1 recorded or else the version the host ran before, with the pre-persona `config.json`, crontable, `/interject` callers, `access.json` and Slack token environment variables: from step 1's files, or a pre-persona copy of `config.json` you kept, where they exist, and otherwise from those still in place. Start it, re-enabling the host's autostart for CSCB if it was disabled, as step 8's "no go" branch does. Put the files back by the old state directory's name, `~/.claude/channels/slack`: a start of the new CSCB has already moved that directory to `~/.config/claude-slack-channel-bots` and left a symbolic link at the old name, which the previous CSCB reads through.
40
- 3. Then, if agent-director is not 0.10.0 (below 0.7.0 under `ad-system-install-too-old`; 0.7.0 to 0.9.x under `ad-below-phase1-floor`), bring it to 0.10.0 outside this runbook. This block gives no command for it.
41
- 4. Then start the runbook at [step 1](#step-1-check-the-host-and-stage-the-release). Step 3 is then the old CSCB's own stop.
38
+ If the previous AD is still installed, first reinstall the previous CSCB without starting the new release, and restore its saved configuration, crontable and `/interject` callers as described in [rollback step 8](#step-8-reinstall-the-previous-cscb). Its client must be compatible with that CLI so step 3's `stop --stop-bots` can stop the workers. If the saved files are missing, rebuild them by hand before continuing; then start the previous CSCB on its compatible CLI and begin step 1.
42
39
 
43
- **A refusal after Phase 1 was installed.** Installing agent-director Phase 1 migrates agent-director's store. A refusal at step 10, or at any later start of the new CSCB (an autostart, `clean_restart` or the restart in step 4 of "Rolling back the switch-over" included), on a host whose Phase 1 install was step 8's or agent-director's own install on a publishing host, means the server finds the wrong agent-director binary. For that refusal, and only for it:
40
+ If AD has already been upgraded, verify that the launcher resolves the upgraded CLI and its `config_dir`; fix any stale path before restarting, or restore both releases with the rollback runbook. No agent or caller may run across the AD upgrade. Do not create a new template or copy a binary over the old install.
44
41
 
45
- 1. Check the binary. The server finds `$HOME/.agent-director/bin/agent-director` first, then the first `agent-director` on `PATH`. Take the binary path the startup-errors entry names and run `<path> version`, as the workers' user in the bot server's launcher environment.
46
- 2. Then either put agent-director Phase 1 back as the binary the server finds, or [follow "Rolling back the switch-over", which starts the previous CSCB](#rolling-back-the-switch-over). To put it back, stop every agent on the host and every long-running agent-director process (`agent-director serve` included) before that binary change, and start them again after it. A human stops any CSCB bot still running through the item "Stopping a set of agents before a binary change" in the "Operator actions" section of agent-director's README. Once the server finds Phase 1, start the new CSCB as [step 10](#step-10-start-the-new-cscb) does.
47
-
48
- Never reinstall the old CSCB onto the migrated store. At the restart in step 4 of "Rolling back the switch-over", an agent that could not be stopped still runs: put nothing back and don't follow the rollback again. The new CSCB stays stopped until agent-director has dealt with that agent (as [that step](#step-4-wait-for-the-new-rows-to-end) says), and then this branch applies.
49
-
50
- **Phase 1 installed some other way.** A host where agent-director Phase 1 was installed in any other way outside this runbook is not a target of this runbook, the same way step 1 sends a host on any version but 0.10.0 outside it. [Follow "Rolling back the switch-over"](#rolling-back-the-switch-over) and the item "agent-director was installed outside the caller's switch-over" in the "Operator actions" section of agent-director's README. Don't put Phase 1 back and start the new CSCB.
51
-
52
- **A stop by the runtime re-check** is not a switch-over case. See [Found while the server was running](../skills/debug-slack-channel-bots/SKILL.md#found-while-the-server-was-running) in the debugging skill.
42
+ A stop by the runtime re-check is not a switch-over case. Follow the debug skill's [Found while the server was running](../skills/debug-slack-channel-bots/SKILL.md#found-while-the-server-was-running) remedy.
53
43
 
54
44
  #### The publishing host
55
45
 
56
- `/publish` checks that the publishing host has an agent-director binary the client accepts: 0.7.0, the client's minimum, or later. This runbook starts by staging a release that is already published, so the host that publishes it is covered here:
57
-
58
- 1. Publish from a host that already passes that check. This is the first choice.
59
- 2. A host with no agent-director installs agent-director's Phase 1 release by agent-director's own install, then publishes. It has no agents and nothing to back up.
60
- 3. A host below the client's minimum (0.7.0) publishes from another host, and is not a target of this runbook: it brings agent-director to 0.10.0 first, outside this runbook, since step 1 stops on any version but 0.10.0. This block gives no command for it.
61
-
62
- A publishing host needs no operator go-ahead.
46
+ `/publish` requires a system-installed CLI admitted by the pinned client: 0.12.0 or later. Use a host that already passes `bun run install-check`. A host without AD installs the released CLI using its installer; a host with an older install follows the coordinated upgrade below before running the new client.
63
47
 
64
48
  #### Step 1: Check the host and stage the release
65
49
 
@@ -68,11 +52,11 @@ Do this beforehand, with the old CSCB running and still installed as the global
68
52
  1. **The go-ahead (operator action).** Confirm the go-ahead for the switch-over on this host, and record it in the switch-over log. The go-ahead is the approval for the switch-over. If there is no go-ahead, stop here, before anything goes down.
69
53
 
70
54
  The go-ahead is the operator's decision, and agent-director ships no install-gate file or record. agent-director's Phase 1 install gate is the set of CSCB fixes that must be in place on a host before Phase 1 is installed there. This release, installed together with agent-director Phase 1 as this runbook does, meets it.
71
- 2. **agent-director's version.** Run `agent-director version` in the bot server's launcher environment, as the workers' user, the same as the Claude Code check below, and confirm that it shows 0.10.0, the only supported starting point. If it does not, stop here, before anything goes down. A host on an earlier version (below 0.7.0, or 0.7.0 to 0.9.x) first brings agent-director to 0.10.0, outside this runbook; this step names no command for it.
55
+ 2. **agent-director's version.** Run `agent-director version` in the bot server's launcher environment as the workers' user. Record its version and resolved path. Stage the released 0.12.0 installer and both binaries, and keep the prior install and flags for restoration. A host already on 0.12.0 or later needs no directory migration; verify its `config_dir` and client compatibility before restarting callers.
72
56
  3. **The tmux socket (operator action).** Pin the tmux socket for the bot server's launcher, the host's sweep schedule and the workers. The sweep schedule is whatever runs `agent-director find-missing` on a schedule on your host: a cron entry, a systemd timer or a loop script. Run `tmux display-message -p '#{socket_path}'` from the bot server's launcher, the sweep schedule's environment and a worker's. Confirm that all three print the same path, that it is the pinned path (a `TMUX_TMPDIR` that names a missing path falls back silently to `/tmp`), and that the three share one HOME. A host with no sweep schedule checks the other two, and runs the one step 11 adds in the same environment. Record the result in the switch-over log.
73
57
  4. **tmux.** Confirm tmux 3.2 or later, with `remain-on-exit` off.
74
- 5. **Claude Code.** Run `claude --version` in the bot server's launcher environment (the same user and `PATH` the server launches workers with), and confirm that the workers' Claude Code is 2.1.280 or later: the minimum agent-director states for its exec-form hooks, and the version the fleet runs. On a Claude Code too old for exec-form hooks (older than 2.1.139), each hook prints nothing, agent-director records `ad.hook.ignored` with the reason `no_exec_form`, and every launch stays `pending`. If it is older than 2.1.280, stop here, before anything goes down. In the same environment, run `command -v claude` and confirm that it resolves to Claude Code's native binary or to a wrapper that `exec`s it. A launcher or shim that runs Claude Code as a child process makes agent-director ignore every hook, recording `ad.hook.ignored` with the reason `pid_mismatch`, and every launch stays `pending`; replace such a launcher now.
75
- 6. **agent-director's timing settings.** Read all nine keys of the `[tmux]` table of `~/.agent-director/config.toml` (see [agent-director's timing settings](agent-director.md#agent-directors-timing-settings); a missing file, a missing key or `0` means the default) and its `[pause] timeout_seconds` (30 s when the file or the key is missing). Record the effective values in the switch-over log. Confirm that the three windows, `pending_grace_seconds`, `stopping_window_seconds` and `starting_session_seconds`, are the values you intend and each is at or above its minimum: `starting_session_seconds` 60, `stopping_window_seconds` 30, and `pending_grace_seconds` the larger of 30 and ⌈(`create_timeout_ms` + `pipe_close_wait_ms`) / 1000⌉ + 20. A `pending_grace_seconds` above 540 s shortens agent-director's SessionStart wait to 540 s. In the same file, read `[defaults] expire_retention_days`: Phase 1 accepts 1 to 106751, and `0` or a missing key gives 31 days. A negative value or one above 106751 makes Phase 1 refuse its whole configuration with `ErrConfigMalformed`, from the install on: every store-backed verb, `serve` and the hooks. Correct such a value now, and record the effective value in the switch-over log.
58
+ 5. **Claude Code.** Run `claude --version` in the bot server's launcher environment (the same user and `PATH` the server launches workers with), and confirm that the workers' Claude Code is 2.1.296 or later: the minimum agent-director states for its exec-form hooks, and the version the fleet runs. On a Claude Code too old for exec-form hooks (older than 2.1.139), each hook prints nothing, agent-director records `ad.hook.ignored` with the reason `no_exec_form`, and every launch stays `pending`. If it is older than 2.1.296, stop here, before anything goes down. In the same environment, run `command -v claude` and confirm that it resolves to Claude Code's native binary or to a wrapper that `exec`s it. A launcher or shim that runs Claude Code as a child process makes agent-director ignore every hook, recording `ad.hook.ignored` with the reason `pid_mismatch`, and every launch stays `pending`; replace such a launcher now.
59
+ 6. **agent-director's timing settings.** Before the AD directory move, read all nine keys of the `[tmux]` table of the existing installation’s `~/.agent-director/config.toml` (an explicit pre-upgrade path exception). If AD is already upgraded, use `~/.config/agent-director/config.toml` instead. A missing file, a missing key or `0` means the default (see [agent-director's timing settings](agent-director.md#agent-directors-timing-settings)). Also read that file’s `[pause] timeout_seconds` (30 s when the file or the key is missing). Record the effective values in the switch-over log. Confirm that the three windows, `pending_grace_seconds`, `stopping_window_seconds` and `starting_session_seconds`, are the values you intend and each is at or above its minimum: `starting_session_seconds` 60, `stopping_window_seconds` 30, and `pending_grace_seconds` the larger of 30 and ⌈(`create_timeout_ms` + `pipe_close_wait_ms`) / 1000⌉ + 20. A `pending_grace_seconds` above 540 s shortens agent-director's SessionStart wait to 540 s. In the same file, read `[defaults] expire_retention_days`: Phase 1 accepts 1 to 106751, and `0` or a missing key gives 31 days. A negative value or one above 106751 makes Phase 1 refuse its whole configuration with `ErrConfigMalformed`, from the install on: every store-backed verb, `serve` and the hooks. Correct such a value now, and record the effective value in the switch-over log.
76
60
  7. **The call timeout.** Always computing from this host's values, confirm that the `agent_director_call_timeout_ms` the persona configuration will carry (staged below; `60000` when it leaves the setting out) exceeds the need: the largest ceiling among the verbs CSCB calls, plus the 15 s margin (15000 ms). Record the computed need in the switch-over log. With Q, A, C, W and E the host's `query_timeout_ms`, `action_timeout_ms`, `create_timeout_ms`, `pipe_close_wait_ms` and `kill_exit_wait_ms`, and B its `sweep_budget_seconds` in ms, the ceilings, in ms, are:
77
61
  - `kill`: the larger of 2Q + 2A + E + 4W and 3Q + 2A + 5W;
78
62
  - `read-pane`: 3Q + A + 4W;
@@ -124,12 +108,12 @@ As the workers' user, on the socket pinned in step 1, run a read-only `tmux ls`.
124
108
 
125
109
  #### Step 6: End the leftovers that have a row
126
110
 
127
- Only if step 5 found a leftover with a row. End each such leftover now, with the still-installed 0.10.0 binary. After the Phase 1 install, a row from before it has no launch token or recorded socket, so no session is ever its current launch's: Phase 1's `kill` fails closed on it, and not even agent-director's option for a finished row ends its session.
111
+ Only if step 5 found a leftover with a row. End each such leftover now, with the still-installed previous binary. After the Phase 1 install, a row from before it has no launch token or recorded socket, so no session is ever its current launch's: Phase 1's `kill` fails closed on it, and not even agent-director's option for a finished row ends its session.
128
112
 
129
113
  For each one:
130
114
 
131
115
  1. Note the session's window ids, as in step 5.
132
- 2. Run `agent-director kill --claude-instance-id <id>` and check its result; on an error, don't delete or respawn. Then make the gone check.
116
+ 2. With a pre-0.12 CLI, take `<id>` from the old `claude_instance_id` field returned by `agent-director list` (any extraction pipeline must read `.claude_instance_id`, not `.instance_id`), then run `agent-director kill --claude-instance-id <id>` and check its result; on an error, don't delete or respawn. These old names are an explicit migration exception while that CLI remains installed. If AD is already on 0.12.0 or later, use the returned `instance_id` and run `agent-director kill --instance-id <id>` and check its result; on an error, don't delete or respawn. Then make the gone check.
133
117
  3. If the session or one of its windows remains, end the session with the exact-name `tmux kill-session -t =<name>`, and make the gone check again.
134
118
  4. Run `agent-director find-missing` until the row reads `ended` or `missing`, for at most 5 minutes.
135
119
 
@@ -144,7 +128,7 @@ A leftover that cannot be ended this way means no Phase 1 install: take step 8's
144
128
  2. Put the persona configuration in place as `config.json`, in the old state directory, `~/.claude/channels/slack`, which the new CLI keeps using until the new CSCB's first start.
145
129
  3. Write each persona's credentials file with the new CLI (see [`claude-slack-channel-bots credentials`](cli.md#claude-slack-channel-bots-credentials)), into the same directory: each `credentials_file` in the staged configuration names a path there.
146
130
  4. Rewrite crontable targets and `/interject` callers to name personas, in the crontable in the same directory.
147
- 5. Run the new install check (see [Checking your agent-director install](agent-director.md#checking-your-agent-director-install)). It passes on the still-installed 0.10.0, with its note.
131
+ 5. Run the new install check (see [Checking your agent-director install](agent-director.md#checking-your-agent-director-install)). An older CLI is refused by the 0.12.0 client; do not start CSCB until step 8 installs the required CLI.
148
132
 
149
133
  Steps 4, 7 and 8 of [Upgrading to personas](#upgrading-to-personas) belong here. From this step the new CSCB is deployed but not started.
150
134
 
@@ -154,16 +138,12 @@ Steps 4, 7 and 8 of [Upgrading to personas](#upgrading-to-personas) belong here.
154
138
 
155
139
  1. **The go-ahead (operator action).** Confirm that the go-ahead for this host still stands, and record it in the switch-over log.
156
140
 
157
- **No go.** If there is no go-ahead, there is no Phase 1 install. Reinstall the previous CSCB, the version step 1 recorded, with the `config.json`, crontable, `/interject` callers, `access.json` and Slack token environment variables saved in step 1. Start it on 0.10.0 and re-enable its autostart (operator action). Every other agent this step already stopped is started again on 0.10.0 by its owner (operator action). The runbook stops there.
158
- 2. **Stop every other agent (operator action).** Otherwise, before the install, stop every other agent on the host: orchestrators' workers, hand-started sessions and sessions with an `agent-director serve`. Confirm with `agent-director list` that each stopped agent's row reads `ended` or `missing` (a row that 0.10.0 left stuck live for a dead agent is expected), and with a read-only `tmux ls` that no agent session is left. Record it in the switch-over log. An agent that cannot be stopped means "no go".
159
- 3. **Back up the store.** Back up `~/.agent-director/state.db` with an online-consistent copy, sqlite3's `.backup`, not a plain file copy, because the store runs in WAL mode:
160
- ```sh
161
- sqlite3 ~/.agent-director/state.db ".backup '<backup file>'"
162
- ```
163
- The backup is for disaster recovery only. It is not the rollback path.
164
- 4. **Install agent-director Phase 1.** Its schema migration adds thirteen columns and the one-row `store_meta` table, which holds the store's id. Confirm that `agent-director version` shows the Phase 1 version.
165
- 5. **Write the `[tmux]` values.** Right after the install, before the restarts that follow and so before the new CSCB starts, write any non-default `[tmux]` values you want into `~/.agent-director/config.toml`. A long-running agent-director process reads the file only when it starts.
166
- 6. **Restart `serve` (operator action).** Restart every `agent-director serve` process and every other long-running agent-director process still running. Compare process start times to confirm that none is older than the install. Record the version and that result in the switch-over log.
141
+ **No go.** If there is no go-ahead, there is no Phase 1 install. Reinstall the previous CSCB, the version step 1 recorded, with the `config.json`, crontable, `/interject` callers, `access.json` and Slack token environment variables saved in step 1. Start it on the previous compatible CLI and re-enable its autostart (operator action). Every other agent this step already stopped is started again on the previous compatible CLI by its owner (operator action). The runbook stops there.
142
+ 2. **Stop every other agent (operator action).** Otherwise, before the install, disable everything that starts agents and stop every other agent on the host: orchestrators' workers, hand-started sessions and sessions with an `agent-director serve`. Stop every long-running AD process and every caller, including TypeScript clients, before the installer runs. Use the still-installed CLI's `kill` and `find-missing` to reconcile stopped agents, using step 6's version-specific identifiers and flags. Confirm with `agent-director list` that all rows are non-live (`ended` or `missing`), with a read-only `tmux ls` that no agent session is left, and with process checks that all workers, callers, serves and launchers are stopped. Record it in the switch-over log. An unresolved live row or a process that cannot be stopped means "no go": do not run the installer, delete rows or edit SQL to fake a terminal state. No agent may run across the upgrade; the installer enforces this with `ErrAgentsLive`.
143
+ 3. **Back up the store and locate the original install.** Run from a shell agent-director did not start, in the directory the original installer ran in (or the same repository). Before the move, the default store is `~/.agent-director/state.db`. For an external `[store] db_path`, save `state.db`, `state.db-wal` and `state.db-shm` while all callers are stopped; the installer migrates an external store in place and rollback needs that copy. Keep the original installer flags and registrations in the switch-over log.
144
+ 4. **Install released agent-director 0.12.0 with its own installer.** Follow [agent-director's installer instructions](https://github.com/gabemahoney/agent-director/blob/v0.12.0/skills/install-agent-director/SKILL.md). Do not copy binaries by hand or create templates in the new directory first. The installer moves `~/.agent-director` to `~/.config/agent-director`, keeps the original as `~/.agent-director.pre-move-<timestamp>`, migrates the store to schema 9, and migrates config and templates. Preserve that kept directory until rollback is no longer wanted. Fix any manual PATH, hook or MCP entries the installer reports, including entries under other Claude config directories; confirm `agent-director version` reports 0.12.0 or later and the new `config_dir`.
145
+ 5. **Write the `[tmux]` values.** Right after the install, before the restarts that follow and so before the new CSCB starts, write any non-default `[tmux]` values you want into `~/.config/agent-director/config.toml`. A long-running agent-director process reads the file only when it starts.
146
+ 6. **Restart callers (operator action).** Restart every `agent-director serve` process and every caller, including TypeScript clients. Compare process start times to confirm that none is older than the install. Record the version and that result in the switch-over log.
167
147
  7. **Check the settings again.** Read the nine timing settings and `[pause] timeout_seconds` again as in step 1, and record the effective values. Confirm them and the call timeout as in step 1; if the need has grown, raise `agent_director_call_timeout_ms` in `config.json` before step 10, in the old state directory's copy (step 7's warning applies). Confirm that `agent-director list` answers without `ErrConfigMalformed`.
168
148
 
169
149
  #### Step 9: Start the other agents again
@@ -190,9 +170,10 @@ The shared orchestrator prompt's "hold until after" wording goes out now (operat
190
170
 
191
171
  ### Rolling back the switch-over
192
172
 
193
- This runbook takes the host back to the previous CSCB on agent-director 0.10.0, whether agent-director Phase 1 was installed by [Switching over to agent-director Phase 1](#switching-over-to-agent-director-phase-1) or some other way. Both binaries are rolled back together.
173
+ This runbook takes the host back to the previous CSCB on its previous compatible agent-director, whether agent-director Phase 1 was installed by [Switching over to agent-director Phase 1](#switching-over-to-agent-director-phase-1) or some other way. Both binaries are rolled back together.
194
174
 
195
- - The previous agent-director is restored only with agent-director's emergency downgrade recipe, in step 6. Never restore `state.db` from the switch-over's backup: the store is shared by every agent-director user on the host, and a restore would drop the rows of other services' workers spawned since the switch-over, leaving them running with no row.
175
+ - Stop all callers before restoring the previous agent-director using the released way-back procedure in step 6: restore the retained old directory and any external store's pre-upgrade copy. Schema 9 has no SQL downgrade. Data written since the upgrade is lost; restart only callers compatible with the restored CLI, including the previous CSCB.
176
+ - The store is shared by every agent-director user on the host. Without stopping all callers first, a restore would drop the rows of other services' workers spawned since the switch-over, leaving them running with no row.
196
177
  - Every agent on the host, with every long-running agent-director process (`agent-director serve` included), is stopped before the previous binary is restored and started again after it.
197
178
  - Keep using the switch-over log. Each step says what to record in it.
198
179
  - Run every command as the workers' user, in the tmux environment switch-over step 1 pinned.
@@ -231,19 +212,19 @@ agent-director list --label service=cscb
231
212
 
232
213
  A row still live after that (for example a retired key whose kill failed, which `stop --stop-bots` does not cover) is ended by a human:
233
214
 
234
- 1. Run `agent-director kill --claude-instance-id <id>`, and check its result; on an error, don't delete or respawn.
215
+ 1. Run `agent-director kill --instance-id <id>`, and check its result; on an error, don't delete or respawn.
235
216
  2. A `kill` refused with CONFLICT ("not this launch's session") has met a leftover of an earlier launch: handle it as "Operator actions" describes.
236
217
  3. If these kills keep failing, follow "Operator actions" for that worker, and record it in the switch-over log.
237
218
 
238
219
  Nothing goes on to step 6 while such a worker runs, because every agent must be stopped before the previous binary is restored.
239
220
 
240
- **When "Operator actions" cannot end it either**, the rollback stops here, with Phase 1 still installed, and the worker is taken to agent-director. So that the fleet does not stay down while agent-director investigates, start the new CSCB again, restore step 2's daily `expire` to the host's sweep schedule, and re-enable the host's autostart for CSCB (operator action). If that start is refused, the new CSCB stays stopped until agent-director has dealt with that worker; then follow "A refusal after Phase 1 was installed" in [Arrived here from a startup refusal?](#arrived-here-from-a-startup-refusal).
221
+ **When "Operator actions" cannot end it either**, the rollback stops here, with Phase 1 still installed, and the worker is taken to agent-director. So that the fleet does not stay down while agent-director investigates, start the new CSCB again, restore step 2's daily `expire` to the host's sweep schedule, and re-enable the host's autostart for CSCB (operator action). If that start is refused, the new CSCB stays stopped until agent-director has dealt with that worker; then follow the already-upgraded CLI check in [Arrived here from a startup refusal?](#arrived-here-from-a-startup-refusal).
241
222
 
242
223
  #### Step 5: Check for leftover persona sessions
243
224
 
244
225
  As the workers' user, on the socket switch-over step 1 pinned, run a read-only `tmux ls`, and confirm that no `slack_bot_<key>` session is left. Handle each leftover by whether it has a row and, if it has one, by that row's current state:
245
226
 
246
- - **A leftover whose row is live:** end it with `agent-director kill --claude-instance-id cscb_<key>`, and check its result; on an error, don't delete or respawn. When that `kill` is refused with "not this launch's session", handle it as "Operator actions" describes.
227
+ - **A leftover whose row is live:** end it with `agent-director kill --instance-id cscb_<key>`, and check its result; on an error, don't delete or respawn. When that `kill` is refused with "not this launch's session", handle it as "Operator actions" describes.
247
228
  - **A finished row's own leftover session:** handle it as "Operator actions" describes.
248
229
  - **An operator action refused inside the stopping window or the starting-session bound:** retry it once the longer of the two has passed. Read all nine timing settings again, as in switch-over step 1, for the two windows' values, and record them in the switch-over log. A plain `kill` never refuses for that reason.
249
230
  - **A leftover with no row:** handle it as in [switch-over step 5](#step-5-check-for-leftover-sessions), against the previous CSCB's session names (`slack_bot_<name>_<channel>` or `slack_bot_<channel ID>`) instead of the new personas'.
@@ -252,17 +233,15 @@ Check each result, then run `agent-director find-missing` and switch-over step 5
252
233
 
253
234
  #### Step 6: Stop every agent and restore the previous agent-director
254
235
 
255
- 1. **Stop every other agent (operator action).** Before the previous binary is restored, stop every other agent on the host and every long-running agent-director process: orchestrators' workers, hand-started sessions and sessions with an `agent-director serve`. A `serve` runs inside its Claude session, so that session is stopped too. Confirm as in [switch-over step 8](#step-8-install-agent-director-phase-1): `agent-director list` shows each stopped agent's row `ended` or `missing`, and a read-only `tmux ls` shows no agent session left. Record it in the switch-over log.
256
-
257
- The restore does not begin while any agent runs. If one cannot be stopped, the rollback stops as step 4 says, and every agent this step stopped is started again on Phase 1 by its owner (operator action).
258
- 2. **Restore the previous agent-director** with agent-director's emergency downgrade recipe, never from the switch-over's `state.db` backup. The recipe drops the thirteen columns the Phase 1 migration added and its `store_meta` table, which holds the store's id, and stamps schema version 4. It deletes no row or history entry, so the older binary shows every life's history again.
236
+ 1. **Stop every other agent (operator action).** Disable everything that starts agents. Before the previous binary is restored, stop every other agent on the host and every long-running agent-director process: orchestrators' workers, hand-started sessions and sessions with an `agent-director serve`. A `serve` runs inside its Claude session, so that session is stopped too. Stop every caller, including TypeScript clients, as well. Confirm as in [switch-over step 8](#step-8-install-agent-director-phase-1): `agent-director list` shows each stopped agent's row `ended` or `missing`, and a read-only `tmux ls` shows no agent session left. Record it in the switch-over log.
259
237
 
260
- A later Phase 1 install creates a new store id. A session labelled before the rollback would then read as another agent-director store's session, which agent-director never acts on: one more reason no agent may run across the restore.
238
+ The restore does not begin while any agent runs. Every row must be non-live and every worker, session, caller, serve and launcher stopped before restoration. An unresolved row means no go; do not delete rows or edit SQL to fake a terminal state. If one cannot be stopped, the rollback stops as step 4 says, and every agent this step stopped is started again on Phase 1 by its owner (operator action).
239
+ 2. **Restore the previous agent-director** using [the released 0.12.0 way-back procedure](https://github.com/gabemahoney/agent-director/blob/v0.12.0/docs/operator-actions.md#going-back-to-the-previous-release). Set the new directory aside and restore the kept `~/.agent-director.pre-move-*` directory to `~/.agent-director`; restore any external store from its pre-upgrade copy. Restore registrations, hooks and paths with the previous release's own installer and its original flags, following that procedure. Schema 9 has no downgrade recipe: a binary swap or SQL column removal cannot roll it back. Data written since the upgrade is lost. Restart only callers compatible with the restored CLI.
261
240
  3. **Start the other agents again (operator action).** Every other agent on the host is started again by its owner on the previous binary, which also starts its `serve` processes on it.
262
241
 
263
242
  #### Step 7: Revert the orchestrator prompt's hold wording
264
243
 
265
- Revert the shared orchestrator prompt's "hold until after" wording that switch-over step 11 put out (operator action). The worker cleanup switch-over step 10 changed to "kill, then leave the row" stays: it works on 0.10.0 too.
244
+ Revert the shared orchestrator prompt's "hold until after" wording that switch-over step 11 put out (operator action). The worker cleanup switch-over step 10 changed to "kill, then leave the row" stays: keep the cleanup compatible with the restored CLI.
266
245
 
267
246
  #### Step 8: Reinstall the previous CSCB
268
247
 
@@ -17,11 +17,11 @@ These top-level fields apply to the whole server. A confirmed change to one take
17
17
  | `mcp_config_path` | string | `~/.claude/slack-mcp.json` | Path to the MCP config file passed to Claude Code when launching managed sessions. |
18
18
  | `append_system_prompt_file` | string | — | Path to a file appended to every managed session's system prompt via `--append-system-prompt-file`. Missing file silently skipped. See `skills/EXAMPLE_CLAUDE.md` for a template. |
19
19
  | `system_prompt_mode` | string | `"append"` | Controls how `append_system_prompt_file` is applied. `"append"`: the custom prompt file is appended on top of `CLAUDE.md` (default, current behavior). `"none"`: only `CLAUDE.md` is used; `append_system_prompt_file` is ignored even if set. Use `"none"` when the project's `CLAUDE.md` already contains everything the bot needs. |
20
- | `fresh_system_prompt` | boolean | `true` | When `true` (default), every bot launches with `--system-prompt-snapshot off`, so a restarted or resumed bot loads the current system prompt file and `CLAUDE.md` and keeps its conversation, whatever `system_prompt_mode` is. When `false`, the flag is omitted. A change takes effect at the next server start. Needs a Claude Code that has `--system-prompt-snapshot` (confirmed on 2.1.280); on an older one set it to `false`. A bot keeps the launch arguments it was started with on every resume, so a change reaches a bot only when it next starts a new conversation; a `clean_restart` that resumes conversations does not apply it to those bots. |
20
+ | `fresh_system_prompt` | boolean | `true` | When `true` (default), every bot launches with `--system-prompt-snapshot off`, so a restarted or resumed bot loads the current system prompt file and `CLAUDE.md` and keeps its conversation, whatever `system_prompt_mode` is. When `false`, the flag is omitted. A change takes effect at the next server start. Needs a Claude Code that has `--system-prompt-snapshot` (confirmed on 2.1.296); on an older one set it to `false`. A bot keeps the launch arguments it was started with on every resume, so a change reaches a bot only when it next starts a new conversation; a `clean_restart` that resumes conversations does not apply it to those bots. |
21
21
  | `cozempic_prescription` | string | `"standard"` | Cozempic cleaning intensity before resume. Valid values: `gentle`, `standard`, `aggressive`. Has no effect if cozempic is not installed. |
22
22
  | `message_archive_db` | string | — | Path to a SQLite DB where every inbound Slack message is archived in real time. Parent directories are created if missing; schema is initialized on first open. Compatible with the `archive-messages.py` backfill script — both can write concurrently. Each channel's name is looked up through the receiving persona's Slack app, which needs the `channels:read` and `groups:read` bot scopes that the shipped `slack-app-manifest.yml` grants. An app created from an earlier manifest stores the channel ID instead, until both scopes are added and the app is re-installed; the `debug-slack-channel-bots` skill has the steps under "The message archive shows channel IDs". A DM's rows keep the DM's `D…` ID whatever the scopes, as a DM has no name; a group DM's rows keep its ID too. Feature is disabled when absent. |
23
23
  | `claude_config_dir` | string | — | Default Claude on-disk config directory for every persona. When a persona has one (its own or this default), its session launches with `CLAUDE_CONFIG_DIR='<resolved-path>'` so the bot authenticates against a specific account. `~` is expanded and the path is resolved to absolute. A persona's own `claude_config_dir` overrides this value. When neither is set, Claude's own default applies. Must be non-empty when set. |
24
- | `resume_enabled` | boolean | `true` | When `true` (default), a bot whose session died — including after a host reboot or pod resume — comes back with its prior conversation history intact instead of starting fresh; after 2 resumes in a row that end before the session starts (a transcript Claude Code can't load), it starts fresh instead (see "Session not restarting after crash" under [Troubleshooting](troubleshooting.md#troubleshooting)). When `false`, a bot is never resumed: on startup and on runtime auto-restart it starts a new conversation on the same instance, even when a stored session exists, and its old conversation is kept as an earlier life of that instance. Set `false` as a workaround if your Claude Code version crashes with "sandbox required but unavailable" on resume (a known regression in v2.1.120). |
24
+ | `resume_enabled` | boolean | `true` | When `true` (default), a bot whose session died — including after a host reboot or pod resume — comes back with its prior conversation history intact instead of starting fresh; after 2 resumes in a row that end before the session starts (a transcript Claude Code can't load), it starts fresh instead (see "Session not restarting after crash" under [Troubleshooting](troubleshooting.md#troubleshooting)). When `false`, a bot is never resumed: on startup and on runtime auto-restart it starts a new conversation on the same instance, even when a stored session exists, and its Claude row retains its AD life and prior sessions. Set `false` as a workaround if your Claude Code version crashes with "sandbox required but unavailable" on resume (a known regression in v2.1.120). |
25
25
  | `agent_director_poll_interval_ms` | number | `1000` | Poll interval (ms) for the agent-director permission relay tick. Must be a positive integer in `[200, 3_600_000]`. Replaces the pre-rename `claude_director_poll_interval_ms` — the old name is rejected at startup. |
26
26
  | `agent_director_call_timeout_ms` | number | `60000` | How long (ms) CSCB waits on each agent-director call it makes for its personas, in the server and in `stop --stop-bots` and `clean_restart`. Must be an integer in `[1000, 3_600_000]`; out-of-range values are rejected at load. It should be greater than the need that agent-director's timing settings give; see [Sizing the agent-director call timeout](agent-director.md#sizing-the-agent-director-call-timeout). |
27
27
  | `stop_hook_bootstrap` | boolean | `true` | Default for every persona: whether the persona gets the Slack Reply Guard reminder (see [Slack Reply Guard (Stop hook)](bots.md#slack-reply-guard-stop-hook)). The value applies per persona, from the persona's first launch after the change is applied (see [Reload](reload.md#reload)). A persona's own `stop_hook_bootstrap` overrides this value. Non-boolean values are rejected at startup. |
@@ -48,7 +48,7 @@ Turn on the persona's app's interactivity, check the bot is in `check_permission
48
48
  Slack refused a post to the persona's destination; a `persona-destination-failed` line names its error, and the prompts and notices are held and retried. For `missing_scope` add `im:write` and re-install the app; for `not_in_channel` invite the app to its destination channel. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-permission-prompt-or-notice-doesnt-arrive).
49
49
 
50
50
  **Bot appears dead / posts a "blocked on a native Claude Code permission prompt" warning**
51
- The bot is stuck on a Claude Code prompt that never reached Slack. A human reads it with `agent-director read-pane --claude-instance-id <id>`, ends the session with `agent-director kill --claude-instance-id <id>` and checks the result; on an error, don't delete or respawn, and never use `send-keys`. The server then brings the persona up again. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-bot-appears-dead--posts-a-blocked-on-a-native-claude-code-permission-prompt-warning).
51
+ The bot is stuck on a Claude Code prompt that never reached Slack. A human reads it with `agent-director read-pane --instance-id <id>`, ends the session with `agent-director kill --instance-id <id>` and checks the result; on an error, don't delete or respawn, and never use `send-keys`. The server then brings the persona up again. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-bot-appears-dead--posts-a-blocked-on-a-native-claude-code-permission-prompt-warning).
52
52
 
53
53
  **Session not restarting after crash**
54
54
  Restarts back off from `session_restart_delay`, and after 5 failed launches in a row the persona posts `SpawnCapReached` and automatic restarts stop. Fix the cause, then restart the server (`claude-slack-channel-bots stop && claude-slack-channel-bots start`) to clear the limit. Each message lost meanwhile gets one lost-message notice naming the recovery state. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-session-not-restarting-after-crash).
@@ -72,7 +72,7 @@ The session runs, but messages can't reach it. A human attaches with `tmux attac
72
72
  agent-director or tmux is not answering for this persona's session; the server keeps retrying and posts *Answering again* when it recovers. After *Still not answering*, a human checks tmux and agent-director with read-only commands only. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-not-answering-still-not-answering-or-answering-again-notice).
73
73
 
74
74
  **A persona posts a *Slow recovery* notice**
75
- agent-director has not yet marked the gone session's row ended or missing; the server relaunches the persona once it does. Nothing is needed unless it persists; then read the row with `agent-director get --claude-instance-id cscb_<key>`. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-slow-recovery-notice).
75
+ agent-director has not yet marked the gone session's row ended or missing; the server relaunches the persona once it does. Nothing is needed unless it persists; then read the row with `agent-director get --instance-id cscb_<key>`. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-slow-recovery-notice).
76
76
 
77
77
  **A persona posts a *Launch stuck* or *Session not starting* notice**
78
78
  The launch did not start in time. *Launch stuck*: the server ended its own launch and relaunched it, and nothing is needed. *Session not starting*: a human answers its startup prompt (`tmux attach -t =slack_bot_<key>`) or ends the launch through agent-director's "Operator actions". [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-launch-stuck-or-session-not-starting-notice).
@@ -93,10 +93,10 @@ The persona's row reads pending with no launch start (an older agent-director pr
93
93
  agent-director rejected the launch's flags (`ErrInvalidFlags`), so the installed agent-director may not match this release. A human checks it against the [Prerequisites](../README.md#prerequisites) and installs it as the docs/migration.md section "Switching over to agent-director Phase 1" describes; the hold ends when the binary changes or the server restarts. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-cannot-launch-notice).
94
94
 
95
95
  **A persona posts a *Kill failed* or *Process outlived kill* notice**
96
- *Kill failed*: the old worker may still run, and nothing is launched over it; a human looks with `agent-director read-pane --claude-instance-id <id>`, ends it with `agent-director kill --claude-instance-id <id>` and checks the result; on an error, don't delete or respawn. *Process outlived kill*: the persona comes back as usual; the process it names is for a human to check. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-kill-failed-or-process-outlived-kill-notice).
96
+ *Kill failed*: the old worker may still run, and nothing is launched over it; a human looks with `agent-director read-pane --instance-id <id>`, ends it with `agent-director kill --instance-id <id>` and checks the result; on an error, don't delete or respawn. *Process outlived kill*: the persona comes back as usual; the process it names is for a human to check. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-a-kill-failed-or-process-outlived-kill-notice).
97
97
 
98
98
  **A persona posts an *agent-director refuses its config file* notice**
99
- A human fixes `~/.agent-director/config.toml` following agent-director's documentation; an *All clear.* notice naming `ad-config-malformed` confirms it. A working `agent-director version` proves nothing here. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-an-agent-director-refuses-its-config-file-notice).
99
+ A human fixes `~/.config/agent-director/config.toml` following agent-director's documentation; an *All clear.* notice naming `ad-config-malformed` confirms it. A working `agent-director version` proves nothing here. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-an-agent-director-refuses-its-config-file-notice).
100
100
 
101
101
  **A persona posts an *Unclassified agent-director error* notice**
102
102
  agent-director returned an error the server can't classify; the server keeps retrying. A human checks the host's agent-director and the persona's lines in `server.log`; the next retry recovers the persona once agent-director answers normally. [Full entry](../skills/debug-slack-channel-bots/SKILL.md#symptom-a-persona-posts-an-unclassified-agent-director-error-notice).
@@ -133,4 +133,6 @@ Note this covers only `server.log` / `clean_restart.log`. `startup-errors.log` a
133
133
 
134
134
  ## Startup errors
135
135
 
136
+ Ordinary older agent-director releases and 0.12.0 release candidates are refused as `ad-system-install-too-old`. The unsupported development placeholder `0.0.0-dev` reaches CSCB's own guard and is refused as `ad-below-phase1-floor`. [Full entries](../skills/debug-slack-channel-bots/SKILL.md#ad-below-phase1-floor).
137
+
136
138
  CSCB writes startup errors to `~/.config/claude-slack-channel-bots/startup-errors.log` (under `SLACK_STATE_DIR` when it is set) and to stderr, one timestamped line each. The file is append-only and never rotated by CSCB; copy `docs/logrotate-startup-errors.conf` into `/etc/logrotate.d/` for host-level rotation. A fatal class, such as a refused agent-director binary or `retired-keys-unreadable`, stops the process; the others are recorded and the start continues. A refused state-directory move at the first start after an upgrade is the exception to where the file is: it is logged in the directory the CLI uses before the move, normally the old one (see [The state directory's location and its move at start](../skills/debug-slack-channel-bots/SKILL.md#the-state-directorys-location-and-its-move-at-start)). [Every class, what it means and what to do](../skills/debug-slack-channel-bots/SKILL.md#startup-errors-startup-errorslog).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-slack-channel-bots",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "Run several Claude Code personas, each its own Slack app, from one server via Socket Mode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,7 +43,7 @@
43
43
  "@modelcontextprotocol/sdk": "^1.0.0",
44
44
  "@slack/socket-mode": "^2.0.0",
45
45
  "@slack/web-api": "^7.0.0",
46
- "agent-director": "0.11.0",
46
+ "agent-director": "0.12.0",
47
47
  "croner": "^10.0.1",
48
48
  "semver": "^7.6.0",
49
49
  "smol-toml": "1.9.0"