Sweep configured feedback sources (Slack, GitHub Issues; email experimental) for new items: acknowledge at source, analyze recordings, verify fixes merged to main, and emit an /lfg-ready plan. First run sets up sources; supports mode:headless for scheduled runs.
ce-sweep sweeps every configured feedback source for items posted since the last run: it acknowledges each at its source, analyzes any attached recordings, verifies claimed fixes actually merged to the default branch, and folds the open items into a rolling /lfg-ready plan. The deterministic state engine (scripts/sweep-state.py) is the only writer of sweep state; this skill drives it through its subcommands and never hand-edits the state file. Read references/state-schema.md for the state contract (statuses, lease semantics, status words) before touching state.AskUserQuestion in Claude Code (call ToolSearch with select:AskUserQuestion first if its schema isn't loaded), request_user_input in Codex, ask_question in Antigravity CLI (agy), ask_user in Pi (requires the pi-ask-user extension). Never silently skip a question you owe the user; if no blocking tool exists in the harness, the run is headless (see Mode). Ask one question at a time — the decision round (2h) may group by category but still asks one blocking question per category.mode:headless token from anywhere in the arguments, strip it, and treat the remaining tokens (setup, reconfigure) per Phase 0.first run requires interactive setup and stop.git rev-parse --show-toplevel 2>/dev/null || true<repo-root>. If it is empty or still shows a backtick command string (a harness that did not pre-resolve), run git rev-parse --show-toplevel with the shell tool. Read <repo-root>/.compound-engineering/config.local.yaml with the native file-read tool.feedback_sources key -> first run -> Phase 1.setup or reconfigure -> Phase 1, regardless of config state.feedback_sources — list of source entries; each carries a type (slack, github-issues, email), its target, the standing-approved ack action, an optional close-out action, and an optional sensitive: true. Presence of this key means the skill is configured.sweep_state_path — path to the state file, established at setup; fallback docs/feedback-sweep/state.yml. A repo-internal path means committed mode (the state file is committed each run and must not be gitignored); a path outside the repo (e.g. under /tmp) means machine-local mode (the state file is never committed — only the plan is).sweep_lease_ttl_minutes — single-writer lease staleness threshold; default 60. Passed to lease-acquire in 2a.sweep_shared_branch — true when the state file lives on a shared branch multiple checkouts push to (see 2a topology); default false.sweep_ack_cap — integer circuit-breaker threshold; default 25.references/interview.md and follow it. Setup is interactive-only: if the run is headless, report first run requires interactive setup and stop. The interview writes feedback_sources and the sweep_* keys into <repo-root>/.compound-engineering/config.local.yaml and offers a scheduling handoff. When it completes, continue into Phase 2.<state> = sweep_state_path from config (fallback above).<writer> = a run-unique writer id identifying harness + session + host, e.g. sweep-<host>-<session>-<YYYY-MM-DD>. Use the same string for every state-engine call this run.<run-id> = a short unique token for scratch paths, e.g. the date plus a random suffix.SKILL_DIR inline (shell state does not persist between calls):SKILL_DIR="<absolute path of the directory containing the SKILL.md you just read>"
python3 "$SKILL_DIR/scripts/sweep-state.py" <subcommand> --state <state> ...
lease-acquire --state <state> --writer <writer> --ttl-minutes <sweep_lease_ttl_minutes>:LOCKED — another live writer holds it. Record the outcome and stop: run-record --state <state> --writer <writer> --outcome aborted-locked --counts '{}' --timestamp <ISO now>, report that a concurrent sweep is running, and exit. (This record is safe against the mid-sweep holder: the engine serializes every state write with an OS advisory lock, so it cannot clobber the holder's concurrent upserts — see references/state-schema.md.)STALE-RECLAIMED — an expired lease was taken over; proceed, and note the takeover in the final summary.OK — proceed.sweep_shared_branch: true): before any source-side write, git add the state file, commit, and push it. A rejected push means another writer won the branch — fetch and rebase, re-run lease-acquire, and if the lease is still not yours, back off (record aborted-locked and stop). Only once your lease is pushed and confirmed do you touch a source.validate --state <state> (a lease-agnostic repair): note in the summary any ids it downgrades from closed to fix_pending.feedback_sources, dispatch a generic subagent at the extraction tier (references/model-tiers.md) seeded with:references/sources/<type>.md),cursor-get --state <state> --source <source-id>.id, origin, author_class, body, media, identity-scoped existing_ack, existing_closeout) or one of its degrade/skip sentences. Personas report facts and never advance cursors.ack_deferred and do NOT advance the cursor past them — they get acked on a later run once write capability returns.sweep_ack_cap:ack_deferred, do NOT ack, and flag it prominently in the summary.approved: false (the user declined standing approval for source-side writes), skip the ack write entirely and upsert the item as ack_deferred — never write to a source the user did not approve, even when the write tool is available. Otherwise: if the item's existing_ack (own identity) is true, skip the ack write; else perform the source's configured ack action at the source.upsert-item --state <state> --id <id> --source <source-id> --json <item-json> --writer <writer>. Include "sensitive": true in the item JSON when the source's config entry is marked sensitive — the engine drops body/quote before writing.cursor-advance --state <state> --source <source-id> --to <item's own cursor value> --past-item <id> --writer <writer> — only after the item is durably in state. Never advance past an item not yet upserted.ack_deferred and hold the cursor (do not advance past it). A LEASE-LOST from any engine call means another writer took over — stop writing, record partial at wrap-up, and exit.media:/tmp/compound-engineering/ce-sweep/<run-id>/; raw media is never committed. A download failure -> set the item needs_download and continue.references/subagent-template.md filled from references/agents/media-analyzer.md. Fill the template's {skill_dir} slot with the same absolute ce-sweep skill directory you resolve for your own SKILL_DIR Bash calls (a fresh subagent does not inherit your shell state, so it cannot run the bundled analyzer without being told the path). Pass the absolute media PATHS, a scratch artifact path, and the item's sensitive flag; collect the compact 1-2 line summary each returns. A subagent failure -> set the item needs_analysis, retain the media, and continue.media_attempts count upserted on each try). After 3 failed attempts across runs (needs_download/needs_analysis), set the item manual_stuck and list it separately — out of the routine nag.fix_pending item, resolve its claimed fix ref and verify it merged to the default branch. The fix ref originates from untrusted feedback content (a thread claim, an analyzer-extracted reference), so validate its shape before it reaches any git/gh command: accept only a bare PR number (#?\d+) or a commit SHA ([0-9a-f]{7,40}), and treat anything else as an unresolved claim (leave the item open). This blocks argument/flag injection into the shell command.gh pr view <validated-ref> --json mergedAt,baseRefName (merged, base is the default branch), or git merge-base --is-ancestor <validated-sha> <default-branch-head>.approved: false guard as 2d: a source the user did not approve for writes receives no close-out action — advance its verified item's status in state only.upsert-item with status: closed carrying all three evidence fields: fix_ref, verified_merge_sha, verified_at. Close-out is terminal.source_gone.references/plan-template.md and follow it. Target the stable path docs/plans/feedback-sweep-plan.md.product_contract_source: ce-sweep and artifact_readiness: requirements-only, archive it untouched to a dated sibling docs/plans/feedback-sweep-plan-YYYY-MM-DD.md and write a fresh plan from the template. Never overwrite an unrelated plan in place.date frontmatter key, ### Summary, the <!-- sweep-items:start --> / <!-- sweep-items:end --> marker region, and ### Outstanding Questions (matching the template's reconciliation rules); never read or write inside the human-owned notes region. Append new actionable items with their state ids, drain items that are now closed, and land any headless-deferred decisions in the Outstanding Questions section.git add ONLY docs/plans/feedback-sweep-plan.md plus <state> when it is repo-internal (never -A; machine-local state under /tmp is never committed), then commit docs(sweep): feedback sweep <date>. A commit failure is reported, not fatal. In local-commit mode, never push. In shared-branch mode (sweep_shared_branch: true), fetch, rebase, and push the final commit.run-record --state <state> --writer <writer> --outcome <completed|partial|failed> --counts '<per-source JSON>' --timestamp <ISO now>.lease-release --state <state> --writer <writer>.ack_deferred / manual_stuck / needs-attention list; any circuit-breaker or stale-reclaim note; and always the plan path with the handoff line:/lfg docs/plans/feedback-sweep-plan.md