---
title: Woken No-Op Sentinel
---

# Woken No-Op Sentinel

## What it is

When a background event re-wakes a session that had already finished — a
`Bash run_in_background` completing, a `--cloud` run, a `Monitor` watch, a
`ScheduleWakeup`, or Omniscio's 30-minute "Please continue." fallback — the agent
sometimes has genuinely nothing left to do. Without this feature it posts a
throwaway "all done, nothing to do" follow-up that clutters the chat and can
re-flag the session as needing you.

Omniscio instructs every agent — Claude and the non-Claude engines alike — to reply, in that
exact situation, with ONE canonical sentence:

> Nothing to do here. Everything was already finished before this background wake-up.

Omniscio deterministically detects that sentence and:

- **hard-hides** the throwaway reply from the transcript, and
- **does not re-ping you** (if the session was already waiting on you, it stays
  exactly there — no fresh chime).

The wake becomes a true no-op.

This works for **every engine, not just Claude.** The non-Claude engines (Codex, Gemini,
OpenCode, slim-ACP, and the rest) are taught the same sentence and get the same hide +
no-re-ping at their own turn-conclusion (via the engine-agnostic sibling — see the contracts
below), so a pointless background wake is a true no-op no matter which engine a session runs on.

## Where to find it

The core feature is **on by default**, invisible, and safe — there is no Settings
toggle for it. Hidden replies are still revealed by the per-session **"Show system
messages"** toggle (which renders the turn whole for auditing). An operator kill
switch exists as the `AMC_DISABLE_WOKEN_NOOP_SENTINEL=1` environment variable
(disables the instruction AND the hide/no-ping; takes effect on restart).

**One opt-in setting — "Prevent mistaken 'nothing to do' replies" (default OFF).**
Sometimes a re-woken session (often a finished automation) mistakes a *short message
you typed* for an automated nudge and wrongly answers it with the no-op sentence.
Turn this setting on (Settings → the session waiting / keep-going area) and Omniscio's
automatic "keep going" nudges self-identify as coming from the app (not you), and
agents are told any message *without* that marker is really from you — so a terse
message like "add this" is no longer taken for a background wake. It ships OFF, and a
mistake that does slip through is already cleaned up automatically (see the misfire
recovery below), so this just makes the mistake rarer.

The setting also covers the `"Please continue"` nudge a **crash or restart revival**
sends (see [Crash recovery](crash-recovery.md)). Until 2026-09-12 it did not: those
revivals hardcoded their text and never read the setting, so turning it on fixed the
keep-going nudges and left every automatic revival still looking exactly like you had
typed it. A nudge you send yourself — the manual **Nudge** button, or a continue over
the command line — stays unmarked either way, because that one really is from you.

## How it behaves

### How it works

1. **Instruct** — a short fragment is folded into the single system-prompt the
   spawned CLI already receives (the same channel as the "you're running inside
   Omniscio" note). It tells the agent the exact sentence to emit when an **automated
   background wake-up** resumes it after all work is finished — including when a
   question it already asked is still unanswered: if the _automated_ wake changed
   nothing it emits the sentinel rather than re-stating the question (re-stating
   would open a fresh turn that buries the decision you still need to make). The
   instruction is explicit that this is for automated wakes ONLY: **anything a
   person actually sends — even a bare key, path, or single word — is real input
   the agent must act on, never a no-op** (the 2026-06-19 fix below). It also tells
   the agent the one sentence **overrides a project or persona instruction to
   "always report what you did / be transparent after every action"** and must be
   sent verbatim — so a verbose automation persona (e.g. a tweet-router) no longer
   rewords it into a status echo that leaks to the inbox (the 2026-08-11 fix).
2. **Detect** — Omniscio matches the agent's reply against the canonical sentence as a
   **whole-message exact match** (tolerating only case / whitespace / dash-style /
   a trailing period). It first drops a leading `▸ Extended thinking` activity tag
   — the marker Omniscio prepends when the agent thinks for a moment before replying —
   so a "thought-first" no-op still matches (that tag is exactly what left most of
   these showing before). It strips ONLY that thinking tag: a message that did real
   work (any substantive tool line survives the strip), or merely quotes the
   sentence, or adds a question, never matches — so a real reply is never hidden by
   accident.
3. **Hide** — only on a genuine background-wake turn (Omniscio's backend knows the
   difference; the agent can't fake it). The throwaway reply is hidden at render.
4. **No re-ping** — if the session was already in "Needs You" before the wake,
   Omniscio silently puts it back there (no sound, no popup; the dot and badge are
   unchanged). If instead the agent had been mid-background-wait and is now
   genuinely done, you DO get pinged once — the honest "it's finished" signal.
   This silent restore ALSO fires when a re-wake left an **already-pending
   decision** (a question / plan / permission you have not answered) UNCHANGED —
   **sentinel or not.** So even a non-sentinel do-nothing wake (the agent ran a
   quick command or wrote a sentence first) can no longer re-chime or bury the
   decision; the pending question stays your live "Needs You" ask. That reply
   stays visible in the thread (only the exact sentinel is hard-hidden), so a wake
   that genuinely surfaced something new is never lost (the 2026-06-17
   "buried question" fix).

### When a real message gets the no-op by mistake (2026-06-19)

The sentinel is for _automated_ wakes only — but a model can misjudge. In the
incident that prompted this fix, a user pasted the API key the agent had been
blocked on, and the agent treated the bare key as a background wake-up and replied
"Nothing to do here." — ignoring it. Two guards now make that safe:

- **It can never be hidden.** The hard-hide + no-re-ping fire ONLY on a genuine
  automated wake (Omniscio's backend marks those; a plain user message — or even a user
  message Omniscio had to re-run for recovery — is not one). So a no-op aimed at your
  message always stays visible; it is never silently swallowed.
- **Omniscio re-sends your message.** If the agent fires the no-op sentinel at a real
  message you sent, Omniscio automatically re-delivers that message once so the agent
  actually responds, and drops a short "that was from you — re-sending it" note. It
  does this at most once per message (so it can never loop), and a legitimate
  automated "Please continue." no-op is never re-sent.

**Update (2026-06-26) — paused sessions too.** The re-send above originally engaged
only when your message reached a session whose process was still alive. A session
that had been **paused** while waiting on you — then resumed by a genuine "Please
continue" — took a different internal path (a fresh respawn) that skipped the guard,
so the agent could answer your "Please continue" with "Nothing to do here." over the
still-open question. That gap is closed: the respawn path now marks your message as
genuine exactly like the live path, so the same backstop re-drives it. (An automated
"Please continue." that crash-recovery sends itself is still correctly treated as
automated and never re-driven.)

**Update (2026-08-15) — the leftover reply now collapses once we've re-sent it.** The
two guards above kept the mistaken "Nothing to do here." fully visible so a genuinely
unanswered message could never be swallowed. But once Omniscio has actually **re-sent**
your message (the common case — the "that was from you — re-sending it" note is proof it
fired), that leftover reply is just superseded noise. It now folds to the same one-line
**"Background check-in"** marker as every other stray no-op — you see the friendly
re-sending note and your real answer, not the confusing "background wake-up" bubble. One
click still expands it, and **"Show system messages"** reveals the raw turn. If Omniscio
could NOT re-send (rare — e.g. it already retried once), the reply stays **fully visible**,
exactly as before, so an unanswered message is never hidden. This is display-only and heals
already-affected sessions the next time you open them. (The live report that prompted it:
"Check again" → the agent no-op'd it → Omniscio re-sent and answered 90 seconds later, but
the bogus bubble had stayed on screen.)

### When a no-op did a quick check first (2026-06-18)

The hide above is deliberately strict: it only hides a reply that is _nothing but_
the sentence. If the agent ran a quick file-check or command **before** saying
"nothing to do", Omniscio will not hard-hide it — that tool might have done real work,
and a hidden message is hard to get back. So that reply stays in the transcript.

But it still shouldn't grab your attention. So Omniscio does two gentler, fully
reversible things instead:

- **It won't scroll you to it.** When you open the session, the view (and the green
  "final message" frame) lands on your last _real_ reply, never on the throwaway —
  this is the case that prompted the fix.
- **It collapses — by default when it ends in the exact sign-off (2026-08-05).** When the
  reply _ends with_ Omniscio's exact "Nothing to do here…" sentence after that quick check
  (the "verify, then sign off" shape — the live row that prompted this), the whole turn
  folds to the same one-line _"Background check-in"_ marker **with no setting to turn on**,
  and even as the newest message — a double-check that changed nothing shouldn't sit between
  you and your real answer. A reply that instead trails off into something _other_ than the
  exact sentence still honors the opt-in "Hide background check-ins" setting (Settings →
  Sessions). Either way, click to expand and read exactly what the agent checked — nothing is
  deleted. This folds **no matter how long the wrap-up narration is** (a 2026-08-06 fix — a
  longer "here's what I verified" write-up used to slip past the fold and render in full); only a
  genuine deliverable — a heading, list, code block, or a question — is ever spared from the fold.

Both are one click away from the full message, which is why Omniscio can do them even
though it won't take the irreversible step of fully hiding the reply.

### When a rate-limit resume's no-op buried the answer (2026-06-30)

The hard-hide (above) fires only on a genuine background-wake turn Omniscio's backend
stamped. But one resume path deliberately does **not** set that stamp: when a
rate-limit clears or the load balancer rebalances a session, Omniscio re-wakes it with
a system-injected "Please continue" that carries **no** `turnBoundary` marker (the
2026-06-26 genuine-user fix keeps the hard-hide off that path so a real "Please
continue" you sent is never swallowed). If that resume lands on a session that had
already posted its real final answer, the agent correctly replies with the exact
sentinel — but with no stamp, the reply was neither hard-hidden nor split into its
own turn. It **merged into the turn that held your real answer** and, because Omniscio
paints a turn's _last_ real reply as the body, the "Nothing to do here…" line
became the visible message and your real answer collapsed into that turn's
"N comments" activity zone. (Reported from a real session whose "~35 states"
answer vanished behind two such no-ops.)

Two builder changes fix it — both display-only, nothing is deleted:

- **Split the no-op into its own turn.** A dropped auto-response now also acts as a
  turn boundary when the run that follows it ends in the exact sentinel (a
  whole-message match, so a real continuation like "Working…" → "Done." never trips
  it). Your real answer stays its turn's visible final reply, and each no-op gets
  its own turn.
- **Collapse that no-op by default.** A turn opened by such a split is flagged
  `isWokenNoOpAck` and folds to the one-line _"Background check-in"_ marker
  **regardless of the "Hide background check-ins" setting** (the exact sentinel is
  unambiguous and a no-op never re-pings, so it is always safe to fold once
  superseded — the same default-fold the canonical wait sentinel gets). One click
  still expands it.

### When the no-op had no boundary at all (2026-07-01)

The 2026-06-30 split above needed something to split _at_: a dropped "Please
continue" auto-response row sitting between your answer and the no-op. But a
background wake can arrive with **no such row and no stamp either** — a session
held **running** on a _"Waiting on a background process…"_ hold that spontaneously
resumes hours later and emits the sentinel. (Reported from a real session whose
2:49pm answer was buried by a 5:18pm "Nothing to do here" — a wake path none of
the earlier fixes had covered.)

Rather than teach the backend to stamp yet another wake path (this bug class had
already been patched four times, once per newly-found wake), the fix moves the
guard to where the symptom actually shows — the transcript builder — and keys it
on the **sentence itself**:

- **Split on the exact sentinel, boundary or not.** When a background reply _is_
  the exact "Nothing to do here…" sentence and it would otherwise bury a real
  answer above it, that reply is given its own turn — the same whole-message match
  as before, so a genuine "Working… → Done." continuation still never splits. Because
  it keys on saved text (not an upstream marker), it covers **every** wake path at
  once, and it **heals sessions that were already broken** the next time you open
  them.
- **Same fold.** The split-off turn is `isWokenNoOpAck` exactly like the 2026-06-30
  case, so it collapses to the one-line _"Background check-in"_ marker; your real
  answer is the visible reply. The per-session **"Show system messages"** toggle
  still shows the raw, un-split turn for auditing.

### When the no-op is REWORDED, not Omniscio's exact sentence (2026-07-01, belt-and-suspenders)

The split above keys on Omniscio's _exact_ instructed sentence. But an agent can improvise
a differently-worded no-op — _"nothing further to do"_, _"nothing more to do"_,
_"nothing left to do"_, _"nothing left to run"_ (the "to run" stem added 2026-08-05,
hidden-final recurrence #14) — through a wake path that also left no marker. That reworded
no-op could still bury your answer. So the same split now _also_ fires on the fuzzy
_"nothing-to-do"_ family, reusing Omniscio's existing, backtested background-ack detector
(no new matcher). Two things keep it safe:

- **A real reply is never moved.** The reused detector refuses to match anything long,
  anything with a question, or anything that's a real deliverable (a heading / list /
  code) — so a genuine answer that merely _mentions_ "nothing to do" is left alone. It
  even ignores _"nothing to do **with** the build"_ and _"nothing to do **but wait**."_
  And the split only ever fires on a no-op that comes **after** a real answer (a direct
  reply to you is the first message of its turn, which is never split).
- **A reword is un-buried but not aggressively hidden.** Unlike the exact sentence
  (which always folds to the one-liner), a _reworded_ no-op is tagged only as a
  _"background check-in"_: your answer stays the visible reply and the view lands on it,
  and the reword collapses to the one-liner only if you've turned on **"Hide background
  check-ins."** A reword is a heuristic match, not Omniscio's own guaranteed sentence, so it
  is treated more conservatively. The irreversible hard-hide stays exact-only.

### When the no-op was a standalone reply to an automated poll (2026-08-05)

Every fix above un-buries a no-op that sits **after** a real answer. But a background
wake can make the "Nothing to do here…" the **only** thing in its turn, with no real
answer above it to protect. The case that surfaced this: a session being driven by
another session (a stress-test "driver") received an automated check-in poll, and the
agent correctly replied with just the sentence. That reply was the first (and only)
message of its turn, so none of the earlier un-buriers touched it — they only fire when
there is an answer above to protect — and the wake was not one Omniscio's backend stamps,
so the full line rendered.

The transcript builder now folds that standalone reply to the same one-line _"Background
check-in"_ marker, keyed on two things together:

- **It IS Omniscio's exact sentence** (a whole-message match) — a real answer, or a
  reworded no-op, is never touched by this path.
- **The turn was opened by something OTHER than you** — an automated poll or a system
  resume, not a message you actually sent. A no-op that answered **your** message stays
  fully visible (and Omniscio re-delivers your message so it gets a real answer), and a
  dev-pipeline phase report is never folded even if its wording happens to match. Only a
  no-op answering an automated, non-you open folds.

As always this is the reversible fold, not the permanent hide — one click expands it, and
the per-session **"Show system messages"** toggle shows the raw turn.

### When a late "auto-landed" note un-folded the no-op (2026-09-02)

Two identical background-wake no-ops, three minutes apart, in the same session: the
first collapsed to the one-line _"Background check-in"_ marker, the second rendered
in full — leaving "Nothing to do here. Everything was already finished before this
background wake-up." as the session's visible last word.

The two replies were byte-identical. The difference was 25 minutes later: the
auto-lander appended a _"Couldn't auto-land …"_ note to the transcript, and a note
that arrives after a turn has closed joins whatever turn is still open — the second
no-op's. Omniscio protects those landed / couldn't-land notes from being swallowed by
a fold, and it did that by **cancelling the fold** — which un-folded the throwaway
reply along with it.

The fix mirrors what the hard-hide already does: **keep the fold, and paint the note
beside the marker.** So a no-op that a land note lands in now shows one quiet
"Background check-in" line plus the green "Auto-landed …" (or amber "Couldn't
auto-land …") row — both visible, neither burying the other. A genuine status
check-in is unchanged: it still stays fully open next to the note, because it carries
a real reason you were pinged.

### When a crash killed the turn first (2026-09-13)

Every un-burier above protects a **settled answer** sitting above the no-op — that is
what tells Omniscio the reply belongs to a background wake rather than to you. When the app
**crashes mid-turn** there is no settled answer to find: the work row is left unfinished,
holding only tool activity and no closing sentence. So when crash-recovery resumed the
session and the agent correctly answered with the one canonical sentence, nothing split it
off — it merged into the turn **you** had opened, and with no wake signal left to read,
the throwaway rendered in full as the last word under your own message. (Reported from a
live session whose 11:04 go-ahead ended on a 12:08 “Nothing to do here”.)

A crash-recovery resume is now enough on its own: when the reply to one **is** the exact
sentence, it gets its own turn and folds to the usual one-line *“Background check-in”*
marker, with no setting to turn on. Your message and all the work above it are untouched and
stay fully visible; one click still expands the fold, and **“Show system messages”**
still reveals the raw turn. Display-only, and it heals already-affected sessions the next
time you open them.

Two limits keep it safe:

- **If the agent had not started work yet, nothing folds.** No work row above the resume
  means your message was dropped on the floor unanswered — that is precisely what you
  need to see, so the reply keeps rendering in full.
- **Only the exact sentence.** A reworded no-op still needs a settled answer above it
  before anything is folded, exactly as before.

### Relationship to "Hide background check-ins" (background-ack)

These are siblings, not duplicates:

- **Background-ack** ("Hide background check-ins", Settings → Sessions, off by
  default) FUZZILY matches status/progress reports ("Tests green (exit 0). Build
  still running.") and **collapses** them to a one-line expandable marker.
- **Woken no-op** (this page) matches ONE deterministic instructed sentence
  meaning "literally nothing to do" and **hard-hides** it (default on), with its
  own no-re-ping.

The deterministic sentence buys the precision needed to hide + suppress the ping
by default; the fuzzy detector stays opt-in. Background-ack additionally carries a
`nothing-to-do` rule family as an opt-in **backup** — it catches natural rewordings
("nothing further to do") and, unlike this default-on hider, reaches **non-Claude
(Codex) sessions**, which are never taught Omniscio's exact sentence.

## For agents

### Key files

| File                                               | Role                                                                                                                                                                                                        |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/shared/woken-noop-sentinel/detect.ts`         | Canonical phrase + metadata key + `detectWokenNoOpSentinel`                                                                                                                                                 |
| `src/main/process/spawn-build.ts`                  | Instruction constant + `resolveWokenNoOpSentinelPrompt`                                                                                                                                                     |
| `src/main/services/woken-noop-sentinel-flag.ts`    | `AMC_DISABLE_WOKEN_NOOP_SENTINEL` kill switch                                                                                                                                                               |
| `src/main/process/streaming-finalizer.ts`          | Finalize: HIDE stamp (snapshot-gated) + `turnSentinelMisfire` latch                                                                                                                                         |
| `src/main/process/user-message-router.ts`          | `turnWasGenuineUserMessage` arming for BOTH delivery paths (shared `markTurnGenuineUserMessage`; live `writeToStdin` + auto-resume `noteResumedTurnGenuineUserMessage`) + per-message re-drive budget reset |
| `src/main/services/session/session-service.ts`     | Auto-resume relaunch arms the genuine-user flag after `launch()` (`noteResumedTurnGenuineUserMessage`, both `--resume` + context-transfer branches)                                                         |
| `src/main/process/process-manager.ts`              | `restoreStatusSilently` + `noteResumedTurnGenuineUserMessage` delegator                                                                                                                                     |
| `src/main/process/ndjson-event-handlers.ts`        | `armTurnBoundary` (pre-wake snapshot) + `handleTurnComplete` gate dispatch                                                                                                                                  |
| `src/main/process/ndjson-auto-disposition.ts`      | `tryWokenNoOpSilentFinish` (no-re-ping) + `tryWokenNoOpMisfireRecover` (re-drive a real message)                                                                                                            |
| `src/shared/real-conversation-turns.ts`            | `isWokenNoOp` turn flag (reads the stamp) + the buried-answer split: `shouldSplitAtDroppedAutoResponse` (boundary-row case) and `shouldSplitBeforeSpontaneousWokenNoOp` (no-boundary spontaneous case)      |
| `src/renderer/src/features/sessions/TurnGroup.tsx` | Render-time hard-hide                                                                                                                                                                                       |

Contracts (invariants + tests): `.claude/memory/contracts/woken-noop-sentinel-contract.md` (Claude) · `.claude/memory/contracts/external-woken-noop-contract.md` (the non-Claude engines).

## Related

The sentinel is one of several guards that decide what a finished turn looks like on screen, so it is closest to [Aborted-response recovery](aborted-response-recovery.md) and [Agent message display](agent-message-display.md). The revive-and-continue path whose nudge it also governs is documented on [Crash recovery](crash-recovery.md).
