Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .omp/extensions/fm-primary-omp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ export default function (omp: ExtensionAPI) {
omp.on("turn_start", () => {
taskInboxDoorbell.notifyTurnStart();
publishTaskTurnStarted();
// An idle-main wake injection opens an agent-initiated turn that emits no
// before_agent_start; this turn boundary is its consumption evidence.
watch.turnStart();
// A turn can begin while no arm child is live - after an actionable close
// whose successor restore failed, an exhausted continuity retry, or a lock
// reclaimed without a follow-up arm - and only the turn-end guard would
Expand Down Expand Up @@ -410,8 +413,20 @@ export default function (omp: ExtensionAPI) {
});

omp.on("message_start", (event) => {
if (event.message.role !== "user") return;
watch.acknowledgeWake(userMessageText(event.message.content));
const message = event.message;
if (message.role === "user") {
watch.acknowledgeWake(userMessageText(message.content));
return;
}
// An idle-main injection is delivered as this custom message inside an
// agent-initiated turn that emits no before_agent_start, so its own
// message_start is the consumption evidence when the text matches a wake.
if (
message.role === "custom" &&
(message as { customType?: unknown }).customType === "firstmate-watcher-wake"
) {
watch.acknowledgeWake(userMessageText(message.content));
}
});

omp.on("session_stop", async (event): Promise<SessionStopEventResult | undefined> => {
Expand Down
21 changes: 19 additions & 2 deletions bin/fm-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
# the beacon mtime, which a healthy between-turns watcher advances every poll);
# later guarded commands in the same episode print a one-line reminder instead.
# Episode state lives only under state/.guard-watcher-stale-banner (volatile,
# bounded). Independent alarms (queued wakes, worktree tangle) are never
# suppressed by that dedup. Normal wake handling (watcher briefly down between a
# bounded). Independent alarms (queued wakes, worktree tangle, a stale
# in-flight OMP fallback wake) are never suppressed by that dedup. Normal wake handling (watcher briefly down between a
# wake and the next supervision resume) stays inside the grace window and stays
# silent. The queued-wakes warning counts only the rows the calling actor can
# itself present or retire (fm_wake_actor_pending_count), so it is never an
Expand Down Expand Up @@ -162,6 +162,23 @@ if [ -n "$tangle_branch" ]; then
} >&2
fi

# A still in-flight OMP main-fallback wake that has outlived a full turn
# boundary means the runtime never surfaced its consumption, so every later
# main-bound wake is being suppressed. The extension mirrors that marker to
# state/extensions/omp-primary-watch/main-fallback-episode.state; warn whenever
# a turn end is recorded after the send with the marker still set.
OMP_EPISODE_STATE="$STATE/extensions/omp-primary-watch/main-fallback-episode.state"
if [ -f "$OMP_EPISODE_STATE" ]; then
omp_inflight=$(sed -n 's/^in_flight=//p' "$OMP_EPISODE_STATE" | head -1)
omp_sent_ms=$(sed -n 's/^in_flight_sent_at_ms=//p' "$OMP_EPISODE_STATE" | head -1)
omp_turn_end_ms=$(sed -n 's/^last_turn_end_at_ms=//p' "$OMP_EPISODE_STATE" | head -1)
if [ "$omp_inflight" = 1 ] \
&& [[ "$omp_sent_ms" =~ ^[0-9]+$ ]] && [ "$omp_sent_ms" -gt 0 ] \
&& [[ "$omp_turn_end_ms" =~ ^[0-9]+$ ]] && [ "$omp_turn_end_ms" -gt "$omp_sent_ms" ]; then
echo "WARNING: an OMP main-fallback wake stayed in-flight across a full turn boundary without consumption - later main-bound wakes are suppressed; a session replacement clears the marker." >&2
fi
fi

# Compute supervision need and watcher-beacon freshness via the shared
# grace-based predicate (bin/fm-supervision-lib.sh). Act when work, an event
# source, or an X-mode relay poll needs supervision.
Expand Down
Loading
Loading