Skip to content

Durable action-status mirror + shared chat-reply poller + late-approval revive#22

Merged
ThinkOffApp merged 1 commit into
mainfrom
feat/action-status-mirror
Jul 6, 2026
Merged

Durable action-status mirror + shared chat-reply poller + late-approval revive#22
ThinkOffApp merged 1 commit into
mainfrom
feat/action-status-mirror

Conversation

@ThinkOffApp

Copy link
Copy Markdown
Owner

Recovers uncommitted CodeWatch-button backend work from the Mac Mini clone that never reached origin, rebased cleanly onto main (post PR #18/#19/#20).

Three coupled fixes to the Approve/Deny button path. They ship together because the daemon and the in-process MCP server wire all three from one place and deploy as a unit.

1. Shared chat-reply poller

startChatReplyPoller moves from bin/iak-mcp-daemon.mjs into src/confirmations.mjs. It previously lived only in the daemon, so the in-process MCP confirmations server never routed room /approve//deny taps — buttons rendered but a tap never settled the intent. Now both run it from one source.

PR #18's owner-only guard is preserved in the shared poller (only petrus/isHuman taps settle; fleet agents echoing /approve are ignored).

2. Durable action-status mirror (antfarm PR #43 backend)

configureActionStatusPush + pushStatus mirror every intent/action transition to the central GroupMind action_status store (POST /actions) so CodeWatch renders durable button state when the phone is off-LAN. Fire-and-forget — a push never throws, blocks, or fails a decision. ACTION_STATUS_MAP keeps pushes monotonic.

3. Late-approval revive (TTL lapse)

An explicit human Approve overrides a soft TTL lapse: decideIntent revives an expired action to pending and executes — the executor re-validates (gh pr view precheck), so a stale-but-clean PR merges and a stale-conflicted one fails safely. scripts/action-request.mjs default TTL 15m → 24h; local poll window decoupled (interactive wait capped 15m, button stays live for full TTL).

Also wires the confirmations daemon into scripts/start-all.sh.

Excluded (stays machine-local): config/dogfood.json and the claudemm DM-poller line (hardcoded /Users/petrus path).

Tests: node --test test/confirmations.test.mjs15/15.

… poller + late-approval revive

Three coupled fixes to the CodeWatch Approve/Deny button path. They ship
together because the daemon and in-process MCP server wire all three from
one place and deploy as a unit.

1. Shared chat-reply poller (startChatReplyPoller in src/confirmations.mjs)
   The room "/approve <id>" / "/deny <id>" poller used to live only in
   bin/iak-mcp-daemon.mjs, so the in-process MCP confirmations server never
   routed taps — buttons rendered but a tap never settled the intent. Moved
   into confirmations.mjs so both the standalone daemon and the in-process
   server run it from one source. The owner-only guard from PR #18 (only
   "petrus" / isHuman taps settle; fleet agents echoing /approve are ignored)
   is preserved in the shared poller.

2. Durable action-status mirror (antfarm PR #43 backend)
   configureActionStatusPush + pushStatus mirror every intent/action
   transition to the central GroupMind action_status store (POST /actions)
   so CodeWatch renders durable button state when the phone is off-LAN and
   can't reach the daemon's localhost /intents. Fire-and-forget: a push
   never throws, blocks, or fails a decision. ACTION_STATUS_MAP keeps pushes
   monotonic (merged->completed, running->processing).

3. Late-approval revive (TTL lapse)
   An explicit human Approve now overrides a soft TTL lapse: if an action
   expired before the tap landed, decideIntent revives it to pending and
   executes — the executor re-validates (gh pr view precheck), so a
   stale-but-clean PR merges and a stale-conflicted one fails safely.
   scripts/action-request.mjs default TTL raised 15m -> 24h and the local
   poll window decoupled from TTL (cap interactive wait at 15m; the button
   stays live for the full TTL and executes whenever petrus taps).

Also wires the confirmations daemon into scripts/start-all.sh (it holds
:8788 and runs the poller). Tests: node --test test/confirmations.test.mjs
stays 15/15.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@ThinkOffApp ThinkOffApp merged commit b910ab6 into main Jul 6, 2026
3 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 96c242d1e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/confirmations.mjs
Comment on lines +427 to +430
pushStatus(action.intentId, action.status, {
actor: action.actor,
decision: action.status === 'denied' ? 'deny' : undefined,
receipt: action.output_summary,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid mirroring revivable expirations as terminal

When an action reaches its TTL, expireAction() now reaches this new pushStatus() path and mirrors expired to the durable action_status row. The same commit allows a later human Approve to reset the action back to pending and execute it, but the status store is documented here as monotonic, so the later processing update is a backwards transition from terminal expired and can be rejected/ignored. In that late-approval path CodeWatch can keep showing the action as expired while it is actually running or has succeeded.

Useful? React with 👍 / 👎.

Comment thread scripts/start-all.sh
# room /approve /deny taps (the CodeWatch Approve/Deny buttons) to the intent
# registry + action executors. Must be iak-mcp-daemon.mjs (not iak-mcp.mjs) —
# only the daemon flavor runs the poller, without which button taps never settle.
$NODE "$IAK_DIR/bin/iak-mcp-daemon.mjs" --config "$CONFIG" > "$IAK_DIR/logs/iak-confirm-daemon.log" 2>&1 &

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Create the log directory before redirecting

This new startup command redirects into $IAK_DIR/logs/iak-confirm-daemon.log, but I could not find any repo setup or this script creating $IAK_DIR/logs. In bash, redirection to a file in a missing directory fails before node is exec'd, so on a fresh checkout or machine without that directory the newly added confirmations daemon will not start and button approvals will never be routed.

Useful? React with 👍 / 👎.

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