import { AMEND_AREA_IDS } from "./setup.ts"; /** Long-form operator help preserved from the pre-manifest CLI. */ export const COMMAND_DETAILS = ` setup interview, then write config.json, the labels, the briefs and the staged host files behind one confirm. Bare is a full run — or, when the project already exists, a chooser of which area to amend. Naming an area positionally skips that chooser and amends only that area: ${AMEND_AREA_IDS.join(", ")}. upgrade discover the installed CLI package root, then update it with the omp plugin, Herdr recovery plugin, and brief as one pinned release. Pauses only new claims, drains live workers, reloads, verifies twice, and restores the prior dispatch state. Run it from a shell outside the target Herdr session. --bootstrap SHA --source PATH installs an exact commit instead of a published version, for when the installed conductor is what is broken: the source must be a checkout AT that sha, the package's own checks run there first, and a check failure refuses rather than installing a partly verified tree. upgrade-install the detached executor half of the fleet-installs-itself request: the same transaction, journaled per surface, leaving verification and dispatch restore to the first tick after the restart. Run by the transient unit, never by hand in a Herdr session. upgrade-rollback the detached rollback unit: restore every surface the failed upgrade-install touched, from the durable pre-install snapshot in the upgrade journal, and report through the durable outbox. start start the installed herdr-fleet.service when present, then run the dispatch loop in the background and wait until it answers GET /healthz on :8787 (override with --port). Refuses if one is running. stop stop the conductor: pause claiming, disarm ticks, then stop the dispatch daemon (systemctl-aware: an explicit stop is recorded by systemd, so Restart=always does not undo it). Pane stays up unless --pane is passed. To bounce the daemon without stopping the fleet, use restart. stop --pane also stop the conductor agent's pane and pin herdr-conductor recovery off for that agent only — it does NOT stop herdr-fleet.service or any other herdr session. resume clears the pin. restart drain the fleet by default: pause new claims, wait until live workers reach 0/N (bounded by --timeout SECONDS, default 1800), restart keeping the running daemon's port and project unless a flag overrides them, then restore the prior dispatch state. --now skips the drain and restarts immediately, orphaning any live runs (old behavior). A drain that hits --timeout restarts nothing and leaves dispatch paused. On boot the new process salvages dirty live worktrees before orphaning those rows — see README "Deploying a new package onto a busy fleet". Goes through systemctl when the unit owns the live pid; a failed installed unit is reset and restarted through systemd, never replaced by an unmanaged daemon. status layered fleet report: dispatch (running|paused|stopped), ticks and next due time, pane, herdr, Telegram bot/API health, daemon, caps and active runs. stats what the fleet accomplished and at what cost, read wholly from the local store: issues merged, merge rate, queue-to-merge lead time (median/p90), attempts per merged issue, metered spend per merged issue, the failure-class breakdown of what did not merge, and tracked gh calls over the window. Continuation chains collapse into one journey; $0.00 runs are reported as unmetered, never averaged in as free. --json prints the stable {project, window, ghCalls, empty, total, repos} shape. doctor read-only deployment health, one finding per past failure mode: gh auth/scopes, exact-case configured labels, systemd unit drift and runtime-dir ownership, config + backup freshness, sqlite integrity, spend telemetry, reporting timezones, Telegram health. The only write anywhere is the self-identified probe message that --probe-telegram sends through the report transport. Exit code 0 only when nothing failed; --json prints the stable CI shape. Run it after install and after every upgrade. snapshot-db publish a transaction-consistent conductor.db snapshot with SQLite VACUUM INTO, record it in the durable event ledger, and apply the daemon cadence's bounded retention. Safe while the daemon writes. restore-db put conductor.db back to a restorable snapshot, replacing the live file and dropping stale -wal/-shm sidecars. Refuses while a daemon is live — restoring under a running daemon is a corruption path. The default source is the newest snapshot in the configured db backup directory; an explicit snapshot path overrides it. board open the live keyboard-driven fleet board. It renders queue holds, every run lifecycle stage, recent merges, spend and health; Enter follows a selected transcript without leaving the board. --json (or a non-interactive stdin/stdout) prints a one-shot JSON snapshot of the same lanes instead. dashboard serve the fleet dashboard in a browser: the UI shell and GET /api/projects, bearer-authenticated with the token minted 0600 at /dashboard-token on first start. Binds 127.0.0.1:8788 by default; a non-loopback --host still starts but prints a warning naming the token file. A separate process — never a route on the daemon port. ledger every conductor-verb call and how the daemon decided it: the verb, the arguments, allow or refuse, the named refusal reason, and the resulting sha. Sessions cannot push, open, merge, label or release except through those verbs, so this is the record of what they tried as well as what they did. --issue narrows it to one issue's run. hold soft stop: pause claiming AND disarm ticks. Daemon and pane stay up. This is "stop the conductor overnight" without killing processes. Use --all to target every configured project. drain start, inspect, or cancel the project's self-expiring admission drain — the durable, bounded alternative to queue-label churn before a release. New claims pause while active runs settle, and admission resumes at the absolute deadline on its own, even if the orchestrator dies. start takes --until (an ISO instant or a relative duration such as 90s/45m/2h/1d, always bounded and in the future) and an optional --reason; status reports the active drain's creation time, expiry, reason and remaining active runs; cancel removes it and is idempotent. The drain never touches the pause sentinel, the arm marker, or any queue label — it is a file record, not a timer or a hold. arm proof-gated: send a Telegram challenge and write the arm marker only after your reply appears as a user turn in the orchestrator transcript. Never auto-armed by resume/hold. Use --all for every project. disarm remove the arm marker so ticks skip. Processes untouched. Use --all to target every configured project. tail follow the newest run for : the worker's assistant text and the tools it calls, printed as they land. Workers are sessions inside the daemon rather than terminals, so this is the only way to watch one live. Runs until Ctrl-C, or until the run has finished and its transcript has stopped growing. extend raise a live run's turn ceiling, or set a bounded one-shot ceiling after a failed, killed, orphaned or blocked run. Refuses values outside configured bounds. worker cooperatively pause one live worker at harness idle, then resume the same session with a continuation prompt. Its wall clock is frozen while parked. Distinct from fleet-level pause/resume. unblock clear 's blocked and failed labels so the next tick can claim it again — the supported way back for an escalation you answered, and why the brief's "never hand-edit a state label" rule can stay absolute. Run history is kept; answered blocks consume the separate operational-continuation budget, not failed implementation attempts. Refuses when the newest attempt's work could not be committed and its worktree is the only copy: re-claiming removes that tree. Use --force once you have recovered it or accepted the loss. Restores the project queue label by default so the issue is actually dispatchable; --no-requeue clears state labels only (the "about to close it" case). verb run one conductor_* verb as the orchestrator, from the CLI: the same checks and the same ledger rows a session's call would get. This is how an external orchestrator merges, labels, releases or reads PR state without a raw gh call that skips every gate. Arguments are strings, one per --arg (e.g. --arg prUrl=https://x --arg headSha=y). A refusal exits 3. See conductor_pr_merge/conductor_label/ conductor_release/conductor_pr_update_branch in the brief. event persist one ordinary material outcome without sending it. Category is a short lowercase slug; summary and evidence name what happened and where it can be verified. --occurred-at defaults to now. report hand a rendered report to the daemon's durable outbox. The report is persisted before anything is sent, delivered by the daemon with bounded retries, and shown by status until it lands. Delivery is at-least-once: a crash mid-send is retried and the retry says it may be a repeat. --kind digest is accepted at most once per local day, decided from the ledger rather than from what you remember sending. A digest associates the comma-separated --events and --notices rows atomically; omitted rows stay owed. The other --kind values (tier2, decision-needed, fleet-stopped, confirmed-failure) declare the report's interrupt category: the reporting policy decides between sending now and holding, exactly as for a daemon escalation. An identical retry is refused only while the earlier handoff is still undelivered, and admitted again once it lands (#453). report withdraw removes only an unsent pending report or unassigned held notice, requires a reason, preserves an audit row, and refuses sending/delivered/failed/already-withdrawn handoffs. message deliver one direct Telegram message to this project's own chat and forum topic, resolved from config rather than from whichever chat last wrote to the session. This is how a locally injected tick answers or asks something directly: telegram_send keeps the active topic only while it names no chat, and a tick has no active topic to keep. A question — text beginning "QUESTION:", or a --category that is not material — records an open decision row before delivery (parked on silence: still pending in every tick until answered or the seven-day expiry, so no one has to remember a separate \`decision open\`), and --category carries the escalation category directly instead of a text prefix. The operator's availability policy still decides between sending now and holding a notice, exactly as it does for an autonomous tick. decision record, list and close the questions you have put to your operator. A question that lives only in a session's context is lost to the next compaction, so \`decision open\` writes it down and every tick's prompt carries what is still owed. \`--resolves-when\` attaches a condition the daemon checks for you (pr-merged:, issue-closed:, npm-version:@); a met one is flagged in that digest. Unanswered rows expire after seven days rather than accumulating. decision open --question TEXT [--blocks TEXT] [--resolves-when COND] decision resolve --answer TEXT decision withdraw [--reason TEXT] decision list watch set a condition or carry note for the orchestrator itself, with no human in the loop — the same no-sleep surface \`decision open --resolves-when\` used to serve, given its own verb so it never renders as a question put to the operator. The running daemon checks a \`--resolves-when\` condition; an armed or ungated heartbeat wakes the orchestrator tick when one is met. With no daemon the durable row is unmonitored. With ticks disarmed the condition may still be marked met, but no tick can be woken until the heartbeat returns. It is listed under its own heading, never under "Open operator decisions", and has no seven-day expiry. \`watch list\` shows open watches; \`watch withdraw \` ends one with a recorded reason — the verb that creates a watch is the verb that ends it (watches are decision rows, so \`decision withdraw \` also works). A watch whose PR condition can no longer be observed — the PR merged or closed before the condition was seen — is withdrawn by the daemon itself with the reason recorded. watch add --note TEXT [--blocks TEXT] [--resolves-when COND] watch list watch withdraw [--reason TEXT] intake keep a raw idea durably before it becomes anything: record it now with \`omp-conductor intake ""\`, list what is still pending, dismiss what turned out to be nothing. Backed by the sqlite store, not a session, so ideas survive restarts; the orchestrator grooms one into an issue as its #300 duty and records that provenance with \`omp-conductor intake groomed --issue \`. friction record a bounded observation the daemon cannot classify itself: an escalation that belonged in a digest, or a tick report that was noise/surprising. Repeated observations feed the existing Learning loop; recording one never edits policy by itself. daemon run the dispatch loop in the foreground; --once runs a single tick and exits. This is what \`start\` launches. resume clear pause and any pane-recovery pin. Does NOT re-arm: run arm after an inbound Telegram proof to bring ticks back. Use --all for every project. setup host [NAME] re-stage the systemd unit and run the install behind one confirm. The units are host-global; NAME (or --project NAME) says which project's per-project tail (tick config, brief link) to write, and is required on a host with several configured projects. On a Herdr host it restarts the session unit, so it must be run from a shell outside that session: from a pane inside it, it refuses before staging or installing anything. setup graph set up the code-graph indexes workers query instead of grepping, end to end: check prerequisites, clone any missing index-only clone as you, install and enable the reindex timer as root, then seed one indexing run and verify it. --no-seed enables without seeding; --print changes nothing. Exits 1 when no repo has graphProject. brief-upgrade inspect the brief overlay (package floor + POLICY.md). Reports by default. --migrate lifts a bannered ORCHESTRATOR.md owned half into POLICY.md and recomposes. --retrofit proposes inserting the YOURS TO EDIT banner before the first Releases/Project context/Reporting/ Amendments heading (#20); --retrofit --apply writes it. --apply only ever confirms one of those two — the legacy single-file merge was removed in 0.4.3. --file checks a brief that is not where the wizard would have put it. help print this text (also --help, -h). --version print the installed omp-conductor package version (also -V, version).`; /** Cross-command lifecycle notes that follow the per-command help. */ export const COMMAND_HELP_TAIL = `Pause is a flag file under the state directory, so it applies to the selected project (or every configured project with --all) and survives a daemon restart. Hold also removes the arm marker the heartbeat reads, so both brains go quiet without killing processes. A running daemon is tracked by a pidfile under $OMP_CONDUCTOR_RUNTIME_DIR (default ~/.omp/run/daemons/omp-conductor), written whether it was started in the background or in the foreground, and probed for liveness on every read — a stale one never blocks a start.`;