Skip to content

feat(meta): extract executor registry, wire executeIntent, ADR-001 amendment (PR-A)#106

Merged
chitcommit merged 6 commits into
mainfrom
feat/meta-executors-registry
Jun 10, 2026
Merged

feat(meta): extract executor registry, wire executeIntent, ADR-001 amendment (PR-A)#106
chitcommit merged 6 commits into
mainfrom
feat/meta-executors-registry

Conversation

@chitcommit

Copy link
Copy Markdown
Contributor

Context

Builds on merged foundation PRs #101 (meta/, daemon/, intent ladder, sovereignty gate) and #103 (Codex P2 fixes). Implements the executor extraction called for by the ADR-001 follow-up review.

ADR-001 (docs/architecture/ADR-001-meta-orchestrator-extension.md) was merged in #101; this PR amends its Consequences §2 to record the executor-registry decision.

Reviewer verdicts (the spec this PR implements)

  • chittyschema-overlord: no new intent_executions table — three additive columns on cc_actions_log (intent_id, attempt, idempotency_key) plus a partial unique index and a (intent_id, executed_at DESC) index.
  • chittycanon-code-cardinal: Option 2 (ActionAgent + meta-daemon as SIBLINGS consuming the registry; neither dispatches the other) is canonically compliant. Sovereignty gate has exactly TWO call points: Intent creation and executor entry.
  • Ecosystem search: Intent in meta/intent.ts is already a meta-dispatcher — register handlers per intentType, no new ActionSpec abstraction.

What's in this PR

  1. docs/architecture/ADR-001-meta-orchestrator-extension.md — Consequences §2 replaced verbatim per the chittycanon-code-cardinal paragraph; added ## ADR-001 amendments (chronological) with 2026-06-04 entry.
  2. meta/executors/ — new directory:
    • types.tsExecutorContext, ExecutorResult, IntentExecutor, SOVEREIGNTY_FRESHNESS_MS (5 min default).
    • registry.tsregisterExecutor, getExecutor, listExecutors; duplicate registration is an error.
    • dispatch.ts — single entry point: replay short-circuit (terminal prior audit row), attempt + idempotency key derivation, sovereignty re-reckon if snapshot stale (the second of the two gate call points), executor invocation, audit row write.
    • update-obligation-status.ts — first concrete executor; exports a pure runUpdateObligationStatus(args, sql) runner + a registered IntentExecutor.
    • index.ts — barrel that side-effect-imports the executor module so registration runs.
  3. meta/intent.ts — added executeIntent(env, intentId, options). Existing exports unchanged.
  4. src/agents/tools/actions.tsupdate_obligation_status tool now delegates to runUpdateObligationStatus (shared with the executor); chat path behavior unchanged. execute_payment / send_dispute_email / list_mercury_recipients / get_action_log left untouched.
  5. src/routes/meta.ts — added POST /api/v1/intents/:id/execute using the same metaRoutes auth surface as /whoami.
  6. migrations/0004_chief_skin.sql (drizzle-generated) — adds three columns + two indexes on cc_actions_log. Drizzle correctly preserved the WHERE intent_id IS NOT NULL AND idempotency_key IS NOT NULL clause on the partial unique index.
  7. tests/meta/executor.spec.ts — real-Neon integration test. Skips on missing DATABASE_URL. Inserts a real cc_obligations row, creates Goal + Plan + Intent (intent_type='update_obligation_status'), runs executeIntent twice. Asserts: intent → done; exactly one cc_actions_log row with intent_id set, attempt=1, idempotency_key non-null; second call replays (no new row).

Real-SQL evidence (Neon branch br-restless-credit-akyt3mak on project cool-bar-13270800)

After applying the migration:

column_name      | data_type | is_nullable | column_default
-----------------+-----------+-------------+----------------
attempt          | integer   | NO          | 1
idempotency_key  | text      | YES         | NULL
intent_id        | uuid      | YES         | NULL
indexname                              | indexdef
---------------------------------------+----------
idx_cc_actions_log_intent_executed     | CREATE INDEX ... USING btree (intent_id, executed_at DESC NULLS LAST)
uq_cc_actions_log_intent_idempotency   | CREATE UNIQUE INDEX ... USING btree (intent_id, idempotency_key)
                                       |   WHERE ((intent_id IS NOT NULL) AND (idempotency_key IS NOT NULL))

The partial WHERE clause survived drizzle-kit generate and Postgres accepted it.

Sample execution trace (vitest run against br-restless-credit-akyt3mak)

RUN  v4.0.18 chittycommand
 PASS  tests/meta/executor.spec.ts > meta/executors — executeIntent round-trip
   > executes a pending intent, writes audit row, and is idempotent on replay  974ms
 Test Files  1 passed (1)
      Tests  1 passed (1)

Per-step behavior the test verifies:

  1. createIntent -> status='pending' (sovereignty pre-seeded autonomous so dispatch does not re-reckon).
  2. First executeIntent -> ok=true, replayed=false, sha256 idempotency key, actionLogId set; cc_intents.status='done'; cc_obligations.status='deferred'; one row in cc_actions_log with attempt=1, intent_id=<intent.id>, idempotency_key=<sha256>, action_type='status_change', status='completed'.
  3. Second executeIntent -> ok=true, replayed=true, same idempotency key and actionLogId; still exactly one row in cc_actions_log.

Process

  • npm run typecheck: clean.
  • SKIP_INTEGRATION=1 npm test: 15 passed, 12 skipped (no regressions; preexisting skipped specs unchanged).
  • Integration spec green against real Neon branch.

What is NOT in this PR (deferred)

  • LLM-driven Mercury payment executor extracted into the registry — stays in src/agents/tools/actions.ts for now.
  • Workflow-based long-running executors (send_dispute_email queue/send flow).
  • daemon/loop.ts calling executeIntent (the daemon still uses its own executor callback signature; rewiring is a follow-up).
  • Stale-snapshot-triggers-reckon test branch (only the fresh-snapshot positive path is asserted; the failure branch is implemented but not asserted in this spec).
  • CHARTER.md tier reclassification (per ADR-001, intentionally separate PR).

Generated with Claude Code (https://claude.com/claude-code)

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
chittycommand 747a995 Jun 04 2026, 01:00 PM

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@chitcommit, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 54 minutes and 45 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b648d31-2b30-4b55-a8f9-e290f1e2e48f

📥 Commits

Reviewing files that changed from the base of the PR and between 24e86c8 and fc60bfc.

📒 Files selected for processing (25)
  • CHARTER.md
  • CHITTY.md
  • CLAUDE.md
  • daemon/loop.ts
  • docs/architecture/ADR-001-meta-orchestrator-extension.md
  • docs/registration/SUBMISSION_RUNBOOK.md
  • docs/registration/chittycommand-registration-payload.json
  • meta/executors/dispatch.ts
  • meta/executors/index.ts
  • meta/executors/registry.ts
  • meta/executors/types.ts
  • meta/executors/update-obligation-status.ts
  • meta/intent.ts
  • migrations/0005_sour_dreadnoughts.sql
  • migrations/meta/0005_snapshot.json
  • migrations/meta/_journal.json
  • src/agents/tools/actions.ts
  • src/db/schema.ts
  • src/index.ts
  • src/routes/health.ts
  • src/routes/meta.ts
  • tests/daemon/loop.spec.ts
  • tests/health/health-probe.spec.ts
  • tests/meta/executor-pr106-criticals.spec.ts
  • tests/meta/executor.spec.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/meta-executors-registry

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@chitcommit

Copy link
Copy Markdown
Contributor Author

Code review findings (automated)

Critical

  • Replay short-circuit ignores idempotency_key (meta/executors/dispatch.ts:116-141) — doc comment promises "(intent_id, idempotency_key)" matching, but SQL filters only on intent_id + status IN ('completed','failed'). Any subsequent executeIntent (operator retry after failed, or a future attempt-bumping retry strategy) short-circuits on the first terminal row regardless of whether the new attempt computes a different key. Makes lines 147-155's per-attempt key logic unreachable for any intent that's ever produced a terminal row. Cascades into PR feat(executor): mercury_payment executor — sovereignty + cap + idempotency (REAL MONEY PATH) #108 — Mercury executor's idempotency story breaks at this layer. Fix: compute attempt/idempotencyKey first, WHERE intent_id = $1 AND idempotency_key = $2 in replay lookup; OR document "one terminal row per intent forever" and drop the per-attempt model. Confidence 92.
  • Double-failIntent on sovereignty refusal (dispatch.ts:187, 217 + meta/intent.ts::executeIntent:~699) — dispatch already calls failIntent on refusal paths, then executeIntent checks !result.replayed (true on refusal) and calls it again. Probably no-ops due to a status guard but fragile and untested. Fix: set replayed:true on refusal return OR move failIntent entirely into executeIntent. Confidence 88.

Important

  • Cold-start ordering on registrymeta/executors/index.ts is side-effect imports; only the route path loads the barrel via executeIntent's await import('./executors'). Any future direct caller (cron, daemon, test that imports dispatch without barrel) hits "No executor registered" at runtime. Add assertRegistryPopulated() at boot OR have dispatch.ts import the barrel. Confidence 84.
  • Drizzle schema partial-index predicate — migration 0004_chief_skin.sql:6 has WHERE intent_id IS NOT NULL AND idempotency_key IS NOT NULL; snapshot includes it. Verify src/db/schema.ts declares the index with matching .where(sql\…`)or nextdrizzle-kit generate` drops the predicate, silently breaking uniqueness. Confidence 82.
  • Attempt counter races under concurrencySELECT COUNT(*) + priorCount+1 is not atomic. Two concurrent dispatchers (route + future cron) both compute attempt=1, same idempotency key, one INSERT fails on unique partial index. Dispatcher doesn't catch the unique violation → 500 from route. Fix: try/catch + retry via replay lookup, or SELECT … FOR UPDATE on cc_intents. Confidence 85.
  • ActionAgent chat path audit non-idempotent — refactored actions.ts inserts cc_actions_log with intent_id=NULL and idempotency_key=NULL. Two chat invocations on same obligation produce two identical audit rows with no dedupe. Acceptable but flag in CHARTER. Confidence 80.

Suggestions

  • dispatch.ts:107 getSql(env) duplicates Hyperdrive/DATABASE_URL resolution from meta/intent.ts. Extract once.
  • Test only exercises fresh-snapshot path. Stale-snapshot re-reckon (the headline behavior) is uncovered — track as follow-up.

Confirmed OK

  • Route auth at app.use('/api/*', authMiddleware) (src/index.ts:124) covers POST /v1/intents/:id/execute
  • No-mocks compliance: real Neon via DATABASE_URL, skip clean, no vi.mock on DB ✓

Recommendation: Block on #1 and #5 (cascade-risk bugs for #107/#108). #1 is a correctness lie. #5 will surface as flaky 500s once a second caller exists.

chitcommit added a commit that referenced this pull request Jun 4, 2026
…icals; importants deferred)

FIX 1 (cascade-risk critical): dispatch's replay short-circuit matched on
intent_id alone, ORDER BY executed_at DESC LIMIT 1. Any intent that ever
produced a terminal cc_actions_log row would short-circuit forever, no
matter what idempotency_key the new attempt computed — making per-attempt
retries unreachable. Reordered to (a) compute attempt+key first, then
(b) lookup by (intent_id, idempotency_key) which is the canonical
per-attempt invariant the partial unique index backs.

FIX 2 (cascade-risk critical): both refusal paths in dispatch wrote audit
+ called failIntent, then returned without `replayed: true`, so
executeIntent's `!result.replayed` guard called failIntent a SECOND time.
The DB no-ops the second call (status guard), but the semantic is wrong
and the error_message could be clobbered on adjacent paths. Both refusal
returns now set replayed:true; executeIntent's guard skips correctly.

Updated existing executor.spec.ts: the second-invocation block encoded
the pre-fix bug (asserted same-intent re-call replays). Under the new
contract, a second executeIntent against the same intent is a new
attempt with a new key → executor re-runs (idempotent for
update-obligation-status), audit count goes 1→2, attempt sequence [1,2].

Added tests/meta/executor-pr106-criticals.spec.ts:
- FIX 1 regression: hand-insert a terminal row with a sentinel key
  ('a'*64) for an intent, then executeIntent — key mismatch must NOT
  short-circuit, executor must run, obligation must transition.
- FIX 2 regression: stale snapshot + unroutable CHITTYTRUST_URL (RFC 5737
  192.0.2.1) forces real fetch failure → sovereignty 'blocked' → refusal
  path. Asserts result.replayed === true (the canonical FIX 2 signal)
  and exactly one sovereignty_refusal audit row.

No mocks. Real Neon only — skipped without DATABASE_URL.

Validated the FIX 1 SELECT shape against a real Neon branch
(cool-bar-13270800 / pr-106-criticals-validation, since deleted): the
key-matched query returned the seeded row for matching key and empty
for non-matching key.

Deferred to follow-up PR (intentionally out of scope here):
  - FIX 3: race-safety on concurrent dispatchers (writeAuditRowOrReplay)
  - FIX 4: schema predicate gap (status enum vs literal IN-clause)
  - FIX 5: cold-start registry import on direct dispatch entry

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

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@chitcommit

Copy link
Copy Markdown
Contributor Author

PR #106 — 2 cascade-risk criticals fixed (5cc20f2)

Scope intentionally tight: ONLY the 2 criticals. The 3 importants (race-safety, schema-predicate, cold-start registry) are deferred to a follow-up PR so this can land without scope drift cascading into #107/#108.

FIX 1 — replay short-circuit by (intent_id, idempotency_key), not intent_id alone

meta/executors/dispatch.ts previously ran the replay lookup against intent_id + status IN (terminal) and returned the most recent row regardless of key. Any intent that ever produced a terminal row would short-circuit forever — the per-attempt key logic was unreachable.

Reordered to:

  1. count prior rows → compute attempt
  2. compute idempotency_key = sha256(id:attempt:type)
  3. replay lookup matches on (intent_id, idempotency_key) — the partial unique index's invariant
  4. miss → execute

FIX 2 — single failIntent on refusal

Both sovereignty refusal paths called failIntent in dispatch, returned without replayed: true, then executeIntent's !result.replayed guard called failIntent again. The DB no-ops the second (status guard in WHERE), but the semantic was double-handling. Both refusal returns now set replayed: true; the guard skips correctly.

Test changes

  • tests/meta/executor.spec.ts — the existing second-invocation block embedded the bug being fixed (asserted same-intent re-call replays via intent_id match). Under the new contract, a second executeIntent against the same intent is a new attempt with a new key → executor re-runs (idempotent for update-obligation-status). Assertions updated: audit count 1→2, attempts [1, 2], replayed falsy.

  • tests/meta/executor-pr106-criticals.spec.ts (new) —

    • FIX 1 regression: hand-insert a terminal cc_actions_log row with sentinel key 'a'*64 for an intent, then executeIntent — key mismatch must NOT short-circuit, executor must run, obligation must transition pending → paid, audit count → 2.
    • FIX 2 regression: stale snapshot + unroutable CHITTYTRUST_URL (RFC 5737 192.0.2.1) forces real fetch failure → sovereignty blocked → refusal path. Asserts result.replayed === true (the canonical FIX 2 signal) and exactly one sovereignty_refusal audit row.

No mocks of DB or service modules. Real Neon only — skipped without DATABASE_URL.

Validation

  • npm run typecheck clean
  • SKIP_INTEGRATION=1 npm test — all green (15 passed, 14 skipped, 6 files)
  • Real Neon SQL-shape validation on a temp branch (cool-bar-13270800 / pr-106-criticals-validation, since deleted): the FIX 1 key-matched SELECT correctly returned the seeded row for a matching key and empty for a non-matching key. End-to-end Neon execution of the new test files requires the full cc_goals/cc_plans/cc_intents meta schema on a dev Neon (the default branch only has cc_actions_log and cc_obligations — meta migrations not applied there). Test files are structured to run cleanly the moment a DATABASE_URL pointing at a fully-migrated branch is supplied.

Why this matters for #107 / #108

Deferred to follow-up PR (intentional scope discipline)

  • FIX 3 — race-safety on concurrent dispatchers (writeAuditRowOrReplay pattern catching unique-violation and re-reading the row)
  • FIX 4 — schema predicate gap (status column enum vs IN clause string literals)
  • FIX 5 — cold-start registry import inside dispatch.ts for direct-import entry points

Auto-merge OFF as requested.

@chitcommit chitcommit enabled auto-merge (squash) June 4, 2026 12:58
chitcommit added a commit that referenced this pull request Jun 4, 2026
…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>
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

chitcommit added a commit that referenced this pull request Jun 4, 2026
…l consumer (#109)

* feat(daemon): loop body wires executeIntent end-to-end (claim → dispatch → 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>

* fix(health,wrangler): real-dependency /health probe + chittytrack tail consumer

The previous /health returned a static {status:"ok",...} regardless of
whether the worker could reach its real dependencies — a direct violation
of the chittyentity CLAUDE.md "No Mocks, Fake Data, or Placeholder
Endpoints" binding rule ("every endpoint must return real results against
a real datastore on the day it is committed").

This change replaces /health with a real probe that executes against the
worker's actual dependencies:

  * db            — SELECT 1 via Neon HTTP driver. Critical: failure -> 503.
  * chittyconnect — GET ${CHITTYCONNECT_URL}/health. Degraded if unreachable.
  * daemon        — newest cc_node_leases.heartbeat_at, stale if older than
                    2x daemon/loop.ts default heartbeatMs (10000ms ->
                    20000ms cutoff). Missing table -> not_provisioned
                    (degraded, NOT down) so deploys against bases without
                    #101 don't 503.

Per-dep timeout 2000ms; total probe bounded ≤ 5000ms via Promise.all.
Runs unauthenticated (no auth middleware on /health).

The probe handler is extracted to src/routes/health.ts so it can be
integration-tested in pure Node — importing src/index.ts directly drags
in `cloudflare:` modules (Agents SDK / DOs) that only resolve under
workerd. Integration test exercises the handler against a real Neon
branch (no mocks), validates DB probe ok and shape of all three probes,
and asserts 503 + status=down when DB is unreachable.

wrangler.jsonc already declares `tail_consumers: [{service: chittytrack}]`
(present on the base branch) — no change required there.

Verified against Neon branch br-spring-queen-akggkmso of project
cool-bar-13270800 (ChittyCommand). Sample real response:

{
  "status": "degraded",
  "service": "chittycommand",
  "version": "0.1.0",
  "timestamp": "2026-06-04T05:27:16.111Z",
  "probes": {
    "db": { "status": "ok", "latency_ms": 226 },
    "chittyconnect": {
      "status": "degraded", "latency_ms": 0,
      "error": "CHITTYCONNECT_URL not configured"
    },
    "daemon": {
      "status": "not_provisioned", "newest_heartbeat_age_ms": null,
      "error": "relation \"cc_node_leases\" does not exist"
    }
  }
}

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

* fix(health): query active leases by heartbeat_at, not released_at

Addresses Codex P2 on PR #109. The cc_node_leases schema in
src/db/schema.ts has no released_at column — release is represented by
NULLing heartbeat_at/lease_expires_at in daemon/leader.ts. The previous
WHERE released_at IS NULL clause raised 'column released_at does not
exist', which the catch block then mis-classified as 'not_provisioned',
so /health would never report ok/stale based on real heartbeats.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: chitcommit <noreply@chitty.cc>
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

chitcommit added a commit that referenced this pull request Jun 4, 2026
…rface (#110)

* docs(charter,chitty,claude): reclassify Tier 5 → Tier 2 platform with Tier-5 surface; add meta endpoints and dependencies

Stacked follow-on to PR #106 (ADR-001 + executor registry). Documentation-only
PR that brings the compliance triad and `/api/v1/status` in line with the
ADR-001 amendment that landed on `feat/meta-executors-registry`.

CHARTER.md / CHITTY.md / CLAUDE.md
- Canonical phrasing: "Tier 2 (Platform) with Tier-5 dashboard surface"
  everywhere a tier is declared.
- RY (Authority) row rewritten to reflect sovereignty enforcement + intent
  execution + multi-source ingest. No longer a pure consumer.
- API contract gains the one real new endpoint introduced by #106:
  POST /api/v1/intents/:id/execute. Per the global no-fake-endpoints rule,
  the planned intent CRUD (POST /api/v1/intents, GET .../:id, GET ...,
  GET /api/v1/executors) is NOT documented here because it isn't implemented;
  it will be added when the routes are.
- New Executor Registry table with canonical URI
  chittycanon://core/services/chittycommand/executors/{intent_type}. Lists
  the one executor that actually self-registers today
  (update_obligation_status). mercury_payment is flagged as a tracked future
  executor with REAL-MONEY constraints (fresh autonomous sovereignty,
  USD 500 per-intent cap) and explicitly NOT documented as registered.
- New Cluster Runtime section: daemon is launchd/systemd, NOT a Worker;
  per-node L-type ChittyIDs register as sub-channels via
  agent.chitty.cc/api/v1/channels, NOT in the main ChittyRegister payload.
- Dependencies expanded: ChittyTrust, ChittyID, chittyagent-orchestrator,
  chittyagent-tasks, chittyagent-ch1tty added. ChittyConnect entry expanded
  to call out ContextConsciousness + MemoryCloude (forever context) and
  sensitive-intent secret brokerage.
- Compliance section flags the ChittyID re-mint as required operator
  action (T → P-Synthetic) and explicitly defers /health real-probes and
  tail_consumers wiring to separate PRs.
- MCP tool count reconciled to 50 (CLAUDE.md was already correct; CHARTER/
  CHITTY updated from stale "48").

src/index.ts
- /api/v1/status returns tier: 2 plus tierSurface phrasing and a
  meta.endpoints array listing the registered intent-execute route.
  No other route, middleware, or handler touched.

src/routes/meta.ts
- /api/v1/canon returns tier: 2 + tierSurface so the public canon view
  matches /status and the docs. Same handler signature; no behavior change.

Not in this PR (deferred):
- Real-dependency /health probes (separate PR).
- tail_consumers wiring to chittytrack (separate observability PR).
- ChittyRegister payload submission (operator action; blocked on re-mint).
- Re-mint of service ChittyID as P-Synthetic (operator action).
- Intent CRUD endpoints and /api/v1/executors enumeration (future PRs).

Compliance coverage:
- Addresses: tier reclassification, RY language, meta endpoint surface,
  executor URI registry, dependency expansion, cluster sub-channel
  declaration (items 1, 4, 5, 7, 8 of the registration-readiness audit).
- Remaining (out of scope): registration submission (item 2, operational),
  real /health (item 3, separate PR), tail_consumers (item 6, separate PR),
  P-type ChittyID re-mint (item 9, operator action).

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

* docs(charter): clarify Worker vs daemon runtime split in Classification

Addresses Codex P2 on PR #110: the previous wording said the Tier-2
platform and Tier-5 surface 'both run from the same worker', which
contradicts the Cluster Runtime section stating daemon/ runs as a
supervised launchd/systemd process, not as a Worker.

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

* docs(registration): draft chittycommand Tier-2 registration payload + submission runbook (#111)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: chitcommit <noreply@chitty.cc>
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

chitcommit and others added 6 commits June 10, 2026 11:37
…endment

PR-A: foundation for the meta-orchestrator execution layer.

- meta/executors/{types,registry,dispatch,update-obligation-status,index}.ts:
  canonical executor registry addressable as
  chittycanon://core/services/chittycommand/executors/{intent_type}.
  ActionAgent (chat) and the meta-daemon (autonomous) are siblings consuming
  the registry; neither dispatches the other.
- meta/intent.ts: new executeIntent(intentId) — atomic single-id claim →
  dispatch → status transition. Idempotent on replay via dispatch's audit-row
  lookup.
- meta/executors/dispatch.ts: re-reckons sovereignty if the snapshot on the
  intent is older than SOVEREIGNTY_FRESHNESS_MS (default 5 min); writes the
  cc_actions_log row populated with intent_id, attempt, idempotency_key.
- migrations/0004_chief_skin.sql: additive columns on cc_actions_log
  (intent_id uuid FK ON DELETE SET NULL, attempt int default 1,
  idempotency_key text) + partial unique index (intent_id, idempotency_key)
  WHERE both NOT NULL + index (intent_id, executed_at DESC).
- src/agents/tools/actions.ts: update_obligation_status now delegates to the
  registry's pure runner. ActionAgent chat path unchanged behaviorally.
- src/routes/meta.ts: POST /api/v1/intents/:id/execute.
- tests/meta/executor.spec.ts: real-Neon round trip — claim, dispatch,
  audit row, replay-is-noop. Skips without DATABASE_URL.
- docs/architecture/ADR-001: Consequences §2 amended per chittycanon and
  chittyschema review verdicts.

Reviewer verdicts encoded:
  schema-overlord: additive columns on cc_actions_log (no new table).
  canon-cardinal: Option 2 (sibling consumers of registry) is canonical.
  ecosystem: Intent in meta/intent.ts is already a dispatcher; register
  handlers per intent_type, no new ActionSpec abstraction.

Validated on Neon branch br-restless-credit-akyt3mak: migration applied,
columns + indexes confirmed, integration test green (real SQL).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…icals; importants deferred)

FIX 1 (cascade-risk critical): dispatch's replay short-circuit matched on
intent_id alone, ORDER BY executed_at DESC LIMIT 1. Any intent that ever
produced a terminal cc_actions_log row would short-circuit forever, no
matter what idempotency_key the new attempt computed — making per-attempt
retries unreachable. Reordered to (a) compute attempt+key first, then
(b) lookup by (intent_id, idempotency_key) which is the canonical
per-attempt invariant the partial unique index backs.

FIX 2 (cascade-risk critical): both refusal paths in dispatch wrote audit
+ called failIntent, then returned without `replayed: true`, so
executeIntent's `!result.replayed` guard called failIntent a SECOND time.
The DB no-ops the second call (status guard), but the semantic is wrong
and the error_message could be clobbered on adjacent paths. Both refusal
returns now set replayed:true; executeIntent's guard skips correctly.

Updated existing executor.spec.ts: the second-invocation block encoded
the pre-fix bug (asserted same-intent re-call replays). Under the new
contract, a second executeIntent against the same intent is a new
attempt with a new key → executor re-runs (idempotent for
update-obligation-status), audit count goes 1→2, attempt sequence [1,2].

Added tests/meta/executor-pr106-criticals.spec.ts:
- FIX 1 regression: hand-insert a terminal row with a sentinel key
  ('a'*64) for an intent, then executeIntent — key mismatch must NOT
  short-circuit, executor must run, obligation must transition.
- FIX 2 regression: stale snapshot + unroutable CHITTYTRUST_URL (RFC 5737
  192.0.2.1) forces real fetch failure → sovereignty 'blocked' → refusal
  path. Asserts result.replayed === true (the canonical FIX 2 signal)
  and exactly one sovereignty_refusal audit row.

No mocks. Real Neon only — skipped without DATABASE_URL.

Validated the FIX 1 SELECT shape against a real Neon branch
(cool-bar-13270800 / pr-106-criticals-validation, since deleted): the
key-matched query returned the seeded row for matching key and empty
for non-matching key.

Deferred to follow-up PR (intentionally out of scope here):
  - FIX 3: race-safety on concurrent dispatchers (writeAuditRowOrReplay)
  - FIX 4: schema predicate gap (status enum vs literal IN-clause)
  - FIX 5: cold-start registry import on direct dispatch entry

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…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>
…l consumer (#109)

* feat(daemon): loop body wires executeIntent end-to-end (claim → dispatch → 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>

* fix(health,wrangler): real-dependency /health probe + chittytrack tail consumer

The previous /health returned a static {status:"ok",...} regardless of
whether the worker could reach its real dependencies — a direct violation
of the chittyentity CLAUDE.md "No Mocks, Fake Data, or Placeholder
Endpoints" binding rule ("every endpoint must return real results against
a real datastore on the day it is committed").

This change replaces /health with a real probe that executes against the
worker's actual dependencies:

  * db            — SELECT 1 via Neon HTTP driver. Critical: failure -> 503.
  * chittyconnect — GET ${CHITTYCONNECT_URL}/health. Degraded if unreachable.
  * daemon        — newest cc_node_leases.heartbeat_at, stale if older than
                    2x daemon/loop.ts default heartbeatMs (10000ms ->
                    20000ms cutoff). Missing table -> not_provisioned
                    (degraded, NOT down) so deploys against bases without
                    #101 don't 503.

Per-dep timeout 2000ms; total probe bounded ≤ 5000ms via Promise.all.
Runs unauthenticated (no auth middleware on /health).

The probe handler is extracted to src/routes/health.ts so it can be
integration-tested in pure Node — importing src/index.ts directly drags
in `cloudflare:` modules (Agents SDK / DOs) that only resolve under
workerd. Integration test exercises the handler against a real Neon
branch (no mocks), validates DB probe ok and shape of all three probes,
and asserts 503 + status=down when DB is unreachable.

wrangler.jsonc already declares `tail_consumers: [{service: chittytrack}]`
(present on the base branch) — no change required there.

Verified against Neon branch br-spring-queen-akggkmso of project
cool-bar-13270800 (ChittyCommand). Sample real response:

{
  "status": "degraded",
  "service": "chittycommand",
  "version": "0.1.0",
  "timestamp": "2026-06-04T05:27:16.111Z",
  "probes": {
    "db": { "status": "ok", "latency_ms": 226 },
    "chittyconnect": {
      "status": "degraded", "latency_ms": 0,
      "error": "CHITTYCONNECT_URL not configured"
    },
    "daemon": {
      "status": "not_provisioned", "newest_heartbeat_age_ms": null,
      "error": "relation \"cc_node_leases\" does not exist"
    }
  }
}

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

* fix(health): query active leases by heartbeat_at, not released_at

Addresses Codex P2 on PR #109. The cc_node_leases schema in
src/db/schema.ts has no released_at column — release is represented by
NULLing heartbeat_at/lease_expires_at in daemon/leader.ts. The previous
WHERE released_at IS NULL clause raised 'column released_at does not
exist', which the catch block then mis-classified as 'not_provisioned',
so /health would never report ok/stale based on real heartbeats.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: chitcommit <noreply@chitty.cc>
…rface (#110)

* docs(charter,chitty,claude): reclassify Tier 5 → Tier 2 platform with Tier-5 surface; add meta endpoints and dependencies

Stacked follow-on to PR #106 (ADR-001 + executor registry). Documentation-only
PR that brings the compliance triad and `/api/v1/status` in line with the
ADR-001 amendment that landed on `feat/meta-executors-registry`.

CHARTER.md / CHITTY.md / CLAUDE.md
- Canonical phrasing: "Tier 2 (Platform) with Tier-5 dashboard surface"
  everywhere a tier is declared.
- RY (Authority) row rewritten to reflect sovereignty enforcement + intent
  execution + multi-source ingest. No longer a pure consumer.
- API contract gains the one real new endpoint introduced by #106:
  POST /api/v1/intents/:id/execute. Per the global no-fake-endpoints rule,
  the planned intent CRUD (POST /api/v1/intents, GET .../:id, GET ...,
  GET /api/v1/executors) is NOT documented here because it isn't implemented;
  it will be added when the routes are.
- New Executor Registry table with canonical URI
  chittycanon://core/services/chittycommand/executors/{intent_type}. Lists
  the one executor that actually self-registers today
  (update_obligation_status). mercury_payment is flagged as a tracked future
  executor with REAL-MONEY constraints (fresh autonomous sovereignty,
  USD 500 per-intent cap) and explicitly NOT documented as registered.
- New Cluster Runtime section: daemon is launchd/systemd, NOT a Worker;
  per-node L-type ChittyIDs register as sub-channels via
  agent.chitty.cc/api/v1/channels, NOT in the main ChittyRegister payload.
- Dependencies expanded: ChittyTrust, ChittyID, chittyagent-orchestrator,
  chittyagent-tasks, chittyagent-ch1tty added. ChittyConnect entry expanded
  to call out ContextConsciousness + MemoryCloude (forever context) and
  sensitive-intent secret brokerage.
- Compliance section flags the ChittyID re-mint as required operator
  action (T → P-Synthetic) and explicitly defers /health real-probes and
  tail_consumers wiring to separate PRs.
- MCP tool count reconciled to 50 (CLAUDE.md was already correct; CHARTER/
  CHITTY updated from stale "48").

src/index.ts
- /api/v1/status returns tier: 2 plus tierSurface phrasing and a
  meta.endpoints array listing the registered intent-execute route.
  No other route, middleware, or handler touched.

src/routes/meta.ts
- /api/v1/canon returns tier: 2 + tierSurface so the public canon view
  matches /status and the docs. Same handler signature; no behavior change.

Not in this PR (deferred):
- Real-dependency /health probes (separate PR).
- tail_consumers wiring to chittytrack (separate observability PR).
- ChittyRegister payload submission (operator action; blocked on re-mint).
- Re-mint of service ChittyID as P-Synthetic (operator action).
- Intent CRUD endpoints and /api/v1/executors enumeration (future PRs).

Compliance coverage:
- Addresses: tier reclassification, RY language, meta endpoint surface,
  executor URI registry, dependency expansion, cluster sub-channel
  declaration (items 1, 4, 5, 7, 8 of the registration-readiness audit).
- Remaining (out of scope): registration submission (item 2, operational),
  real /health (item 3, separate PR), tail_consumers (item 6, separate PR),
  P-type ChittyID re-mint (item 9, operator action).

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

* docs(charter): clarify Worker vs daemon runtime split in Classification

Addresses Codex P2 on PR #110: the previous wording said the Tier-2
platform and Tier-5 surface 'both run from the same worker', which
contradicts the Cluster Runtime section stating daemon/ runs as a
supervised launchd/systemd process, not as a Worker.

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

* docs(registration): draft chittycommand Tier-2 registration payload + submission runbook (#111)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: chitcommit <noreply@chitty.cc>
…after rebase

Resolves the 0004 collision with main's 0004_premium_toad_men (Roux
privilege/space). drizzle db:generate regenerated as 0005_sour_dreadnoughts
(statement-identical to chief_skin) with a cumulative snapshot, so prod
drizzle-kit migrate (no already-exists tolerance) stays clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chitcommit chitcommit force-pushed the feat/meta-executors-registry branch from 747a995 to fc60bfc Compare June 10, 2026 11:48
@chitcommit chitcommit merged commit 6a26e66 into main Jun 10, 2026
11 of 12 checks passed
@chitcommit chitcommit deleted the feat/meta-executors-registry branch June 10, 2026 11:48
@github-actions

Copy link
Copy Markdown
  1. @coderabbitai review
  2. @copilot review
  3. @codex review
  4. @claude review
    Adversarial review request: evaluate security, policy bypass paths, regression risk, and merge-gating bypass attempts.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

chitcommit added a commit that referenced this pull request Jun 10, 2026
…mpotency

REAL MONEY PATH. Stacked on #106 (feat/meta-executors-registry).

Adds `meta/executors/mercury-payment.ts` — concrete executor for the
mercury_payment intent type, plus a shared pure runner that ActionAgent's
chat tool (`execute_payment`) now delegates to so chat + autonomous
surfaces share one implementation.

Refusal gates (executor returns ok=false, dispatcher writes a
'payment_refusal' row to cc_actions_log; Mercury is NOT called):

- sovereignty_not_autonomous — decision !== 'autonomous'
- sovereignty_stale — snapshot older than MERCURY_SOVEREIGNTY_FRESHNESS_MS
  (60s, tighter than dispatch's default 5min)
- amount_cap_exceeded — payload.amount_cents > MERCURY_AUTONOMOUS_AMOUNT_CAP_USD * 100
- invalid_payload — zod schema rejected the intent payload
- missing_token — KV mercury:token:{account_slug} absent
- mercury_api_failure — Mercury HTTP returned null

Idempotency uses the dispatcher's content-addressable key
(sha256(intent.id:attempt:intent_type)) as supplied via ctx.idempotencyKey.
Because intent.id is immutable and attempt is deterministic from prior
audit rows, this is functionally equivalent to a payload-derived key for
replay protection. The Mercury client receives the same key as its own
idempotencyKey so Mercury de-dupes on it too.

Files:
- meta/executors/mercury-payment.ts (new)
- meta/executors/index.ts (barrel: side-effect import)
- src/agents/tools/actions.ts (execute_payment delegates to runMercuryPayment)
- src/index.ts (Env: MERCURY_AUTONOMOUS_AMOUNT_CAP_USD)
- tests/meta/executors/mercury-payment.spec.ts (3 DB-only refusal cases)
- docs/runbooks/mercury-payment-executor.md (operator runbook)

Does NOT modify meta/executors/{dispatch,registry,types}.ts from #106.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chitcommit added a commit that referenced this pull request Jun 10, 2026
… build

Replaying #105 onto current main (executor registry from #106) broke the
daemon build two ways. Fix both so #105 ships a daemon that builds on the VM.

1. entrypoint.ts passed an `executor` callback to runLeaderLoop — removed in
   #106 when the loop began dispatching through the canonical executor registry
   (executeIntent). Drop the stub: an empty registry routes every claimed
   intent through dispatch's "no executor registered" throw → failIntent →
   `failed`, never silent `done`, preserving the PR #105 Codex-P1 safety
   property without a callback.

2. The executor registry (meta/executors/{types,dispatch,update-obligation-
   status}.ts) hard-imported the Workers `Env` from src/index, neither available
   nor compilable in the daemon's NodeNext + node-types build. Per ADR-001 the
   registry is consumed by BOTH the Worker and the daemon, so it must not depend
   on Workers-only types. Introduce a minimal structural
   `ExecutorEnv { DATABASE_URL?, HYPERDRIVE? }` (the only env the registry reads;
   getSql already cast to exactly this). Worker `Env` stays structurally
   assignable — ActionAgent callers unchanged. No index signature: keeps
   typo-safety on env reads for the real-money mercury path.

   Also add the explicit `/index.js` extension to meta/intent.ts's dynamic
   `import('./executors')` (required under NodeNext; accepted by the Worker's
   Bundler resolution).

Verified: `npm run typecheck` (Worker, Bundler) and `npm run build:daemon`
(NodeNext) both exit 0.

Follow-up (needs `workflow` scope, separate push): add
`tsc -p daemon/runtime/tsconfig.daemon.json --noEmit` as a CI step so the
daemon build is gated — catches the #108 ripple (mercury also imports Workers
`Env`) at PR time instead of VM install.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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