Monitors¶
This page names how a monitor turn starts, runs a supervised command, and reports the outcome. A monitor turn is a real agent-session member whose work is one OS command instead of an LLM turn.
The agent-session picture¶
Starting a monitor promotes the calling sase-agent to an agent session, exactly as
%id(suffix, session=parent) would, and adds the monitor as a named proc member. No
successor is launched yet. After the command settles, the frozen outcome branch may
launch one ordinary follow-up, complete through the host, or do nothing. stopped and
lost always do nothing:
sase-agent "acme" before right after `sase monitor start`
────────────────────────────────────────────────────────────────────────────────
acme (one-turn agent, RUNNING) acme (agent session)
├─ acme--0 DONE ← starter turn, killed
└─ acme--mon TESTING ← monitor named proc
After the command finishes on an ordinary continue branch:
acme
├─ acme--0 DONE
├─ acme--mon TESTED ← monitor finished
└─ acme--1 RUNNING ← follow-up turn
| Term | Meaning |
|---|---|
| starter turn | The agent turn that ran sase monitor start (absent for host-started monitors) |
| monitor turn | The --mon named proc representing the supervised command |
| supervisor | The detached process that runs the command and streams its output |
| follow-up | The agent turn launched under the session after the command finishes |
sase monitor start, run from inside an agent, is the last thing that agent does: it
kills the calling agent's turn (the same handoff mechanism sase plan propose and
sase questions use), while the supervisor keeps the command running under the same
workspace claim. The workspace is never released and re-claimed between the starter and
the follow-up — the follow-up sees exactly the tree the monitor started with, plus
whatever the command itself changed.
A monitor turn has an ordinary artifacts directory, just like an agent turn, so
everything that already understands agent sessions — the Agents tab, session roster,
runtime aggregation, sase chat, %wait/#fork resolution — works on it with no
special casing. There is no separate monitor store: a monitor's durable record is its
agent_meta.json plus done.json, and sase monitor list/show are queries over the
existing agent artifact index.
Starting a monitor¶
Host finalizer recovery and conflict-repair turns cannot hand off through
sase monitor start, sase pipe, plan proposal, or questions. Those turns are owned by
an already-running finalizer that must receive their result in the same invocation; a
handoff cannot complete that contract. The CLI refuses before creating the handoff.
Finish the repair and any verification inline, or report the blocker to the host.
sase monitor start \
-p verify \
-r 'Verify the refactor before replying to the user' \
-t 45m \
-n 'Fix anything just check reported, then reply to the user.' \
-- just check
When just check-full is explicitly requested (typically to repair a CI failure), use
the same verify profile — never run it inline.
Use the same shape when the command you need is an agent-completion gate. The intended
composition is a monitor running sase agent wait, followed by a normal follow-up agent
that acts on the result:
sase monitor start \
-s WAITING -S WAITED \
-n 'agents finished; land the epic' \
-- sase agent wait -a
That keeps the wait outside the current provider turn, preserves the workspace claim, and records the wait output where the follow-up can inspect it.
- The command is the remainder after
--(for example-- just check). That is the formsase monitor start --helpshows.-c/--commandstill works as a hidden compatibility alias for a single shell string, but new invocations should use--. A single word after--is used verbatim as the shell command, so quote a pipeline or&&chain as one argument (-- 'just fix && just check'). Several words are re-quoted withshlex.join, so each stays one literal argument and shell operators among them are not interpreted. SASE already runs the command with/bin/sh -c, so do not wrap it inbash -corsh -c;sase monitor startwarns when it sees that redundant wrapper. -p/--profile verifysupplies the standard verification policy:TESTING/TESTEDlabels,--next-output auto, no continuation after success, and a recovery continuation after failure or timeout.-n/--nextreplaces the built-in recovery instruction. Profile selection alone never authorizes host completion; prepared completion still requires-f/--completion.-s/--start-statusand-S/--stop-statusare required when no profile supplies them — the present-tense label shown while the command runs (e.g.TESTING) and the past-tense label shown when it finishes (e.g.TESTED). Each is capped at 20 characters; over-length values are truncated with a trailing…and a warning.TESTING/TESTEDis the pair forjust checkandjust check-full; a different kind of wait picks its own pair (for exampleSLEEPING FOR 300s→SLEPT FOR 300s).-r/--reasondefaults torun command.-t/--timeoutdefaults to1h(bare seconds, or90s/45m/2h). Pass both when the default reason or budget would be misleading.--idle-timeout/-iis optional. It kills a command that stops producing output for the given duration, while still allowing intentionally quiet commands when omitted.--next/-nis the shared follow-up instruction. Without a profile, policy, or prepared completion, omitting it makes the monitor fire-and-forget: the command still runs to completion and its output, exit state, and runtime are recorded, but no agent launches afterward.--model/-mselects a model or alias for an ordinary follow-up (for exampleopus,opus@high,@small, orcodex/gpt-5). It may accompany--next, a profile, an outcome policy, or a prepared completion that has a recovery branch. When omitted, an ordinary follow-up inherits the starter's model and reasoning effort.-o/--next-output auto|tail|file|nonecontrols how much retained command output is handed to the follow-up agent.autois the default and selects outcome-aware evidence;tailembeds a bounded raw tail;filepoints at refs and log locators;nonegives only the outcome summary andsase monitor show --all-linespointer.-k/--checkpoint FILEbinds an authored YAML/JSON checkpoint by content digest. Use it to preserve the objective, constraints, findings, unresolved decisions, remaining work, source refs, or coverage separately from--next.-P/--policy FILEloads a per-outcome JSON/YAML policy. It is mutually exclusive with-p/--profile; both are validated and frozen before SASE creates the monitor member.-f/--completion REFbinds a single-use host-completion intent produced bysase final prepare. The monitored argv must exactly match the intent's verification command.--label/-L,--agent/-a(--laneremains accepted as an alias),--cwd/-C, and--tail-lines/-Tare optional; see the generatedsase monitor startpage for the full flag list.-k,-P,-p, and-fare always available: every new monitor persists versioned continuation records, so checkpoint, outcome-policy, profile, and host-completion controls work unconditionally.
--cwd may name any directory inside a managed checkout, including a repository opened
with sase repo open. When --cwd is omitted, the default is the current directory
when it is inside a repo opened with sase repo open, else the agent's workspace, else
$PWD. The command runs in that directory, but the monitor records and claims the
checkout that contains it, and an ordinary follow-up starts at that checkout's root.
When the directory is inside the caller's own workspace, the caller's claim moves to the
monitor; inside a different managed workspace, the monitor takes a fresh claim and the
caller keeps its own. A directory outside every managed checkout uses workspace 0.
Before it creates the monitor member or binds a completion intent, sase monitor start
checks the target workspace's claims. If another live process holds that workspace, the
start fails with could not claim workspace for monitor: … and leaves no monitor row
behind. A claim left by the same agent whose process has died is taken over instead.
Only one monitor may be running per agent at a time. Repeating the same full request
returns the existing running record; changing the command, cwd, reason, label, timeout,
idle timeout, next action, model, status labels, tail lines, profile, checkpoint,
policy, completion intent, output policy, or joined run is rejected until the active
monitor settles. A lost monitor is never implicitly replayed: repeating the identical
request is refused with a pointer to sase monitor show, while a different request may
start a new monitor.
Joining a detached ToolRun¶
(Agents only.) sase monitor start -J/--join RUN adopts an existing starter-scoped
detached ToolRun — the kind sase tool run -d starts — instead of starting a command.
The run keeps one id end to end, and its executing proc stays the owner; the monitor
only follows it:
sase monitor start -J 0f1a2b3c -p verify -n 'finish check'
The display command replays the original sase tool run words (ad-hoc argv included),
the default label is tool:<name> (joined), and the default reason is
finish <tool> (joined run). -J cannot be combined with a command remainder, -c,
-f (pass -n for a follow-up instead), or -a: the join always runs on the calling
agent's lane. The caller must be the agent that started the run; anything else — outside
an agent, a run with no starter scope, or another agent's run — is refused with exit
2. A run that already settled, has a stop request, or is joined elsewhere is refused
with exit 1, showing the state and a sase tool show RUN pointer. No monitor starts
on any refusal.
The join records atomically after the monitor member exists and before its proc submits;
a refused or failed start tears the member down (releasing the join) and runs nothing.
The monitor proc follows the run into the monitor log without a deadline, then renders a
compact summary with the triage footer and exits with the run's mapped code. In the
Procs tab that proc carries a tool-run-join:<id> tag, so Enter opens Admin Center →
Tools focused on the run (⏎: run) instead of jumping to the monitor's agent. A monitor
that owns a reserved run uses tool-run:<id> and opens the same way. See
Opening a row. stop, timeout, and a lost joiner
converge through the detached-run watchdog with the stop_requested reason preserved,
and sase tool stop RUN on a joined run stops the active joining monitor (suppressing
its follow-up) rather than the run's proc.
Tool-run wrapping¶
A verify-profile monitor runs its command inside sase tool run, so the run is
recorded with the monitor as owner and the starter agent attributed — plain
-- just check is equivalent to -- sase tool run check and agents need not remember
the wrapper. Only the argv the proc supervisor execs is wrapped: monitor_command and
monitor_execution_argv stay exactly as written, so every prepared-completion -f
binding keeps resolving the raw command. The wrapper is built from the supervisor's own
Python (sys.executable -m sase), never PATH. The starter's provider rides along as
SASE_TOOL_RUN_PROVIDER for the run's demand context; ceilings are never forwarded,
because a monitor has none.
Where the table below resolves to a tool run, the start reserves that run up front — a
created hand-off run owned by the monitor id — and the proc execs the claiming worker
instead of the wrapped argv, so one semantic run is never recorded twice. The run id
prints in the start output (Tool run), rides the --json envelope (tool_run_id),
persists as the flat monitor_tool_run_id meta field, and shows in sase monitor show,
so the follow-up can sase tool show RUN -F, sase tool stop RUN, or
sase tool wait RUN on it. If the reservation cannot be committed, the start keeps the
wrapped argv (fail-open) and pre-writes one sase: tool run not reserved (...) reason
line to the monitor log.
| Monitor | Proc argv |
|---|---|
Host-owned execution_argv launch (an epic sase bead work) |
unchanged, never wrapped |
Exactly one sase tool run … already |
unchanged |
SASE_TOOL_BYPASS set or monitor.tool_wrap: off |
unchanged, plus one reason line in the log |
No profile or a non-verify profile under the default monitor.tool_wrap: verify |
unchanged, plus one reason line in the log |
| The project tool catalog cannot be loaded | unchanged, plus one reason line in the log |
| A simple command equal to a catalog tool's argv, with the monitor's cwd at that catalog's project root | <sase> tool run <name> (named upgrade) |
Anything else under -p verify (or with any or no profile under monitor.tool_wrap: all) |
<sase> tool run -- /bin/sh -c CMD (ad-hoc) |
Rows are checked top to bottom and the first match wins. A simple command is one shell
word-split with no operators, redirects, globs, expansions, or leading NAME=value
assignment: just check upgrades by name, while just install-venv && just check,
just check 2>&1 | tail -20, or FOO=1 just check wraps ad-hoc with the command
verbatim, so the run is at least as faithful as the raw command. Extra arguments match
only where that tool's args: allow policy permits them. Every unwrapped-by-policy case
writes exactly one sase: running unwrapped (<reason>) line at the top of the monitor
log, so the log explains itself. monitor.tool_wrap (off | verify | all, default
verify) changes the policy without a code change: all widens wrapping to every
monitor and off disables it; see Tool runs for the environment contract
and the guarded recipes this pairs with.
Checkpoints and outcome policies¶
An authored checkpoint is a bounded (at most 256 KiB), UTF-8 YAML or JSON object. It
must provide at least one of objective, constraints, findings,
unresolved_decisions, or remaining_work; each accepts a string or a list of strings.
Optional source_refs and coverage identify the evidence and scope already handled.
Do not put next or next_action in the file: the checkpoint records durable state,
while -n/--next records what the successor should do.
objective: Finish the parser refactor safely.
constraints:
- Preserve the public CLI.
findings:
- The focused tests pass.
remaining_work:
- Run the full verification gate.
source_refs:
- file:explicit:parser-notes
coverage:
- parser
- cli
An explicit outcome policy chooses continue, none, or complete independently for
completed, failed, and timeout. It may also spell out stopped and lost, but
those two outcomes never dispatch. A continue branch can set next_action, model,
and evidence policy; an explicit branch overrides the shared --next, --model, and
--next-output values. A complete branch is rejected unless --completion binds a
prepared intent.
completed:
action: none
failed:
action: continue
next_action: Repair the failed verification, then finish the task.
model: opus@high
timeout:
action: continue
next_action: Diagnose the timeout without rerunning the command blindly.
SASE freezes the validated policy, inherited route, prepared-completion ref, and checkpoint before changing workspace claims. Editing the source files afterward cannot change a running monitor's settlement decision.
Prepared host completion¶
sase final prepare turns a normal finalizer declaration plus one exact verification
command into a host-sealed, single-use intent. Preparation publishes the current final
context and records the relevant repositories' HEAD, index, dirty paths, and protected
or foreign state. It does not accept a final submission, run a finalizer, commit, or
end the turn.
Prepare retains observations for every relevant repository as evidence, but eligibility and the worktree-staleness fingerprint use only repositories with a repository decision. Those are the repositories host completion can commit; unrelated sidecars may have pre-existing dirt or receive concurrent commits without making a safe completion stale.
{
"success_message": "Required checks passed in {duration}.",
"verification": { "command": ["just", "check-full"] },
"declaration": {
"schema_version": 2,
"context_digest": "<from sase final context>",
"plan_digest": "<from sase final context>",
"payloads": [
{
"instance_id": "commit",
"payload": {
"repositories": [
{
"repo_id": "<repository obligation id>",
"action": "commit",
"message": "docs: refresh user documentation"
}
],
"deferrals": []
}
}
]
}
}
Prepare the wrapper, then bind the returned intent_ref to the matching command:
sase final prepare completion.json -j
sase monitor start -p verify -f '<intent-ref>' -- just check
Binding is atomic. A command mismatch creates no monitor, and a failed monitor startup
returns the intent to the prepared state; once a live monitor has bound it, the intent
cannot be reused. If the command completes successfully and the sealed command,
finalizer plan, repository observations, workspace, and diagnostics remain eligible, the
host installs the prepared declaration and runs its finalizers without another model
turn. The monitor reports Completed by host and retains a completion receipt.
A failed or timed-out command, stale repository state, or another eligibility failure
invalidates host completion and launches one ordinary recovery continuation instead.
That successor receives the reason and must repair or finish normally; without --next,
it is told to diagnose the failure or stale verification and then finish the requested
change. Stopped and lost monitors never complete or continue automatically.
The monitor's host-completion status (Host final in sase's TUI) reads finalizing
while the finalizers run, completed_by_host once they succeed, and recovery after
the intent was invalidated and handed to a recovery continuation. needs_attention
means a commit receipt is ambiguous, so SASE neither completes nor continues
automatically; inspect the receipt and finish the change by hand. A monitor that carries
a host-completion status cannot be resumed with sase monitor resume. See
Commit Finalizer
for the declaration side of the protocol.
Resolving the implicit agent¶
--agent / -a is only needed to start a monitor outside an agent turn (no
SASE_AGENT_NAME set) or to target a different agent than the caller. From inside an
agent -- including an epic phase lane and a promoted agent session -- omitting it
resolves the calling agent turn metadata-first:
- The caller's own artifacts dir (
SASE_ARTIFACTS_DIR), when it belongs to the caller. - An exact
SASE_AGENT_NAMEmatch against an artifact's own name. - The newest non-monitor member of the caller's own session, when
SASE_AGENT_NAMEnames a session container rather than a concrete turn -- session members can replace one another inside a single process, leavingSASE_AGENT_NAMEset to the session while the running turn's own artifacts carry the concrete member name. A settled--monmember is never selected here, even when it is the newest member of the session.
An unresolvable caller (no artifacts match any of the above) is a clear error naming
-a/--agent, not a silent fallback to the current working directory or another agent's
lane. sase monitor show/stop with no id resolve the same way, against the caller's
own durable session -- never a parent's or sibling's.
The stored command string is executed with /bin/sh -c, so shell quoting, redirection,
and variable expansion in a single-string command are the caller's responsibility.
Monitors are for batch commands: do not use them for interactive programs or commands
that require a TTY.
Supervision guarantees¶
The detached supervisor owns the command's process group and writes combined stdout and stderr to a bounded rotating log. Completion is based on process exit, not pipe EOF, so a backgrounded grandchild that holds stdout open cannot keep the monitor running after the command process exits. Total timeouts and TERM-to-KILL escalation are checked on every supervisor tick, independent of whether the command is quiet, chatty, writing partial lines, or emitting non-UTF-8 bytes. Non-UTF-8 output is retained with replacement characters rather than crashing the supervisor.
Monitor logs are bounded. The active log is live_reply.md; when it rotates, readers
stitch the rotated live_reply.md.1 and active file where appropriate. Very large
output therefore keeps a recent on-disk view and a head-plus-tail retained summary for
the follow-up agent rather than preserving unlimited bytes.
The command does not inherit the starter agent's SASE_AGENT* identity or
SASE_ARTIFACTS_DIR, so tools run by the command cannot accidentally write artifacts or
variables into the dead starter's directory. It receives SASE_MONITOR_ID,
SASE_MONITOR_ARTIFACTS_DIR, and SASE_MONITOR_DIAGNOSTICS_DIR instead. Only the
command's own tools/run_silent stages record into that diagnostics directory: a
stage's children never inherit it, so a nested tools/run_silent cannot add stages of
its own.
Surviving the starter's teardown¶
sase monitor start, run from inside an agent, hands the command to a supervisor and
then kills the calling agent's runner group as part of the same handoff. The supervisor
must not be a casualty of that kill: it is spawned through a double-fork bootstrap that
reparents it to PID 1 before start_monitor returns, so a process-tree teardown
launched after the call cannot reach it by walking PPIDs. The supervisor also sets
SIGHUP to ignored and installs its SIGTERM/SIGINT handling in the first statements
it runs, before any expensive import, closing the startup window in which a stray signal
could kill it silently.
On Linux, when the starter runs inside a SASE-owned systemd unit or scope such as
sase.service or under a reachable user systemd manager, the supervisor also moves into
its own transient systemd-run --user --scope (with OOMPolicy=continue), so
restarting that service does not kill a running monitor. Set
SASE_DETACH_SCOPE_DISABLE=1 to opt out.
A start is fast, and never silent¶
An in-agent sase monitor start has to finish inside the harness's own tool budget:
Codex's yielding exec_command hands control back to the model after about 30 seconds
while the command keeps running, and anything still running when the turn ends is
killed. So the start touches only what it needs and says so at once:
- Inside an agent it prints one line to stderr before any slow work
(
sase monitor start: starting monitor for lane <lane>; ... wait for it to exit), so a yielded result is never empty. All stdout, including the--jsonenvelope, still prints before the runner is killed. - The calling agent is resolved by reading its own
SASE_ARTIFACTS_DIRdirectly. The full project scan is only the fallback for a missing, stale, or foreign pin. - The lane's existing monitors are read once, through the artifact index's
agent_sessioncandidate filter, and that snapshot answers the replay check and the--monsuffix allocation. A lane read does not hydrate other lanes' records or walk the project's source directories. - Each phase of the start (identity, lane lock, replay lookup, lane resolution, claim
preflight, member creation, ToolRun reservation, supervisor spawn and ack, intent
persistence) is timed. The timings are logged at debug level and written to
monitor_start_timing.jsonin the new member's artifacts directory.
On a host with about 12,000 artifact records this took an in-agent start from roughly 20-40 seconds to well under one.
Startup acknowledgement¶
The monitored command runs under the durable proc service's detached supervisor, and
start_monitor never hands back a running record for a supervisor that is not
provably alive, because its caller's very next act — inside an agent — is to kill
itself. Once the supervisor has taken ownership, it writes a .proc_started
acknowledgement in its proc runtime directory. start_monitor waits for that marker for
up to 20 seconds (SASE_PROC_START_ACK_TIMEOUT_SECONDS overrides the budget), polling
the supervisor's liveness too so a supervisor that is already dead fails fast instead of
waiting out the full budget. A failed start hands the workspace claim back to the
still-live starter exactly as it held it (never releasing it into the free pool),
returns any bound completion intent to the prepared state, tears the member down as
terminal failed, and raises MonitorError — so the starter agent stays alive,
sase monitor start exits non-zero, and nothing downstream ever hands off to a phantom.
A monitor owns its workspace until it is reconciled¶
A running monitor's workspace claim is not released just because its supervisor's pid looks dead. The stale-claim sweeper reconciles a monitor's own markers first (killing a confirmed-dead process group, finalizing the log, disposing the claim, and running or recording the follow-up disposition) and only then releases the claim — and if reconciliation itself fails, the sweeper leaves that claim in place rather than guessing. This closes the window where another agent could be handed a workspace a monitor is still using: a not-yet-reconciled dead supervisor is exactly the state a live monitor is in from the outside, so a bare dead-pid check is not sufficient evidence to release.
Status and bucket¶
The displayed status is always the configured label. Because those labels are arbitrary
strings, the underlying bucket (Running / Done / Failed, used for grouping and
counting) is tracked separately from the label, from monitor_state:
monitor_state |
Bucket | Displayed status |
|---|---|---|
running |
Running | start status |
completed |
Done | stop status |
failed |
Failed | stop status (+ exit code or supervisor error) |
timeout |
Failed | stop status (+ total-timeout or idle-timeout note) |
stopped |
Done | stop status |
lost |
Failed | stop status; command outcome is unknown |
The stop-status label is descriptive text, not a success condition. It is reused for
every terminal state, including an explicit stop; use the bucket, monitor_state, exit
code, and output to decide what happened.
Display contract¶
A monitor's identity on every surface is the ordered pair of its two labels. Three orthogonal signals carry the rest:
| signal | carries | mechanism |
|---|---|---|
| hue | which kind of monitor this is | one deterministic accent color per pair |
| weight | live or settled | bold while running, normal weight once settled |
| glyph | how it went | ✓ completed, ⊘ stopped, ✗ failed, ⧖ timeout, ⚠ lost |
Failure keeps red: failed, timeout, and lost render bold red regardless of the
pair accent. Two different pairs can share a color; the words still differ. Reusing one
pair across related monitors (for example every just check wait as TESTING /
TESTED) makes those rows read as one lane.
A monitor is terminal only after it is settled: the command has exited or been
reconciled, the log has been finalized, the workspace claim has been released or
transferred, and the follow-up has launched or its disposition has been recorded.
Polling commands such as sase monitor show --follow and %wait continue waiting while
a monitor is stopped but not yet settled.
A failing just check shows up as a Failed member with its exit code visible, and the
follow-up agent still launches so it can fix what broke. A timed-out command is killed
(its whole process group, not just the shell), and the follow-up is told plainly which
budget fired: total runtime or no-output idle time. A lost monitor means the command
outcome is unknown: the supervisor died or belongs to a previous boot without a frozen
command result. Lost monitors are not automatically re-run, and their recorded follow-up
action is not launched. A known command result that survived a later bookkeeping crash
is not lost: settlement resumes the frozen status, exit code, reason, and command-end
time, finishes remaining artifacts and follow-up work, and does not rerun the command.
sase monitor resume still rejects lost; this repair does not scan or rewrite
historical monitors that were already terminalized as lost.
The follow-up agent¶
When the frozen outcome branch selects continue, one follow-up agent turn launches
under the same agent session once the command finishes and the monitor settles. A shared
--next, the verify profile, or an explicit policy may supply that branch. It
receives:
- the starter's full prior conversation, via
#fork, once the starter's own record has settled (settlement waits up to 60 seconds for it); the follow-up joins the session it forks and does not wait on or list itself, though it still waits for any other live session member; - the original
--reasonand the resolved next instruction, verbatim, under its own heading; - the authored checkpoint, when supplied, as protected state distinct from the next action;
- a command-run breakdown: outcome, exit code, elapsed time vs. the timeout budget, and
the selected output policy from
--next-output; - the full log path and the exact
sase monitor show <id> --all-linesinvocation to read more than the tail.
By default, the follow-up inherits the starter's model and reasoning effort. Pass
--model / -m to replace that routing with a model, provider-qualified model, or
model alias; an optional @effort suffix travels with the selection. %model text in
--next remains literal prompt text and does not control routing.
Failure triage in follow-ups¶
When a failed monitor has a settled ToolRun with triage, its follow-up prompt inserts a
## Failure triage section before selected diagnostics. It gives the verdict, NEW and
UNKNOWN items with their evidence and possible owners, KNOWN/FLAKY counts, and the exact
sase tool show RUN -j command. The section is absent only when no stored triage is
available; it never changes the monitor result or the command's exit code.
Continuation history is replayed from versioned parent links rather than reconstructed from turn names. Each ancestor is hydrated once in order, including local authored prompt segments, host instructions, final responses, checkpoints, and monitor-result evidence. A missing ancestor is disclosed as a gap; SASE does not guess across uncertain history. Artifact-run pruning keeps old runs that a live or recoverable continuation still references, and SASE registers required checkpoints and monitor results as portable artifacts, so replay can still hydrate them after the local files are gone.
Automatic retries keep failed-attempt evidence under attempts/<N>/, but continuation
replay excludes superseded failed attempts from the ancestor chain. A later #fork
therefore follows the settled attempt and its original launch lineage without injecting
obsolete retry errors as extra conversation turns. Prior attempts remain inspectable in
the TUI's attempt view.
Before invoking the provider, SASE measures the fully expanded continuation against its
context and transport budget. If essential content is too large, the provider is not
called: the follow-up becomes not-launchable, the budget decision and composed prompt
remain inspectable, and recovery guidance asks for an adequate checkpoint or a route
with more context. The monitored command is not rerun merely to reconstruct context.
A follow-up that cannot launch keeps its recovery evidence. Before SASE releases the
monitor's workspace, it saves a best-effort git diff HEAD plus untracked files as
diagnostics/worktree_recovery.diff (recorded as monitor_worktree_recovery_diff_path)
and links it from the saved prompt. An agent waiting on that monitor gets a
terminally blocked wait notification that names
the sase monitor resume <id> command and the saved diff. If the follow-up was blocked
only because the starter had not settled yet, a later sase monitor resume repairs the
missing parent link and launches normally.
The launch is not coupled to a workspace-claim handoff that can fail: if the monitor's
own workspace claim can no longer be transferred to the follow-up (for example, a stale
sweep already released it), the follow-up still launches — first against a fresh claim
on the same workspace, then against workspace 0 if that workspace has since been taken
by another agent. Either fallback is recorded as a degraded launch, and the
follow-up prompt says plainly which happened, because a follow-up in a different
workspace than the monitor ran in cannot assume the command's artifacts are present.
Only when a follow-up genuinely cannot be launched at all is it dropped — and even then
the composed prompt is persisted as a durable artifact so the instruction can be
replayed by hand instead of surviving only as an error string. See
Visibility below for how a dropped or degraded
follow-up is surfaced.
The follow-up prompt's body is enclosed in a macro-disabled region, so directives,
#macro references, and $(...) command substitution inside --reason, --next,
table fields, diagnostics, and embedded output are delivered as literal text. Only the
routing prefix remains live: #fork:, %model:, %effort:, a %queue(...) line that
carries the monitor's recorded queue weight (plus any recorded priority or capacity),
and the starter's VCS workspace reference. Successors inherit autonomy structurally from
the starter member's live record; an explicit %auto in agent-authored follow-up text
can only narrow, and a widening request keeps the inherited record. When
--next-output tail is used, retained output is also fenced and labeled as untrusted
program output. The command and cwd fields are fenced too, so directive-shaped strings
inside a shell command or path remain literal even if the disabled region is ever
removed. --next-output auto defaults completed runs to facts and refs, failed runs to
bounded selected diagnostics when available, and timeouts to a bounded raw tail. Use
--next-output file for large or hostile logs when the follow-up should inspect the log
explicitly, or --next-output none when the outcome summary and
sase monitor show --all-lines pointer are enough. The continuation evidence limits
live under monitor.evidence_limits in sase.yml; the shipped defaults are 8 KiB
selected diagnostics, 4 KiB fallback tail, 12 KiB total raw excerpt budget, and 200
raw-tail lines.
Runner slots¶
A monitor is not a way to free runner capacity. The session keeps its one weighted claim
against max_running_agents for the
monitor's whole lifetime, and the monitor inherits the starter's queue weight. The
starter's runner process exits at handoff, but occupancy stays continuous: the monitor
member counts as soon as it has a recorded supervisor pid. An ordinary follow-up carries
that weight forward in its %queue(...) prefix and, as a serial session member,
continues the session's claim. A fire-and-forget monitor (an outcome policy with no
continuation) still holds the claim until the command settles. In-process successors
such as sase pipe keep the same session's claim as well; they never become a second
occupant.
The host-owned monitor that launches an approved epic is the exception: it records an
explicit zero queue weight and consumes no capacity, because the phase agents it
launches claim their own. That host-set zero applies to the monitor member only:
successors inherit the starter's weight (or the default 1.0), never the monitor's
zero. Only a user-authored %q(w=0) propagates zero weight to successors.
Holding a claim and waiting for one stay separate. Only a root or a live parallel clan member parks at the gate. Serial session members — the monitor and any ordinary follow-up included — ride the claim the session already holds. See Agent queued for a runner slot.
Related¶
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.