Skip to content

Bulletproof the background wait, batch delivery, and the editor - #42

Merged
petergyang merged 19 commits into
mainfrom
feat/background-poll
Sep 3, 2026
Merged

Bulletproof the background wait, batch delivery, and the editor#42
petergyang merged 19 commits into
mainfrom
feat/background-poll

Conversation

@petergyang

@petergyang petergyang commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

Builds on the background-poll change so every wait terminates cleanly, then lands the top items from the audit and the community PRs. One branch, phased commits.

The wait (why #42 exists)

  • The agent starts one human-review poll <target> in the background and ends its turn. The command exits only on Send, End review, tab close, or when no review is open — never on a timer. An open-ended wait reconnects across server restarts and caps itself at 12 hours.
  • Closing the tab ends the review after a short grace (a reload comes back in time); a tab that silently vanishes ends it after 30 minutes; a session no browser ever connected to is dropped after a minute. Every path releases the poll with status: closed, a reason, and the unsent counts, and the skill tells the agent to report those in one line.
  • A newer poll for the same target supersedes an older one, so stale waits cannot double-apply.

Batch delivery

  • delivered is persisted, so a server replaced between delivery and --ack honors the ack instead of re-shipping. A batch written by an overlapping server is read from disk.
  • Sending while the agent is applying a batch queues only the new items; the agent's next --ack clears both. The UI shows "queued" rather than "nobody is listening".
  • Per page: edits_saved and markdown flags. Edit text is capped at 200k with an explicit truncated marker instead of a silent 4k cut. Markdown and self-rendering pages keep unsent edit rows when the file changes on disk.
  • Rewording a comment updates an undelivered batch in place, or takes a fresh id after delivery so the rewrite ships next time.

Hardening

  • One server per state directory: lock file plus health-checked takeover of an older-protocol server. Protocol 9.
  • Artifact URLs carry a per-run secret; a path-derived key is no longer enough to read sibling files.
  • Localhost reviews proxy the app's own requests (API calls, prefetches, srcset) to the app origin. srcset is absolutized too.

Editor

  • Undo for block delete and move (toast with an Undo button).
  • Leftover feedback from a review that ended without a Send gets a Keep/Discard banner on the next open, noting that HTML text edits are already in the file.
  • Typing over a selection that spans blocks reports every block, including a deleted row for one that vanished; a paragraph turned into a list keeps its label and original; label ordinals follow load order so a deletion doesn't renumber siblings.
  • Comments get an Edit button and an edited badge.
  • The tab re-opens its session after a long sleep instead of becoming a dead tab that looks alive.

Community

SKILL.md

  • closed/superseded handling, edits_saved, deleted and truncated rows, quotes are rendered text with anchor context, copy staged assets before ack, note-only batches. Codex block matches.

Test plan

  • npm test — 117 passing; new test/lifecycle.test.js covers tab close, reload grace, no-review grace, supersede, queued sends, delivery across restart, truncation, external writes, undo/discard, rewording, edits_saved, artifact secret, and the localhost proxy
  • Real Chrome: closing the review tab released a background poll with reason: window_closed; Undo toast removed the row; leftover banner shows the right counts; no console errors

Production cleanup (added)

  • setup now owns the AGENTS.md block with marker comments and rewrites it on every run, replacing the unmarked block older setups wrote. Projects set up months ago pick up current instructions instead of keeping stale ones forever.
  • Each page's pristine copy lives in pristine/<key>.html instead of inside state.json, written only when it changes. Edit flushes no longer rewrite whole documents.
  • Editor smoke tests under jsdom (delete + undo, multi-block typing, stable labels), which caught one more labeling bug.
  • Version 0.7.0.
  • Tests: 123 passing.

The skill told agents to run poll --timeout 600 in the foreground and
re-run it on every timeout. Now it starts one open-ended poll in the
background and ends the turn; the harness wakes the agent when the
command exits, which only happens on Send or when the review is closed.

To make an open-ended wait safe, the CLI reconnects (restarting the
server if needed) when the connection drops instead of giving up after
three tries, and treats a socket close without end as a drop so the
promise cannot be left unsettled.
…eading-label bugs

- Port community PR #7 by @noammigdali-hio onto the invocation(run) signature:
  a bin under any node_modules segment is transient, not just npm's _npx cache.
- Issue #12: clicks inside the compose textarea place the caret instead of
  being swallowed by the card's focus handler.
- Issue #25: <summary> keeps its native toggle so collapsed content stays reachable.
- Issue #26: a heading labels itself instead of being named after the heading before it.
…ds, one server per state dir

- A tab that unloads tells the server; unless it reconnects within a short
  grace the review ends with reason window_closed and the waiting poll is
  released. A tab that silently vanishes ends the review after the 30-minute
  TTL instead of being deleted with the poll left hanging.
- A poll with no review open waits a short grace for one, then closes with
  reason no_review_open. Closed answers carry the unsent counts.
- A new poll for a target supersedes an older one, so stale background waits
  cannot double-apply a batch.
- delivered is persisted, so a server replaced between delivery and --ack
  honors the ack instead of re-shipping the batch. A batch written by an
  overlapping server is read from disk.
- Sending while the agent is applying a batch queues only the new items and
  carries the old cleanup forward; the agent's next --ack clears both.
- edits_saved and markdown flags per page; edit text capped at 200k with an
  explicit truncated marker instead of a silent 4k cut; Markdown and
  self-rendering pages keep unsent edit rows when the file changes on disk.
- Artifact URLs carry a per-run secret segment; a page key derived from the
  file path is no longer enough to read sibling files.
- Localhost reviews proxy the app's own requests (API calls, prefetches,
  srcset images) to the app origin instead of 404ing.
- One server per state directory: a lock file plus a health-checked takeover
  of an older-protocol server. Protocol bumped to 9.
- New routes: DELETE edit (undo), POST mode (dynamic), POST discard, POST away.
- Comment rewording updates an undelivered batch in place, or takes a fresh
  id after delivery so the rewrite ships next time.
…er banner, multi-block edits

- The chrome tells the server when the tab unloads (keepalive fetch on
  pagehide) and re-opens a session when the server has forgotten it, instead
  of turning into a dead tab that still looks alive.
- Undo for the two irreversible gestures: a toast with an Undo button after a
  block delete or move restores the block and drops its edit row.
- Feedback left over from a review that ended without a Send shows a
  Keep/Discard banner on the next open, noting that HTML text edits are
  already in the file either way.
- A second Send while the agent works shows as queued, not as nobody
  listening. Comments get an Edit button and an edited badge; rewording
  after delivery explains it ships with the next Send.
- Typing over a selection that spans blocks reports every block it touched,
  including a deleted row for one that vanished; a paragraph turned into a
  list keeps its label and captured original.
- CLI: an open-ended wait ends after 12 hours with a timeout status that
  says to check status and start again; superseded polls just print.
- SKILL.md: closed/superseded handling, edits_saved, deleted and truncated
  rows, rendered quotes with anchors, staged assets before ack, note-only
  batches. Codex block updated to match.
- srcset URLs are absolutized for localhost reviews.
…oad order

- After the server ended a session, the tab's event stream reconnected, got
  a 404, and the new re-bootstrap path opened a fresh session with no tab
  behind it, which kept the review open and the poll waiting. The chrome
  now never re-bootstraps once the review ended or the tab is unloading,
  and the server drops a session no browser connected to within a minute.
- Label ordinals ("p 3") come from sibling order at load, so deleting a
  paragraph does not hand two blocks the same label within one review.
- A list command now retracts the row the fresh <li> emitted about itself
  and reports the change under the paragraph's own label with its original
  text.
@petergyang petergyang changed the title Wait for Send with one background poll instead of timed re-polls Bulletproof the background wait, batch delivery, and the editor Sep 2, 2026
…meout

On a slow Windows runner the poll's reconnect could spawn its own server
before the test's replacement announced itself, and then wait forever for a
batch sent elsewhere. The first reconnect now waits 3s, the test races the
poll against a 30s timer, and every test gets a 60s timeout so a hang fails
the file instead of the whole job.
…the poll in the foreground

Codex returns a detached session id and nothing wakes the agent when that
session finishes, so a Send sat unread until the user nudged. The skill and
the AGENTS.md block now say which harness gets which wait, and how to cope
with a shell tool that caps command duration.
A project set up months ago kept the instructions from that day forever,
because setup skipped any AGENTS.md that already mentioned human-review.
The block is now written between marker comments and rewritten on every
setup; the unmarked block an older setup wrote is recognized by its heading
and replaced; a project that wrote its own human-review guidance is left
alone.
Every edit flush rewrote state.json in full, and it embedded the complete
HTML of every page reviewed in the last month. The copy now lives in
pristine/<key>.html, is written only when it changes, and is removed with
its page. A state file that still embeds copies loads as before and moves
them out on its next save.
Three tests boot the SDK against a jsdom document: block delete with undo,
typing over a selection that spans blocks, and stable labels after a
deletion. The last one caught that the load-order snapshot skipped the body
element, so top-level paragraphs still renumbered.
@petergyang
petergyang merged commit b20f82b into main Sep 3, 2026
6 checks passed
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