Skip to content

watch: the cursor moves when the round is known to have landed - #10

Merged
itswl merged 1 commit into
mainfrom
watch-cursor-two-phase
Oct 19, 2026
Merged

itswl merged 1 commit into
mainfrom
watch-cursor-two-phase

Conversation

@itswl

@itswl itswl commented Oct 19, 2026

Copy link
Copy Markdown
Owner

The watch scanner wrote its new cursors before the model ran. A round whose run failed — a model error, a dead gateway, a refused delivery, a timer restart between scan and POST — was never looked at again, and nothing said so: the next tick scanned from the new cursor as if the round had been judged. A second deployment running a copy of this scanner measured it on 2026-10-12 (two rounds lost to one configuration fault) and fixed it by staging the cursor and reconciling against the job's run record. This is that shape, built from the pieces this family already has.

How it works (deploy/watch/watch_scan.py, hookprobe/examples/patrols/patrol-timer.sh, the work compose)

  • A round that fires stages its new cursors under staged in scan.json and runs Jira against a staged copy of its state; feeds keeps the committed cursors.
  • After each POST the timer writes what the pipe answered to round.json (posted, event id, outcome, fired-at).
  • The next tick settles first, before the window check: the scanner reads the node's own run record for probe:watch-due:<event id> under the timer's read-only /node-state/results mount. completed commits the cursors and the Jira copy. failed, a POST that failed, an answer other than routed, or no finished record within WATCH_RUN_TIMEOUT (30 min) puts the cursors back; the next scan re-offers from them and says so in the digest. A run still going holds the tick instead of offering the same messages twice.
  • After two replays and an hour since the first failure, only the first lost round's content is skipped (its cursors are committed) and the skipped span is named in the digest until a round lands. Jira is never skipped: its lookback re-reports the span, and a repeat is a nuisance where a miss is a failure.
  • offered and previous keep their meaning for the signer and the poster; a quiet round (no model asked) commits directly as before.

Tests — twelve in deploy/watch/tests/test_scan.py: commit on completed, replay on failed / not posted / posted before the scan / not routed / no event id, hold while a run is young or still running, lost past the timeout, give up after two replays and an hour (and not before), a stray Jira copy dropped, the skipped note cleared by a delivered round, the record found by its key, and Jira running against the staged copy with the committed state untouched.

Docs — a paragraph in docs/deployments.md under the work shape; decision note .agents/notes/implemented/2026-10-19-the-cursor-moves-when-the-round-is-known-to-have-landed.md.

Nothing is running; the work stack picks this up at its next recreate (the timer's two new variables are in the compose). Gate: STACK GATE GREEN locally; CI read back below before merging.

The scanner wrote its new cursors before the model ran, so a round whose run failed was never looked at again and nothing said so; a second deployment running a copy of this scanner lost two rounds that way on 2026-10-12. A round that fires now stages its cursors (and Jira's state, on a staged copy) and the next tick settles them first, before the window check, against the node's own run record for the event the pipe numbered — the timer writes the pipe's answer to round.json after each POST. completed commits; failed, a POST that failed, an answer other than routed, or no finished record within WATCH_RUN_TIMEOUT puts the cursors back and the next scan re-offers from them with a line saying so; a run still going holds the tick rather than offering the same messages twice.

After two replays and an hour since the first failure only the first lost round's content is skipped and the skipped span is named in the digest until a round lands. Jira is never skipped: its lookback re-reports the span, and a repeat is a nuisance where a miss is a failure. feeds in scan.json now always means committed; offered and previous keep their meaning for the signer and the poster. Twelve tests; the work compose names WATCH_ROUND_FILE and WATCH_NODE_RESULTS; decision note beside it.
@itswl
itswl merged commit 8b7f595 into main Oct 19, 2026
8 checks passed
@itswl
itswl deleted the watch-cursor-two-phase branch October 19, 2026 02:14
itswl added a commit that referenced this pull request Oct 26, 2026
- runs: resolve a session key by matching iterdir() entries on the sanitized filename, so no path is ever built from the key (py/path-injection #14).
- engine_replay: replace the Title: regex with a line scan, so the prompt cannot reach a backtracking pattern (py/polynomial-redos #10).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant