Skill Tracer
Skills › Compound Engineering › ce-sweep › interview.md

interview.md

208 lines · skills/ce-sweep/references/interview.md at 3449230

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Sweep First-Run Interview
2
3Loaded by `SKILL.md` when `ce-sweep` runs with `feedback_sources` unset in both the local override file and `config.yaml`. It captures the setup that will be merged into `<repo-root>/.compound-engineering/config.local.yaml`, the optional local override file that interviews write to. Later runs re-read those keys from the local file first, then from `config.yaml`.
4
5This interview is **interactive only**. The caller refuses first-run setup in non-interactive mode. A scheduled or piped run with no config aborts and tells the user to run `ce-sweep` interactively once. Do not attempt to infer sources, actions, or approvals without asking.
6
7**User-runnable invocation rendering.** Whenever this interview prints or registers a `ce-sweep` invocation, default to `/ce-sweep` (plus any arguments); use `$ce-sweep` only when the active host is Codex or explicitly documents dollar-prefixed skill invocation. On oh-my-pi (`omp`), use `/skill:ce-sweep` (plus any arguments). Render only the invocation as inline code and output one form only.
8

Sweep First-Run Interview

Loaded by SKILL.md when ce-sweep runs with feedback_sources unset in both the local override file and config.yaml. It captures the setup that will be merged into <repo-root>/.compound-engineering/config.local.yaml, the optional local override file that interviews write to. Later runs re-read those keys from the local file first, then from config.yaml.

This interview is interactive only. The caller refuses first-run setup in non-interactive mode. A scheduled or piped run with no config aborts and tells the user to run ce-sweep interactively once. Do not attempt to infer sources, actions, or approvals without asking.

User-runnable invocation rendering. Whenever this interview prints or registers a ce-sweep invocation, default to /ce-sweep (plus any arguments); use $ce-sweep only when the active host is Codex or explicitly documents dollar-prefixed skill invocation. On oh-my-pi (omp), use /skill:ce-sweep (plus any arguments). Render only the invocation as inline code and output one form only.

Raw lines 1 to 8
9## Interaction Method
11Ask **one question at a time** using the host's blocking question tool already in the current tool list (match by capability, not by a host-specific name). Presence in the current tool list is proof the tool exists; never call a user-facing question tool to discover whether it exists. If a matching tool is listed but unloaded, use the host's tool-discovery primitive to load that capability. Do not search for another host's tool name. Fall back to numbered options in the host's user-visible chat only when no such tool is in the list or a real question call errors. Never silently skip a question, and never assume a default without telling the user.

Interaction Method

Ask one question at a time using the host's blocking question tool already in the current tool list (match by capability, not by a host-specific name). Presence in the current tool list is proof the tool exists; never call a user-facing question tool to discover whether it exists. If a matching tool is listed but unloaded, use the host's tool-discovery primitive to load that capability. Do not search for another host's tool name. Fall back to numbered options in the host's user-visible chat only when no such tool is in the list or a real question call errors. Never silently skip a question, and never assume a default without telling the user.

Raw lines 9 to 12
13## Overall Rules
151. **One source at a time, fully.** Sections 1-3 form a per-source loop. For each source, capture its identity, its acknowledgment actions plus standing approval, and its sensitivity flag before moving to the next source. Do not batch these across sources. A user answering "approve writes" needs to know *which* source they are approving.
162. **Standing approval is consent, captured verbatim.** Section 2's approval question authorizes source-side writes (Slack reactions, GitHub labels) on every future run with no per-run confirmation. Record the literal yes/no. A "no" is not a failure. It leaves that source read-only.
173. **Defaults are shown, not silently applied.** Every question with a default states the default in the question. The user accepts or overrides; you never pick for them.
184. **Capture in the user's own terms.** Config ids, emoji names, and label names are read by the whole team and used verbatim by the connectors. Record exactly what the user gives.
20---

Overall Rules

  1. One source at a time, fully. Sections 1-3 form a per-source loop. For each source, capture its identity, its acknowledgment actions plus standing approval, and its sensitivity flag before moving to the next source. Do not batch these across sources. A user answering "approve writes" needs to know which source they are approving.
  2. Standing approval is consent, captured verbatim. Section 2's approval question authorizes source-side writes (Slack reactions, GitHub labels) on every future run with no per-run confirmation. Record the literal yes/no. A "no" is not a failure. It leaves that source read-only.
  3. Defaults are shown, not silently applied. Every question with a default states the default in the question. The user accepts or overrides; you never pick for them.
  4. Capture in the user's own terms. Config ids, emoji names, and label names are read by the whole team and used verbatim by the connectors. Record exactly what the user gives.

Raw lines 13 to 21
22## 1. Sources (repeatable loop)
24**Opening framing:** "Let's wire up the feedback sources this sweep will watch. We'll add them one at a time. You can add as many as you want."
26For each source, ask two things:
281. **Source type**, one of:
29 - `slack`, a Slack channel
30 - `github-issues`, a GitHub repository's issues
31 - `email-experimental`, an email account or folder (experimental; stored in config as `type: email`)
322. **Identity**, which depends on the type:
33 - Slack: the **channel ID** (e.g. `C0XXXXXXX`, not the `#name`). Stored as `target`.
34 - GitHub: the repo as `owner/repo`. Stored as `target`.
35 - Email: the account plus a folder/label hint (e.g. `feedback@acme.com / Inbox`). Stored as `target`.
37Then assign the source a **short config id**. This is a stable, lowercase, hyphenated handle that the state file and reports use to name this source (e.g. `slack-alpha`, `gh-issues`). Suggest one derived from the type and let the user override it. Ids must be unique within `feedback_sources`.
39After each source's actions and sensitivity are captured (sections 2-3), ask: **"Add another source?"** Loop until the user is done. At least one source is required to proceed.
41**Capture per source:** `type` (`slack` | `github-issues` | `email`), `id` (short handle), `target` (channel ID / `owner/repo` / mailbox hint).
43---

1. Sources (repeatable loop)

Opening framing: "Let's wire up the feedback sources this sweep will watch. We'll add them one at a time. You can add as many as you want."

For each source, ask two things:

  1. Source type, one of:
  2. slack, a Slack channel
  3. github-issues, a GitHub repository's issues
  4. email-experimental, an email account or folder (experimental; stored in config as type: email)
  5. Identity, which depends on the type:
  6. Slack: the channel ID (e.g. C0XXXXXXX, not the #name). Stored as target.
  7. GitHub: the repo as owner/repo. Stored as target.
  8. Email: the account plus a folder/label hint (e.g. feedback@acme.com / Inbox). Stored as target.

Then assign the source a short config id. This is a stable, lowercase, hyphenated handle that the state file and reports use to name this source (e.g. slack-alpha, gh-issues). Suggest one derived from the type and let the user override it. Ids must be unique within feedback_sources.

After each source's actions and sensitivity are captured (sections 2-3), ask: "Add another source?" Loop until the user is done. At least one source is required to proceed.

Capture per source: type (slack | github-issues | email), id (short handle), target (channel ID / owner/repo / mailbox hint).


Raw lines 22 to 44
45## 2. Acknowledgment actions + standing approval (per source)
47Every source carries two source-side actions the sweep can perform, plus a standing approval that decides whether it may perform them unattended.
49**Ask the acknowledgment action.** This is what the sweep does to mark an item *seen* on its source:
51- Slack: an **emoji reaction** name. Default `eyes`.
52- GitHub: a **label** to apply. Default `feedback:ack`.
53- Email: none. Email items are tracked only in state; there is no source-side ack. Skip this question for email sources and say so.
55**Ask the close-out action.** This is what the sweep does to mark an item *resolved* on its source:
57- Slack: an emoji reaction. Default `white_check_mark`.
58- GitHub: a label. Default `feedback:resolved`.
59- Email: none. Email items stay state-tracked only. Explain that there is no source-side close-out.
61**Then ask the standing-approval question, verbatim:**
63> "Do you approve the sweep performing these actions — applying the acknowledgment and close-out `{{action names}}` on `{{source id}}` — on **every future run, without asking you again each time**? Yes authorizes source-side writes for this source going forward. No keeps this source read-only: the sweep ingests and triages items but never touches the source, and items land as `ack_deferred` for you to action manually."
65Record the literal answer:
67- **Yes** -> `approved: true`. The sweep may apply the ack and close-out actions on this source unattended.
68- **No** -> `approved: false`. The source is read-only. Its items are tracked as `ack_deferred` and no reaction or label is ever written.
70For email sources there are no source-side actions, so approval does not apply. Record `approved: false` and note that the source is inherently read-only.
72**Capture per source:** `ack_action` (emoji/label name, or omit for email), `closeout_action` (emoji/label name, or omit for email), `approved` (`true` | `false`).
74---

2. Acknowledgment actions + standing approval (per source)

Every source carries two source-side actions the sweep can perform, plus a standing approval that decides whether it may perform them unattended.

Ask the acknowledgment action. This is what the sweep does to mark an item seen on its source:

  • Slack: an emoji reaction name. Default eyes.
  • GitHub: a label to apply. Default feedback:ack.
  • Email: none. Email items are tracked only in state; there is no source-side ack. Skip this question for email sources and say so.

Ask the close-out action. This is what the sweep does to mark an item resolved on its source:

  • Slack: an emoji reaction. Default white_check_mark.
  • GitHub: a label. Default feedback:resolved.
  • Email: none. Email items stay state-tracked only. Explain that there is no source-side close-out.

Then ask the standing-approval question, verbatim:

"Do you approve the sweep performing these actions — applying the acknowledgment and close-out {{action names}} on {{source id}} — on every future run, without asking you again each time? Yes authorizes source-side writes for this source going forward. No keeps this source read-only: the sweep ingests and triages items but never touches the source, and items land as ack_deferred for you to action manually."

Record the literal answer:

  • Yes -> approved: true. The sweep may apply the ack and close-out actions on this source unattended.
  • No -> approved: false. The source is read-only. Its items are tracked as ack_deferred and no reaction or label is ever written.

For email sources there are no source-side actions, so approval does not apply. Record approved: false and note that the source is inherently read-only.

Capture per source: ack_action (emoji/label name, or omit for email), closeout_action (emoji/label name, or omit for email), approved (true | false).


Raw lines 45 to 75
76## 3. Sensitive flag (per source)
78**Ask:** "Should item content from `{{source id}}` be withheld from committed state and from plan text? Say yes when the source can carry screen recordings, PII, customer data, or anything you don't want written to a file that may be committed or shared. When yes, the sweep drops item body and quote before writing state, so only titles, urls, ids, and status persist. Default is no."
80- **No** (default) -> `sensitive: false`. Full item content is retained in state and available to plans.
81- **Yes** -> `sensitive: true`. The state engine drops `body` and `quote` at write time for this source's items, and plans reference items by id, title, and url only.
83**Capture per source:** `sensitive` (`true` | `false`).
85---

3. Sensitive flag (per source)

Ask: "Should item content from {{source id}} be withheld from committed state and from plan text? Say yes when the source can carry screen recordings, PII, customer data, or anything you don't want written to a file that may be committed or shared. When yes, the sweep drops item body and quote before writing state, so only titles, urls, ids, and status persist. Default is no."

  • No (default) -> sensitive: false. Full item content is retained in state and available to plans.
  • Yes -> sensitive: true. The state engine drops body and quote at write time for this source's items, and plans reference items by id, title, and url only.

Capture per source: sensitive (true | false).


Raw lines 76 to 86
87## 4. State location
89Ask where the sweep's state file lives:
91- **Committed to the repo** (recommended when multiple agents or machines share branches, so everyone reads and writes one source of truth). Sets `sweep_state_path` to the committed default under the artifact root's `feedback-sweep/`. Resolve `<root>` to its concrete value first (e.g. the default `docs`), so the persisted value is `<resolved-root>/feedback-sweep/state.yml` and never the literal `<root>` placeholder (per the persist rule below).
92- **Machine-local under `/tmp`** (solo setups; keeps sweep bookkeeping out of the repo, with no commit noise). Resolve the path immediately with this shell block, substituting a sanitized repository slug:
94 ```bash
95 SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)";
96 [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)";
97 if [ -L "$SCRATCH_ROOT" ]; then echo "unsafe scratch root symlink: $SCRATCH_ROOT" >&2; exit 1; fi;
98 (umask 077; mkdir -p "$SCRATCH_ROOT") || exit 1;
99 if [ -L "$SCRATCH_ROOT" ] || [ ! -O "$SCRATCH_ROOT" ]; then echo "scratch root is not owned by the current user: $SCRATCH_ROOT" >&2; exit 1; fi;
100 chmod 700 "$SCRATCH_ROOT" || exit 1;
101 SWEEP_STATE_PATH="$SCRATCH_ROOT/ce-sweep/<repo-slug>/state.yml";
102 SWEEP_STATE_DIR="$(dirname "$SWEEP_STATE_PATH")"; (umask 077; mkdir -p "$SWEEP_STATE_DIR") || exit 1; chmod 700 "$SWEEP_STATE_DIR" || exit 1;
103 echo "$SWEEP_STATE_PATH";
104 ```
106 Persist the echoed absolute path as `sweep_state_path`; never persist a placeholder.
108Let the user override the path if they want a different location. If they pick machine-local, note that a fresh checkout or a teammate's machine will not see this state. It is per-machine by design.
110**Capture:** `sweep_state_path` (string).
112---

4. State location

Ask where the sweep's state file lives:

  • Committed to the repo (recommended when multiple agents or machines share branches, so everyone reads and writes one source of truth). Sets sweep_state_path to the committed default under the artifact root's feedback-sweep/. Resolve <root> to its concrete value first (e.g. the default docs), so the persisted value is <resolved-root>/feedback-sweep/state.yml and never the literal <root> placeholder (per the persist rule below).
  • **Machine-local under /tmp** (solo setups; keeps sweep bookkeeping out of the repo, with no commit noise). Resolve the path immediately with this shell block, substituting a sanitized repository slug:

bash SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"; [ ! -L "$SCRATCH_ROOT" ] && (umask 077; mkdir -p "$SCRATCH_ROOT") 2>/dev/null && [ ! -L "$SCRATCH_ROOT" ] && [ -O "$SCRATCH_ROOT" ] && [ -w "$SCRATCH_ROOT" ] || SCRATCH_ROOT="${TMPDIR:-/tmp}/compound-engineering-$(id -u)"; if [ -L "$SCRATCH_ROOT" ]; then echo "unsafe scratch root symlink: $SCRATCH_ROOT" >&2; exit 1; fi; (umask 077; mkdir -p "$SCRATCH_ROOT") || exit 1; if [ -L "$SCRATCH_ROOT" ] || [ ! -O "$SCRATCH_ROOT" ]; then echo "scratch root is not owned by the current user: $SCRATCH_ROOT" >&2; exit 1; fi; chmod 700 "$SCRATCH_ROOT" || exit 1; SWEEP_STATE_PATH="$SCRATCH_ROOT/ce-sweep/<repo-slug>/state.yml"; SWEEP_STATE_DIR="$(dirname "$SWEEP_STATE_PATH")"; (umask 077; mkdir -p "$SWEEP_STATE_DIR") || exit 1; chmod 700 "$SWEEP_STATE_DIR" || exit 1; echo "$SWEEP_STATE_PATH";

Persist the echoed absolute path as sweep_state_path; never persist a placeholder.

Let the user override the path if they want a different location. If they pick machine-local, note that a fresh checkout or a teammate's machine will not see this state. It is per-machine by design.

Capture: sweep_state_path (string).


Raw lines 87 to 113
114## 5. Acknowledgment cap
116**Ask:** "What's the most acknowledgments the sweep may perform on a single source in one run before it pauses? This is a circuit breaker against a runaway sweep spamming a channel or issue tracker. When the cap is hit, an interactive run pauses and asks you; a non-interactive run stops acknowledging and defers the rest. Default is 25."
118**Capture:** `sweep_ack_cap` (integer, default 25).
120---

5. Acknowledgment cap

Ask: "What's the most acknowledgments the sweep may perform on a single source in one run before it pauses? This is a circuit breaker against a runaway sweep spamming a channel or issue tracker. When the cap is hit, an interactive run pauses and asks you; a non-interactive run stops acknowledging and defers the rest. Default is 25."

Capture: sweep_ack_cap (integer, default 25).


Raw lines 114 to 121
122## 6. Shared branch (only if committed state)
124**Skip this section entirely if the user chose machine-local state in section 4.** The shared-branch topology only applies to committed state.
126**Ask:** "Is this a multi-agent setup where several checkouts push the sweep state to a shared docs branch? Answer yes only if more than one machine or agent commits and pushes to the same branch. Default is no, meaning a single checkout committing locally."
128- **No** (default) -> `sweep_shared_branch: false`. The lease that lets only one sweep write at a time makes overlapping sweeps within one checkout take turns.
129- **Yes** -> `sweep_shared_branch: true`. Explain that the lease becomes **push-gated**: before any source-side write, the sweep commits and pushes the lease acquisition on the shared branch and confirms its writer won. This makes the lease a repo-wide mutex across machines.
131**Capture:** `sweep_shared_branch` (`true` | `false`).
133---

6. Shared branch (only if committed state)

Skip this section entirely if the user chose machine-local state in section 4. The shared-branch topology only applies to committed state.

Ask: "Is this a multi-agent setup where several checkouts push the sweep state to a shared docs branch? Answer yes only if more than one machine or agent commits and pushes to the same branch. Default is no, meaning a single checkout committing locally."

  • No (default) -> sweep_shared_branch: false. The lease that lets only one sweep write at a time makes overlapping sweeps within one checkout take turns.
  • Yes -> sweep_shared_branch: true. Explain that the lease becomes push-gated: before any source-side write, the sweep commits and pushes the lease acquisition on the shared branch and confirms its writer won. This makes the lease a repo-wide mutex across machines.

Capture: sweep_shared_branch (true | false).


Raw lines 122 to 134
135## 7. Legacy import (optional)
137Offer to seed state from an existing legacy feedback-tracking file so prior work is not re-ingested and already-acknowledged items are not acknowledged again.
139**Ask:** "Do you have an existing feedback state file to import? For example, a prior dogfood tracker like `<root>/dogfood-reports/cora-v2-alpha-feedback-state.yml`. Importing carries over its cursors and items so the first sweep skips what's already been processed. Skip if this is a clean start."
141- **No / skip** -> proceed to section 8.
142- **Yes** -> ask for the file path. Then build a `--source-map`. For each legacy channel or source id in the file, pair it with the configured source id from section 1 (the short name the live connector reads by), as a JSON object like `{"C0AQLMQBGBD":"slack-alpha"}`. This map is required. Without it, an imported `C0AQLMQBGBD` cursor lands under `C0AQLMQBGBD` while the connector reads under `slack-alpha`. That orphans the cursor and re-ingests everything on the first sweep. Run the import from **this skill's directory**; set `SKILL_DIR` inline to the absolute path of the directory containing the `SKILL.md` you loaded:
144 ```bash
145 SKILL_DIR="<absolute path of this skill's directory>";
146 PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; };
147 "$PY" "$SKILL_DIR/scripts/sweep-state.py" import-legacy --state <sweep_state_path> --file <legacy-path> --source-map '{"<legacy-id>":"<config-source-id>"}'
148 ```
150 where `<sweep_state_path>` is the value captured in section 4 and `<legacy-path>` is the file the user named. Omit `--source-map` only when the legacy ids already equal the configured source ids. Report the `cursors_imported` and `items_imported` counts the command returns. The import is additive and best-effort. It maps what matches known shapes and skips the rest. It does **not** re-ingest source content and does **not** re-acknowledge imported items. Mapped cursors carry forward, so already-processed items stay processed.
152---

7. Legacy import (optional)

Offer to seed state from an existing legacy feedback-tracking file so prior work is not re-ingested and already-acknowledged items are not acknowledged again.

Ask: "Do you have an existing feedback state file to import? For example, a prior dogfood tracker like <root>/dogfood-reports/cora-v2-alpha-feedback-state.yml. Importing carries over its cursors and items so the first sweep skips what's already been processed. Skip if this is a clean start."

  • No / skip -> proceed to section 8.
  • Yes -> ask for the file path. Then build a --source-map. For each legacy channel or source id in the file, pair it with the configured source id from section 1 (the short name the live connector reads by), as a JSON object like {"C0AQLMQBGBD":"slack-alpha"}. This map is required. Without it, an imported C0AQLMQBGBD cursor lands under C0AQLMQBGBD while the connector reads under slack-alpha. That orphans the cursor and re-ingests everything on the first sweep. Run the import from this skill's directory; set SKILL_DIR inline to the absolute path of the directory containing the SKILL.md you loaded:

bash SKILL_DIR="<absolute path of this skill's directory>"; PY="$(for c in python3 python py; do command -v "$c" >/dev/null 2>&1 && "$c" -c '' >/dev/null 2>&1 && { echo "$c"; break; }; done)"; [ -n "$PY" ] || { echo "no working Python 3 interpreter on PATH" >&2; exit 1; }; "$PY" "$SKILL_DIR/scripts/sweep-state.py" import-legacy --state <sweep_state_path> --file <legacy-path> --source-map '{"<legacy-id>":"<config-source-id>"}'

where <sweep_state_path> is the value captured in section 4 and <legacy-path> is the file the user named. Omit --source-map only when the legacy ids already equal the configured source ids. Report the cursors_imported and items_imported counts the command returns. The import is additive and best-effort. It maps what matches known shapes and skips the rest. It does not re-ingest source content and does not re-acknowledge imported items. Mapped cursors carry forward, so already-processed items stay processed.


Raw lines 135 to 153
Names a skill ce-dogfood :139
Runs code scripts/sweep-state.py :147
154## 8. Write config
156Merge the captured settings into `<repo-root>/.compound-engineering/config.local.yaml`. Resolve the repo root with `git rev-parse --show-toplevel`.
158- If the directory or file does not exist, create `.compound-engineering/` and write the file.
159- If the file exists, merge the sweep keys into the existing YAML, **preserving every unrelated key untouched** (e.g. `pulse_*`, `plan_*`). Only add or update the sweep keys.
160- If `.compound-engineering/config.local.yaml` is not already covered by the repo's `.gitignore`, offer to add the entry before writing.
162Write these keys (see "Config File Shape" below for the exact form):
164- `feedback_sources`, the list of source maps assembled across sections 1-3.
165- `sweep_state_path`, from section 4.
166- `sweep_ack_cap`, from section 5.
167- `sweep_shared_branch`, from section 6 (default `false`; only meaningful with committed state).
169Then show the user the resulting Sweep section in chat and offer **one round of edits**.
171---

8. Write config

Merge the captured settings into <repo-root>/.compound-engineering/config.local.yaml. Resolve the repo root with git rev-parse --show-toplevel.

  • If the directory or file does not exist, create .compound-engineering/ and write the file.
  • If the file exists, merge the sweep keys into the existing YAML, preserving every unrelated key untouched (e.g. pulse_*, plan_*). Only add or update the sweep keys.
  • If .compound-engineering/config.local.yaml is not already covered by the repo's .gitignore, offer to add the entry before writing.

Write these keys (see "Config File Shape" below for the exact form):

  • feedback_sources, the list of source maps assembled across sections 1-3.
  • sweep_state_path, from section 4.
  • sweep_ack_cap, from section 5.
  • sweep_shared_branch, from section 6 (default false; only meaningful with committed state).

Then show the user the resulting Sweep section in chat and offer one round of edits.


Raw lines 154 to 172
Names a skill ce-plan :159
173## 9. Schedule offer
175**Ask:** "Want the sweep to run on a recurring schedule so feedback gets triaged automatically, or run it on demand? On-demand works fully without a schedule."
177- **On demand** -> nothing to register. Note that the rendered `ce-sweep` invocation is ready to run any time.
178- **Recurring** -> hand off to whichever scheduling primitive the harness exposes. Use the in-plugin `schedule` skill if it is installed. Otherwise name the platform-native mechanism (cron, GitHub Actions, the host's own automation) and give a brief hint of what would need to run. **The registered invocation must include `mode:non-interactive`** (deprecated alias `mode:headless` still works if already registered) using the rendering rule above, so the scheduled run knows it is unattended and defers instead of prompting. Never schedule inline; always hand off to the scheduling primitive.
180Declining a schedule leaves on-demand use fully working.
182**End the interview:** tell the user setup is complete and print the rendered `ce-sweep` invocation for the first run.
184---

9. Schedule offer

Ask: "Want the sweep to run on a recurring schedule so feedback gets triaged automatically, or run it on demand? On-demand works fully without a schedule."

  • On demand -> nothing to register. Note that the rendered ce-sweep invocation is ready to run any time.
  • Recurring -> hand off to whichever scheduling primitive the harness exposes. Use the in-plugin schedule skill if it is installed. Otherwise name the platform-native mechanism (cron, GitHub Actions, the host's own automation) and give a brief hint of what would need to run. **The registered invocation must include mode:non-interactive** (deprecated alias mode:headless still works if already registered) using the rendering rule above, so the scheduled run knows it is unattended and defers instead of prompting. Never schedule inline; always hand off to the scheduling primitive.

Declining a schedule leaves on-demand use fully working.

End the interview: tell the user setup is complete and print the rendered ce-sweep invocation for the first run.


Raw lines 173 to 185
186## Config File Shape
188After the interview completes, merge these flat keys into `<repo-root>/.compound-engineering/config.local.yaml`, preserving any unrelated keys already present.
190~~~yaml
191# --- Sweep (ce-sweep) ---
193feedback_sources:
194 - { type: slack, id: slack-alpha, target: C0XXXXXXX, ack_action: eyes, closeout_action: white_check_mark, sensitive: false, approved: true }
195 - { type: github-issues, id: gh-issues, target: owner/repo, ack_action: "feedback:ack", closeout_action: "feedback:resolved", sensitive: false, approved: true }
197sweep_state_path: <resolved-root>/feedback-sweep/state.yml # concrete path (<root> resolved before persisting); committed (multi-agent) or a /tmp path (solo)
198sweep_ack_cap: 25 # max acks per source per run before the circuit breaker
199sweep_lease_ttl_minutes: 60 # single-writer lease staleness threshold; not asked interactively, tunable here
200sweep_shared_branch: false # true: push-gated lease for shared-docs-branch topology
201~~~
203Notes:
205- Each `feedback_sources` entry carries: `type` (`slack` | `github-issues` | `email`), `id` (short handle), `target` (channel ID / `owner/repo` / mailbox hint), `ack_action` and `closeout_action` (emoji/label names; omit both for email), `sensitive` (`true` withholds body/quote from committed state and plan text), and `approved` (standing approval for source-side writes; `false` keeps the source read-only with `ack_deferred` items).
206- `feedback_sources` is a generic key. Other skills may read this list.
207- `sweep_lease_ttl_minutes` is not asked in the interview. It is written with its default of `60` and left as a tunable the user can edit.
208- Email sources are read-only: omit `ack_action`/`closeout_action`, and record `approved: false`.

Config File Shape

After the interview completes, merge these flat keys into <repo-root>/.compound-engineering/config.local.yaml, preserving any unrelated keys already present.

~~~yaml

--- Sweep (ce-sweep) ---

feedback_sources:

  • { type: slack, id: slack-alpha, target: C0XXXXXXX, ack_action: eyes, closeout_action: white_check_mark, sensitive: false, approved: true }
  • { type: github-issues, id: gh-issues, target: owner/repo, ack_action: "feedback:ack", closeout_action: "feedback:resolved", sensitive: false, approved: true }

sweep_state_path: <resolved-root>/feedback-sweep/state.yml # concrete path (<root> resolved before persisting); committed (multi-agent) or a /tmp path (solo) sweep_ack_cap: 25 # max acks per source per run before the circuit breaker sweep_lease_ttl_minutes: 60 # single-writer lease staleness threshold; not asked interactively, tunable here sweep_shared_branch: false # true: push-gated lease for shared-docs-branch topology ~~~

Notes:

  • Each feedback_sources entry carries: type (slack | github-issues | email), id (short handle), target (channel ID / owner/repo / mailbox hint), ack_action and closeout_action (emoji/label names; omit both for email), sensitive (true withholds body/quote from committed state and plan text), and approved (standing approval for source-side writes; false keeps the source read-only with ack_deferred items).
  • feedback_sources is a generic key. Other skills may read this list.
  • sweep_lease_ttl_minutes is not asked in the interview. It is written with its default of 60 and left as a tunable the user can edit.
  • Email sources are read-only: omit ack_action/closeout_action, and record approved: false.
Raw lines 186 to 208
Uses an outside tool ~~yaml :190