feat(daemon): loop body wires executeIntent end-to-end (claim → dispatch → heartbeat)#107
Conversation
…tch → heartbeat) Stacked on #106. Replaces the injected-executor abstraction in daemon/loop.ts with a direct call to meta/intent.ts::executeIntent, closing the meta-orchestrator loop. Status transitions, audit-row writes, and the second sovereignty gate are all owned by executeIntent → dispatch; the loop's responsibility is leader lifecycle, intent claiming, heartbeats, and outcome classification. Four outcomes are handled distinctly: - ok=true (executed) → bump processed counter, reset error backoff - ok=true (replayed) → bump replayed counter, no backoff, no double-count - ok=false (refused) → bump refused counter, no backoff (steady-state) - ok=false (exec error) → bump errored counter, bounded exp backoff Sovereignty refusals are identified by canonical error prefixes emitted by meta/executors/dispatch.ts ("sovereignty re-reckon:" / "sovereignty snapshot stale ..."). Refusals are NOT treated as transient faults — they are valid outcomes and do not trigger backoff. The loop honors options.signal via AbortController throughout, including inside the sleep helper, so SIGTERM from daemon/runtime/entrypoint.ts (PR #105) unwinds cleanly through releaseLeadership. tests/daemon/loop.spec.ts — real Neon integration. Seeds two pending intents against the update_obligation_status executor, runs runLeaderLoop with maxIntents=2, asserts: - both intents reach status='done' - each produces exactly one cc_actions_log row (attempt=1, key set, status='completed') - cc_obligations rows actually moved to 'deferred' - cc_node_leases shows leadership released on clean exit - log stream contains intent_heartbeat_before / intent_heartbeat_after pairs Out of scope (not in this PR): - new executors (mercury, etc.) - production deploy - multi-node coordination beyond single-node leader - schema additions on cc_intents / cc_actions_log Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
chittycommand | 56603bc | Jun 04 2026, 12:58 PM |
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
To use Codex here, create a Codex account and connect to github. |
P1 entrypoint stub executor → refusal (codex 3352439151): The stub returned a sentinel dispatchedTaskId, causing the leader loop to call markIntentDispatched + completeIntent and record real pending intents as 'done' without execution. Replace with an explicit throw so the loop's failure path runs (intent → failed with clear refusal message). The real executor lands in PR #107. P2 entrypoint SIGTERM release sessionId (codex 3353069328): releaseLeadership gates on session ownership (codex-p2 PR#101); the fallback shutdown release passed no sessionId, so it always no-op'd against our own session-stamped lease. Pass the loop's sessionId. P2 install-daemon-vm.sh devDeps prune (codex 3352439158): npm ci --omit=dev pruned typescript, breaking 'npm run build:daemon'. Install full deps for the repo build step; runtime image in still gets --omit=dev separately. P2 systemd unit NODE_BIN (codex 3352439162): Hard-coded /usr/bin/node breaks nvm / /usr/local/bin installs. Ship unit with @@NODE_BIN@@ placeholder; install script substitutes the detected node path before installing. P2 systemd MDWE+JIT (codex 3352439169): MemoryDenyWriteExecute=true is documented incompatible with V8 JIT and would abort node at startup. Remove the flag; document why. P2 launchd plist missing env (codex 3352439167): launchd has no EnvironmentFile equivalent. Plist invoked node directly without DATABASE_URL/NODE_CHITTY_ID/NODE_DESCRIPTOR, hitting entrypoint.ts's fatal-missing-env branch on Mac Mini nodes. Add a launchd-shim.sh that sources /etc/chittycommand/env (same shape as systemd EnvironmentFile) then execs node. P2 migration 0016 single-upload conflict (codex 3352439160): Unique partial index on r2_key broke single /upload's plain INSERT...RETURNING — re-uploads now 500'd on unique violation. Add ON CONFLICT (r2_key) WHERE (r2_key IS NOT NULL) DO NOTHING to match batch path; fall back to SELECT-existing returning 200. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review findings (automated)Critical
Important
Suggestion
Confirmed OK
|
a8eb86c
into
feat/meta-executors-registry
|
|
To use Codex here, create a Codex account and connect to github. |
…tch → heartbeat) (#107) Stacked on #106. Replaces the injected-executor abstraction in daemon/loop.ts with a direct call to meta/intent.ts::executeIntent, closing the meta-orchestrator loop. Status transitions, audit-row writes, and the second sovereignty gate are all owned by executeIntent → dispatch; the loop's responsibility is leader lifecycle, intent claiming, heartbeats, and outcome classification. Four outcomes are handled distinctly: - ok=true (executed) → bump processed counter, reset error backoff - ok=true (replayed) → bump replayed counter, no backoff, no double-count - ok=false (refused) → bump refused counter, no backoff (steady-state) - ok=false (exec error) → bump errored counter, bounded exp backoff Sovereignty refusals are identified by canonical error prefixes emitted by meta/executors/dispatch.ts ("sovereignty re-reckon:" / "sovereignty snapshot stale ..."). Refusals are NOT treated as transient faults — they are valid outcomes and do not trigger backoff. The loop honors options.signal via AbortController throughout, including inside the sleep helper, so SIGTERM from daemon/runtime/entrypoint.ts (PR #105) unwinds cleanly through releaseLeadership. tests/daemon/loop.spec.ts — real Neon integration. Seeds two pending intents against the update_obligation_status executor, runs runLeaderLoop with maxIntents=2, asserts: - both intents reach status='done' - each produces exactly one cc_actions_log row (attempt=1, key set, status='completed') - cc_obligations rows actually moved to 'deferred' - cc_node_leases shows leadership released on clean exit - log stream contains intent_heartbeat_before / intent_heartbeat_after pairs Out of scope (not in this PR): - new executors (mercury, etc.) - production deploy - multi-node coordination beyond single-node leader - schema additions on cc_intents / cc_actions_log Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P1 entrypoint stub executor → refusal (codex 3352439151): The stub returned a sentinel dispatchedTaskId, causing the leader loop to call markIntentDispatched + completeIntent and record real pending intents as 'done' without execution. Replace with an explicit throw so the loop's failure path runs (intent → failed with clear refusal message). The real executor lands in PR #107. P2 entrypoint SIGTERM release sessionId (codex 3353069328): releaseLeadership gates on session ownership (codex-p2 PR#101); the fallback shutdown release passed no sessionId, so it always no-op'd against our own session-stamped lease. Pass the loop's sessionId. P2 install-daemon-vm.sh devDeps prune (codex 3352439158): npm ci --omit=dev pruned typescript, breaking 'npm run build:daemon'. Install full deps for the repo build step; runtime image in still gets --omit=dev separately. P2 systemd unit NODE_BIN (codex 3352439162): Hard-coded /usr/bin/node breaks nvm / /usr/local/bin installs. Ship unit with @@NODE_BIN@@ placeholder; install script substitutes the detected node path before installing. P2 systemd MDWE+JIT (codex 3352439169): MemoryDenyWriteExecute=true is documented incompatible with V8 JIT and would abort node at startup. Remove the flag; document why. P2 launchd plist missing env (codex 3352439167): launchd has no EnvironmentFile equivalent. Plist invoked node directly without DATABASE_URL/NODE_CHITTY_ID/NODE_DESCRIPTOR, hitting entrypoint.ts's fatal-missing-env branch on Mac Mini nodes. Add a launchd-shim.sh that sources /etc/chittycommand/env (same shape as systemd EnvironmentFile) then execs node. P2 migration 0016 single-upload conflict (codex 3352439160): Unique partial index on r2_key broke single /upload's plain INSERT...RETURNING — re-uploads now 500'd on unique violation. Add ON CONFLICT (r2_key) WHERE (r2_key IS NOT NULL) DO NOTHING to match batch path; fall back to SELECT-existing returning 200. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stacked on #106 (
feat/meta-executors-registry). Merge #106 first.Summary
Closes the meta-orchestrator end-to-end loop. The cluster daemon's persistent leader loop now drives claimed Intents through
executeIntent(added in #106), which dispatches via the executor registry, applies the second sovereignty gate, and atomically updatescc_intents.status+ writescc_actions_log.Removes the injected
IntentExecutorabstraction from the foundation skeleton — it was a placeholder per ADR-001 out-of-scope, replaced now that the registry exists.What changed
daemon/loop.tsIntentExecutorinterface andmarkIntentDispatched/completeIntent/failIntentcalls from the loop body — those are now owned byexecuteIntent, which avoids double-driving the state machine.claimNextIntent→executeIntent→ classifyExecutorResult→ heartbeat → continue.okreplayedintentsProcessed, reset backoffintentsReplayed, no backoff, no double-countintentsRefused, no backoff (steady-state)intentsErrored, bounded exp backoff (1s → 30s cap)meta/executors/dispatch.ts(sovereignty re-reckon:andsovereignty snapshot stale ...). Refusals are NOT treated as transient faults.reclaimStuckIntents, andAbortSignal-driven shutdown viareleaseLeadershipare preserved from the skeleton.maxIterationsoption provides a hard safety cap for tests when the queue might drain to empty before reachingmaxIntents.tests/daemon/loop.spec.ts(new)Real-Neon integration test. Skips without
DATABASE_URL. Seeds two real Goals/Plans/Intents againstupdate_obligation_statusplus two realcc_obligationsrows, then runsrunLeaderLoopwithmaxIntents=2.Asserts:
status='done'cc_actions_logrow (attempt=1, 64-char hexidempotency_key,status='completed',action_type='status_change')cc_obligationsrows actually moved to'deferred'cc_node_leasesrow shows leadership was released on clean exit (node_idNULL,session_idNULL)intent_heartbeat_before/intent_heartbeat_afterpairs and at least oneleader_acquiredEnd-to-end trace
Test ran on Neon project
cool-bar-13270800, branchpr-daemon-loop-executes-intents(parent:pr-a-meta-executors-registry):Lease state after clean exit (post-test query):
The loop log stream during the run shows the canonical sequence:
What is NOT in this PR
feat/daemon-supervisor-vm-bootstrap). PR feat(daemon): supervisor unit + chittyserv-vm bootstrap (stops before start) #105's entrypoint passes a stub executor; once both feat(meta): extract executor registry, wire executeIntent, ADR-001 amendment (PR-A) #106 and this PR merge, feat(daemon): supervisor unit + chittyserv-vm bootstrap (stops before start) #105 needs a one-line follow-up to drop the stub and rebase. Not in scope here.cc_intents/cc_actions_logbeyond what feat(meta): extract executor registry, wire executeIntent, ADR-001 amendment (PR-A) #106 added. The stop-condition checked: the loop body did NOT need new fields.meta/executors/*ormeta/intent.ts::executeIntent. This PR consumes them; it does not refactor them.Validation
npm run typecheck— cleanSKIP_INTEGRATION=1 npm test— 15 passed, 13 skippedTest plan
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com