Scripts
Echo’s behavior lives in deterministic scripts; the slash commands and worker
prompts are thin front-ends over them. This page maps every script under
skills/echo-core/scripts/ and skills/echo-backlog/scripts/, grouped by the
role it plays. Summaries are derived from each script’s own header comment.
Everything reads configuration and paths through two keystone helpers, so nothing hardcodes a board slug, status name, or directory:
| Script | What it does |
|---|---|
echo-config | The keystone config resolver. Deep-merges defaults.json ← registry entry ← per-project config.json and prints the merged object or a dotted-path scalar. Every script reads config through this. |
echo-paths | Derives a registered project’s runtime paths (state dir, lock/flock files, worker-pid glob, worktree base, agent-runs dir, outbox/notifications dirs) from config + project name. |
locate_claude.sh | (Sourced) Resolves the claude CLI to a real executable path — spawns need a real binary, not a shell alias. |
Setup / onboarding
Section titled “Setup / onboarding”Run by /echo:setup, one phase each. All live under scripts/setup/.
| Script | What it does / when it runs |
|---|---|
setup/inspect.sh | Read-only repo inspection for the interview phase — cold-detects repo slug, owner, and proposals (--json). |
setup/preflight.sh | Provisioning-readiness checks; prints an error list (not a first-failure throw) that must clear before provisioning. |
setup/provision_board.sh | Create-or-adopt the GitHub Projects v2 board + its single-select fields; --dry-run previews first. |
setup/write_config.sh | Persists the assembled per-project config.json, backing up any existing file with a timestamp first (safe reconfigure). |
setup/register_project.sh | Adds/updates the project’s entry in the machine registry — replaces, never appends, an existing entry for the same root. |
setup/bootstrap_claudemd.sh | Drafts (and, with --apply, writes) a subsystem-map CLAUDE.md; refuses to overwrite a substantial existing file. |
Dispatch
Section titled “Dispatch”The /echo:tick sweep and the per-project pipeline it hands off to.
| Script | What it does / when it runs |
|---|---|
tick.sh | One hub sweep over every enabled registered project: global gates once, then per-project fast-paths; one status line per project. Never exits non-zero for a per-project condition. |
dispatch_one.sh | The per-project pipeline tick.sh hands a dispatchable project to: lock → select → pre-validate → flip Ready→Planning → spawn worker → comment → release. |
lock.sh | Acquires the per-project dispatcher lock + a free worker slot (bounded-N concurrency). Honors ECHO_DISPATCHER_DISABLED and the ECHO_WORKER_SLOTS=0 soft kill-switch. |
select_next_item.sh | Picks the next eligible Ready item (status == Ready, issue not closed), sorted by the project’s Priority rank then board position. Emits its metadata as JSON. |
prevalidate_item.sh | The one LLM-judgment dispatch step: a small-model classify of the item as proceed / needs_human / defer, handling the side effects of the latter two. |
chain_next.sh | Chain-triggers the next sweep the moment a worker frees a slot — so freed capacity or Ready work elsewhere is picked up without waiting for the next cron tick. |
Worker
Section titled “Worker”Spawned per item; the worker drives brainstorm → plan → implement → PR → review.
| Script | What it does / when it runs |
|---|---|
spawn_worker.sh | Launches a detached claude -p worker for a chosen item: renders the cacheable prompt prefix, cuts a worktree off the base branch, records the PID. |
check_test_coverage.sh | Mechanical PR test-coverage gate — a large non-docs diff must touch tests or carry a ## Test justification; tags needs-tests (blocking only when it also touches auth/isolation paths, exit 20). |
sensitive_path_gate.sh | The single source of truth for “does this changed-file set touch auth/tenant-isolation code?” — a match makes the worker hold auto-merge and route to human review. |
file_review_findings.sh | Files one tracked backlog item per confirmed-and-unfixed adversarial-review finding, so surfaced debt leaves a trail instead of just a PR comment. |
flip_to_needs_human.sh | Centralized wrapper to flip an item to Needs Human — status mutation + issue comment + queued Discord outbox notification, as one unit. |
flip_to_ready.sh | Re-queues an item to Ready with operator guidance as an issue comment — the inbound mirror of flip_to_needs_human.sh. |
Merge / reconcile
Section titled “Merge / reconcile”Trusted dispatcher-side merge and the post-merge routing sweep.
| Script | What it does / when it runs |
|---|---|
gated_merge.sh | Trusted dispatcher-side merge: selects AI-reviewing items whose open PR carries echo:merge-ready, re-runs the deterministic gates, then merges or holds for a human. |
reconcile.sh | The universal post-merge trigger: for any in-flight item whose worker branch has a merged PR, routes it onward — straight to Done (verify.type: none) or to Building & Deploying + a deploy-watch. |
Deploy-watch
Section titled “Deploy-watch”Post-merge verification that a change actually reached production.
| Script | What it does / when it runs |
|---|---|
spawn_watcher.sh | Detached, read-only deploy-watch launcher (per-item locked, idempotent). Refuses to launch on a non-read-only verify command; escalates instead. |
watcher-run.sh | Parent shell of the detached deploy-watch process — forces a terminal board flip if the watcher ever exits without finalizing (the death backstop). |
finalize_watcher.sh | Owns the terminal board transition for a deploy-watch (done / needs-human / guard); idempotent, read-back-confirmed, never a false Done. |
checkout_supporting_repo.sh | Lazily creates a worktree of a declared supportingRepos repo for cross-repo edits (e.g. an image-tag bump); prefers a local clone, falls back to cloning. |
resolve_supporting_repo.sh | Looks up one declared supportingRepos entry by name and emits it as compact JSON. |
Discord
Section titled “Discord”Escalation delivery and inbound reply routing (the posting itself is the hub’s job via the Discord MCP tool; these scripts prepare and record it).
| Script | What it does / when it runs |
|---|---|
drain_outbox.sh | Claims queued Discord escalation files for one project (the atomic mv is the per-file lock, fixing double-posts) and emits their payloads for the hub to post. |
record_delivery.sh | Stamps the posted escalation’s Discord message id into its record so inbound quote-replies can be matched back to the item. |
route_replies.sh | Inbound reply router: for each operator quote-reply matching a Needs-Human escalation, re-queues the item to Ready with the reply text as guidance. Idempotent. |
Best-effort Obsidian-native run notes — never load-bearing.
| Script | What it does / when it runs |
|---|---|
vault_note.sh | Write-side echo-vault notes (Dataview frontmatter + wikilinks): project / run / needs-human / archive / lessons subcommands. Unset vault → no-op. |
distill_run.sh | Best-effort LLM enrichment of a run note with a Decisions/Patterns/Solutions/Blockers taxonomy distilled from the worker transcript. Any failure → silent no-op. |
Backlog (skills/echo-backlog/scripts/)
Section titled “Backlog (skills/echo-backlog/scripts/)”The board read/write layer — used by the sweep, /echo:status, and feature
filing.
| Script | What it does / when it runs |
|---|---|
fields.sh | (Sourced) Shared board identity + live GraphQL resolution of project/field/option ids for a registered project. Every other backlog script sources this. |
list_backlog.sh | Lists board items with optional --status / --category / --priority / --search filters; --json for raw output. Backs /echo:status’s single board read. |
view_item.sh | Shows full detail of one item by issue number or project-item id, merging in the issue body (which the projection doesn’t include). |
create_feature.sh | Files a feature: opens a GitHub issue, adds it to the board, and sets Status/Category/Priority/Size (defaults from createFeature.*). |