Skip to content

refactor(commands): extract command poll + dispatch + ack into commands/drain.ts#1317

Merged
itskai-dev merged 1 commit into
mainfrom
refactor/commands-drain-extract
Apr 29, 2026
Merged

refactor(commands): extract command poll + dispatch + ack into commands/drain.ts#1317
itskai-dev merged 1 commit into
mainfrom
refactor/commands-drain-extract

Conversation

@itskai-dev

Copy link
Copy Markdown
Collaborator

Summary

§11e step 1 of COMMAND_PLUGIN_SKILL_ARCHITECTURE.md v1.0 (reflectt-cloud merged at da6cdba3). Behavior-zero relocation of the inline command runtime that lived in cloud.ts into a dedicated src/commands/drain.ts.

This is step 1 only. Per @kai (msg-1777492135899): "After that, reassess before the empty registry PR." Step 2 (empty registry contract + relocate the two existing verbs into per-verb handler files) lands as a separate PR.

What moves

From src/cloud.tssrc/commands/drain.ts, verbatim:

  • pollAndProcessCommands() — heartbeat-tick poll, 10s active / 60s idle
  • handleCommand(cmd) — inline if/else dispatch by cmd.type
  • handleContextSync(cmd) — fetches local context, posts to cloud sync
  • handleRunApprove(cmd) — calls local approval-queue, acks
  • COMMAND_POLL_ACTIVE_MS / COMMAND_POLL_IDLE_MS constants
  • commandPollErrors / lastCommandPollAt counters
  • PendingCommand interface

cloud.ts exports state, config, isIdle, cloudGet, cloudPost so drain.ts can import them. ES live-binding semantics preserve behavior — cloud.ts still owns the lets and reassigns config at line 368. The two existing pollAndProcessCommands() call sites in cloud.ts (heartbeat tick + immediate kick) are unchanged.

Scope locks (kai msg-1777492135899 / link msg-1777492193718)

  • behavior-zero
  • same poll cadence
  • same ack contract
  • same inline dispatch semantics
  • no registry creep until step 2
  • no auth/intake churn
  • no new verbs

Gates

  • npx tsc --noEmit — clean
  • npm test — 242/242 files pass, 2608/2608 tests pass (1 pre-existing skip), 31s
  • npm run build — clean
  • canonical staging command-queue smoke (poll, ack-complete, ack-fail) — recommend rolling rn-34faba44-wlgkeq to the new image and walking a context_sync command before merge

Test plan

  • npm test green in CI
  • npm run build green in CI
  • After merge: docker-publish rolls new image; canonical host rn-34faba44-wlgkeq updated; observe 📬 Command polling: 10s active → 60s idle startup log + at least one ☁️ [Commands] Processing context_sync line in production logs

Reviewer hooks

  • @link — runtime/drain seam: is commands/drain.ts the right module shape? Are the cloud.ts re-exports of state/config/cloudGet/cloudPost minimal enough?
  • @kai — confirm step 1 alone (reassess before step 2 per your msg-1777492135899)

🤖 Generated with Claude Code

…ds/drain.ts

§11e step 1 of COMMAND_PLUGIN_SKILL_ARCHITECTURE.md v1.0 (merged in
reflectt-cloud as commit da6cdba3). Behavior-zero relocation of the
inline command runtime that lived in cloud.ts.

Moved verbatim from cloud.ts into src/commands/drain.ts:
- pollAndProcessCommands()  — heartbeat-tick poll, 10s active / 60s idle
- handleCommand(cmd)        — inline if/else dispatch by cmd.type
- handleContextSync(cmd)    — fetches local context, posts to cloud sync
- handleRunApprove(cmd)     — calls local approval-queue, acks
- COMMAND_POLL_ACTIVE_MS / COMMAND_POLL_IDLE_MS constants
- commandPollErrors / lastCommandPollAt counters
- PendingCommand interface

Same poll cadence. Same ack contract. Same inline dispatch (no registry
yet — that lands in step 2). No new verbs. No auth/intake churn.

cloud.ts changes:
- exports state, config, isIdle, cloudGet, cloudPost so drain.ts can
  import them. ES live-binding semantics preserve current behavior;
  cloud.ts still owns the lets and reassigns config at line 368.
- imports pollAndProcessCommands + the two MS constants from
  ./commands/drain.js. Two existing call sites (heartbeat tick line 436
  and immediate-tick line 573) unchanged.
- 197 net lines removed; module shrinks toward owning just the
  cloud-transport concern.

Scope locks per @kai (msg-1777492135899) and @link (msg-1777492193718):
- behavior-zero
- same poll cadence
- same ack contract
- same inline dispatch semantics
- no registry creep until step 2
- no auth/intake churn
- no new verbs

Gates:
- typecheck: clean (npx tsc --noEmit, exit 0)
- vitest: 242/242 files pass, 2608/2608 tests pass (1 pre-existing skip)
- build: clean (npm run build)

Step 2 follow-up (per kai: "After that, reassess before the empty
registry PR") will land separately — this is step 1 only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@itskai-dev

Copy link
Copy Markdown
Collaborator Author

Closeout — §11e step 1 merged on 2/3 proofs

Per kai (msg-1777493903222) + link (msg-1777493906404):

Proofs captured on canonical staging (rn-34faba44-wlgkeq machine 568354e7ad5348 running ghcr.io/reflectt/reflectt-node:6aca844):

  1. ✅ Clean boot — agent suite (compass/main/orbit/watchdog) starts normally
  2. ✅ No regression — proactive ContextSync, presence/events/chat all healthy
  3. ❌ One real command through the relocated drain — upstream blocker, not a drain concern

Named seam (carries to step 2 reassess, not into this PR):

The cloud API has no HTTP enqueue endpoint for commands. apps/api/src/routes/registry.ts registers only GET /api/hosts/:hostId/commands (poll) and POST .../ack — there is no POST /api/hosts/:hostId/commands. The in-memory commandStore (apps/api/src/domains/command/queue.ts) has exactly one in-process producer: spend-alert.ts calling pushCommand on threshold breach.

So canonical can prove clean boot + no regression, but not a naturally-queued command through the relocated drain path — because no reachable cloud enqueue path exists yet.

Next reassess (per kai): decide whether "no reachable cloud enqueue path" belongs inside the next node-runtime step (§11e step 2 = empty registry + per-verb handlers), or is a separate cloud/runtime seam that stays split out.

Until that call: hold per feedback_stop_and_hold_after_lane_close.

@itskai-dev
itskai-dev merged commit f5fe369 into main Apr 29, 2026
12 checks passed
@itskai-dev
itskai-dev deleted the refactor/commands-drain-extract branch April 29, 2026 20:19
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