Skip to content

feat(entitlements): move daily quotas into UserMeter - #1115

Merged
kody-bot merged 15 commits into
mainfrom
cursor/meter-do-38c8
Aug 1, 2026
Merged

kody-bot merged 15 commits into
mainfrom
cursor/meter-do-38c8

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Aug 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add a per-user SQLite UserMeter Durable Object as the authoritative daily quota coordinator
  • cold-bootstrap each user/resource/day from D1, then keep the warm enforcement path off awaited D1
  • preserve existing reporting through a revision-ordered, non-awaited D1 mirror
  • move account/email/admin point reads to UserMeter and preserve inbound delivery retry idempotency
  • inventory UserMeter for account deletion and paged export
  • compose with RunLog as the separate authority for atomic concurrent_workflows reservations and projections

Validation

Deployment notes

This is the expand deployment. entitlement_daily_counters remains in D1 and is mirrored for reporting compatibility. Mirror/table retirement is deferred until production parity verification. RunLog remains authoritative for concurrent_workflows; UserMeter does not absorb workflow reservations.

System recap — adds User meter (high risk)

Mode: recap · Base: main @ f4115ea6 · Head: edfd973a

Classification: adds — introduces a per-user SQLite Durable Object and moves daily entitlement authority from shared D1 to UserMeter while composing with RunLog workflow reservations.

Primitives touched

Primitive Group Impact
user-meter Storage & infrastructure adds — per-user daily counters and inbound idempotency
entitlements Identity & auth extends — UserMeter daily authority + RunLog workflow authority routing
run-log Storage & infrastructure composes — concurrent-workflow reservations and projections remain authoritative
email User-facing assistant primitives extends — retry-safe inbound claims and DO usage reads
mcp-server Entry points composes — execute/fetch quota calls route to UserMeter
account-export User-facing assistant primitives extends — preserves RunLog phases and adds UserMeter export
app-ui Entry points extends — authoritative usage reads and deletion purge

System map

Daily quota paths coordinate through UserMeter; concurrent workflows reserve through RunLog; D1 receives compatibility mirrors only.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	callers["mcp-server<br/>MCP endpoint (/mcp)"]:::touched
	entitlements["entitlements<br/>Plans & entitlements"]:::extended
	userMeter["user-meter<br/>User meter"]:::added
	runLog["run-log<br/>Run log"]:::touched
	d1AppDb["d1-app-db<br/>D1 app database"]:::untouched
	callers -->|"daily execute/fetch consume"| entitlements
	entitlements -->|"daily resource RPC"| userMeter
	entitlements -->|"concurrent_workflows count/reserve"| runLog
	userMeter -->|"revision-ordered async daily mirror"| d1AppDb
	runLog -->|"async workflow projection mirror"| d1AppDb
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Invariants

  • every Durable Object id is derived from the acting stable userId
  • daily check/increment is serialized in UserMeter
  • concurrent-workflow reservation/count is atomic in RunLog
  • inbound delivery claim and increment commit in one UserMeter SQLite transaction
  • no D1 table or column is dropped in this expand deployment

Conductor report

  • STATUS: done
  • What shipped: UserMeter daily-counter authority, DO-backed usage reads, retry-safe inbound charging, account purge/export inventory, and the merged authority seam where RunLog owns atomic concurrent_workflows reservations/projections.
  • Risk: high — merged under Kent's explicit one-PR authority after local, reviewer, and CI validation.
  • Merged/deployed: yes / yes; PR #1115, post-merge validation, and production deploy all completed successfully.
  • Sibling-track spill: preserved runlog-consolidation semantics from f4115ea6; no mailbox-do or cron-restructure changes were made.
  • Dependency: D1 mirror and entitlement_daily_counters retirement remain gated on production parity verification.
  • Remaining track roadmap: slice 3 storage accounting, slice 4 service liveness, then slice 5 deletion fencing as separate PRs. Slice 3 will not change scheduled-lane wiring owned by cron-restructure; mailbox-do owns all unrelated email/ work.
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Added per-account tracking for daily email, fetch, and entitlement usage.
    • Usage views and administrative tools now show authoritative current consumption.
    • Account exports include daily usage counters with pagination and truncation warnings.
  • Bug Fixes
    • Inbound delivery retries no longer duplicate quota charges.
    • Account deletion now removes associated usage data and reports cleanup issues for retry.
  • Documentation
    • Updated architecture and export documentation for usage tracking, retention, and account cleanup.

cursoragent and others added 5 commits July 31, 2026 22:46
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds a per-user UserMeter Durable Object for daily entitlement counters and inbound delivery claims. Daily usage reads and mutations use UserMeter, with D1 bootstrap and asynchronous mirroring. Account export, deletion, runtime bindings, tests, and architecture documentation now include UserMeter.

Changes

UserMeter entitlement migration

Layer / File(s) Summary
UserMeter storage and runtime registration
packages/worker/src/entitlements/user-meter-do.ts, packages/worker/src/entitlements/user-meter-client.ts, packages/worker/src/user-scoped-durable-object-name.ts, packages/worker/wrangler.jsonc, packages/worker/src/index.ts, packages/worker/worker-configuration.d.ts
Adds SQLite counters, delivery claims, retention, atomic operations, refunds, purge, pagination, typed RPC access, naming, and runtime bindings.
Authoritative entitlement service
packages/worker/src/entitlements/service.ts, packages/worker/src/admin/user-usage-data.ts, packages/worker/src/app/account-usage-data.ts, packages/worker/src/app/account-email-data.ts, packages/worker/src/mcp/capabilities/email/email-usage-get.ts
Routes daily usage and mutations through UserMeter. Uses D1 for cold bootstrap and revision-ordered asynchronous mirrors. Uses RunLog projections for concurrent workflows.
Inbound delivery and quota integrations
packages/worker/src/email/inbound-delivery.ts, packages/worker/src/email/inbound.ts, packages/worker/src/email/outbound.ts, packages/worker/src/mcp/fetch-gateway.ts, packages/worker/src/mcp/tools/execute.ts
Uses UserMeter for idempotent inbound charging and daily outbound quota enforcement. Passes waitUntil for mirror work.
Account export and deletion surfaces
packages/worker/src/account/export.ts, packages/worker/src/app/account-deletion.ts, packages/worker/src/account/user-owned-surfaces.ts
Adds UserMeter counters to exports and manifests, supports paginated section reads, purges meters during deletion, and registers the user-owned surface.
Validation, test support, and documentation
packages/worker/src/test-support/*, packages/worker/src/**/*test.ts, docs/contributing/architecture/*
Adds in-memory UserMeter and RunLog environments. Covers bootstrap, mirroring, concurrency, replay, refunds, exports, usage precedence, gateway quotas, and deletion. Documents the storage and migration model.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the PR's main change: moving daily entitlement quotas into the UserMeter Durable Object.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/meter-do-38c8

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.

cursoragent and others added 6 commits August 1, 2026 00:23
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts:
#	packages/worker/src/email/inbound.ts
#	packages/worker/src/email/outbound.ts
#	packages/worker/worker-configuration.d.ts

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot marked this pull request as ready for review August 1, 2026 01:16
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review

@github-actions

github-actions Bot commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-1115.kody-a99.workers.dev

Worker: kody-pr-1115
D1: kody-pr-1115-db
KV: kody-pr-1115-oauth-kv

Mocks:

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/contributing/architecture/entitlements.md (1)

1-1: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Broken anchor #user-meter-expand-phase used in five places. The heading ## UserMeter (expand phase) slugifies to usermeter-expand-phase under the GitHub/markdownlint heading algorithm (lowercase, strip punctuation including parentheses, then replace spaces with hyphens). Every link below uses user-meter-expand-phase instead, which markdownlint-cli2's MD051 rule flags as invalid on the entitlements.md occurrence.

  • docs/contributing/architecture/entitlements.md#L110-113: change the self-referencing link [UserMeter (expand phase)](#user-meter-expand-phase) to #usermeter-expand-phase.
  • docs/contributing/architecture/data-storage.md#L194-198: change [Entitlements](./entitlements.md#user-meter-expand-phase) to ./entitlements.md#usermeter-expand-phase.
  • docs/contributing/architecture/data-storage.md#L511-519: change See [Entitlements](./entitlements.md#user-meter-expand-phase). to ./entitlements.md#usermeter-expand-phase.
  • docs/contributing/architecture/data-storage.md#L562-564: change See [Entitlements](./entitlements.md#user-meter-expand-phase). to ./entitlements.md#usermeter-expand-phase.
  • docs/contributing/architecture/data-storage.md#L630-631: change [Entitlements](./entitlements.md#user-meter-expand-phase)) to ./entitlements.md#usermeter-expand-phase).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contributing/architecture/entitlements.md` at line 1, Update every
reference to the UserMeter expand-phase heading to use the valid anchor
`#usermeter-expand-phase`, including the self-link in entitlements.md and all four
links in data-storage.md. Preserve the existing link text and correct the extra
closing parenthesis in the final reference.

Source: Linters/SAST tools

🧹 Nitpick comments (8)
packages/worker/src/email/inbound-delivery.ts (1)

455-495: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the shared bootstrap helper instead of repeating the protocol.

readUserInboundReceiveCount (Lines 471-494) and chargeUserInboundDeliveryOnce (Lines 550-575) repeat the same four-step bootstrap protocol: read D1 baseline, call initialize, retry the meter call, then throw when the outcome is still needs_bootstrap. packages/worker/src/entitlements/service.ts already contains ensureUserMeterCounterInitialized for exactly this step. Extract one local helper, or export and reuse the service helper, so the bootstrap contract has a single definition.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/inbound-delivery.ts` around lines 455 - 495, Reuse
the existing ensureUserMeterCounterInitialized helper from the entitlements
service in readUserInboundReceiveCount and chargeUserInboundDeliveryOnce,
replacing their duplicated baseline-read, initialize, retry, and needs_bootstrap
error handling. Preserve each function’s existing meter resource, day,
timestamp, and return behavior while centralizing the bootstrap contract in that
shared helper.
packages/worker/src/mcp/tools/execute.ts (1)

265-269: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the waitUntil local already bound at Line 161.

Line 269 calls agent.waitUntil?.bind(agent) again. The enclosing scope already holds the identical bound callback in the waitUntil local, which runExecuteTool closes over and passes to runModuleWithRegistry at Line 358. Use that local for one binding site per call.

♻️ Proposed change
 					await consumeDailyEntitlement({
 						db: env.APP_DB,
 						env,
 						userId: callerContext.user.userId,
 						email: callerContext.user.email,
 						resource: 'execute_calls_per_day',
-						waitUntil: agent.waitUntil?.bind(agent),
+						waitUntil,
 					})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/tools/execute.ts` around lines 265 - 269, The execute
tool configuration redundantly rebinds agent.waitUntil instead of reusing the
existing bound callback. In the resource object near the execute call, replace
the agent.waitUntil?.bind(agent) expression with the enclosing waitUntil local
already created and used by runExecuteTool/runModuleWithRegistry, preserving a
single binding per call.
packages/worker/src/mcp/fetch-gateway.ts (1)

132-139: 🚀 Performance & Scalability | 🔵 Trivial

Plan for per-user meter serialization on this hot path.

Every sandbox fetch now performs a blocking RPC to the single per-user UserMeter Durable Object before the outbound request runs. All concurrent fetches for one user serialize on that object, and its region fixes the added latency. Add a metric for consumeDailyEntitlement duration on this path, and an alert on its tail latency, so a hot user or a distant DO region is visible before it degrades sandbox fetches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/fetch-gateway.ts` around lines 132 - 139, Instrument
the consumeDailyEntitlement call in the sandbox fetch path with a duration
metric, recording latency for every invocation including failures. Use the
project’s existing metrics conventions and add a tail-latency alert for this
metric, with thresholds appropriate to detect serialized per-user Durable Object
or regional latency before fetches degrade.
packages/worker/src/email/inbound-entitlements.workers.test.ts (1)

74-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Three test files reimplement the same UserMeter daily_counters seeding. The shared root cause is that packages/worker/src/test-support/user-meter.ts exposes no counter-seed helper, so each suite writes the Durable Object internal SQLite schema itself. Every copy repeats the same runInDurableObject warm-up read, the same INSERT ... ON CONFLICT upsert, and the same hardcoded revision = 1. A change to the DO table layout then breaks three files.

  • packages/worker/src/email/inbound-entitlements.workers.test.ts#L74-L105: replace the inline block with a call to a new seedUserMeterDailyCounter({ userId, resource: 'email_receives_per_day', day, count }) helper in packages/worker/src/test-support/user-meter.ts, and keep the extra D1 mirror write here.
  • packages/worker/src/email/outbound.workers.test.ts#L83-L104: call the same helper with resource: 'email_sends_per_day' and delete the local block.
  • packages/worker/src/mcp/fetch-gateway.workers.test.ts#L16-L38: call the same helper with resource: 'outbound_fetches_per_day' and delete the local block.

In the shared helper, preserve or increment the existing revision instead of forcing 1, so seeding never moves a counter revision backwards.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/email/inbound-entitlements.workers.test.ts` around lines
74 - 105, The three test files duplicate UserMeter daily-counter seeding and
hardcode revision 1. In packages/worker/src/test-support/user-meter.ts, add
seedUserMeterDailyCounter that performs the existing Durable Object
initialization and upsert while preserving or incrementing the current revision.
Replace the inline blocks in
packages/worker/src/email/inbound-entitlements.workers.test.ts:74-105,
packages/worker/src/email/outbound.workers.test.ts:83-104, and
packages/worker/src/mcp/fetch-gateway.workers.test.ts:16-38 with calls using
their respective resources; retain the inbound test’s additional D1 mirror
write.
packages/worker/src/app/account-usage-data.node.test.ts (1)

85-207: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider splitting the three scenarios into separate tests.

This test covers three independent scenarios: baseline with no counters, D1 bootstrap, and a warm meter that overrides D1. A failure in the first block hides the precedence coverage in the third block, which is the most important assertion for this migration.

Two smaller points on the baseline block. expect(baseline?.entitlementConsumption.length).toBeGreaterThan(5) is loose; accountUsageEntitlementResources declares 13 entries, so an exact count would catch accidental list edits. The baseline block also asserts no daily resource value, so the cold-and-empty path returns zero only implicitly.

♻️ Suggested tightening for the baseline assertions
 	expect(currentFor(baseline, 'saved_packages')).toBe(2)
-	expect(baseline?.entitlementConsumption.length).toBeGreaterThan(5)
+	expect(baseline?.entitlementConsumption).toHaveLength(13)
+	expect(currentFor(baseline, 'email_sends_per_day')).toBe(0)
+	expect(currentFor(baseline, 'email_receives_per_day')).toBe(0)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/app/account-usage-data.node.test.ts` around lines 85 -
207, Split loadAccountUsageData coverage into three independent tests for the
empty baseline, D1 bootstrap, and warm UserMeter precedence scenarios so
failures do not mask later assertions. In the baseline test, assert
entitlementConsumption has exactly the 13 resources declared by
accountUsageEntitlementResources and explicitly verify the daily resource value
is zero. Preserve the existing assertions for plan, date, saved packages,
bootstrap counts, and authoritative warm-meter counts.
packages/worker/src/test-support/user-meter.ts (1)

112-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

exportCounters ignores pageSize and startAfter.

The real UserMeter.exportCounters accepts { pageSize?, startAfter? } and returns a bounded, cursor-paginated page (see packages/worker/src/entitlements/user-meter-do.ts exportCounters). This stub's exportCounters() takes no parameters and always returns every row in one un-paginated response.

TypeScript's structural typing lets this zero-arg function satisfy the UserMeterEnv interface, so no compile error occurs. Any future test that reuses this shared stub to exercise export pagination or truncation will silently get the full list instead of a page, masking bugs in pagination logic.

Accept pageSize and startAfter, and slice/paginate the in-memory rows to mirror the real contract.

♻️ Proposed fix to honor pagination parameters
-			async exportCounters() {
-				const counters = [...rows.entries()].map(([entryKey, row]) => {
+			async exportCounters(input: { pageSize?: number; startAfter?: string | null } = {}) {
+				const pageSize = input.pageSize ?? 100
+				const sorted = [...rows.entries()]
+					.map(([entryKey, row]) => {
+						const [resource, day] = entryKey.split('\0')
+						return { resource: resource as DailyEntitlementResource, day: day!, row }
+					})
+					.sort((a, b) => a.day.localeCompare(b.day) || a.resource.localeCompare(b.resource))
+				const startIndex = input.startAfter
+					? sorted.findIndex((entry) => `${entry.day}:${entry.resource}` === input.startAfter) + 1
+					: 0
+				const page = sorted.slice(startIndex, startIndex + pageSize)
+				const truncated = startIndex + pageSize < sorted.length
+				const counters = page.map(({ resource, day, row }) => {
					return {
						resource: resource as DailyEntitlementResource,
						day: day!,
						count: row.count,
						revision: row.revision,
						updatedAt: new Date().toISOString(),
						mirrorUpdatedAt: userMeterMirrorUpdatedAtToken(row.revision),
					}
				})
-				return { counters, nextStartAfter: null, truncated: false }
+				const last = page.at(-1)
+				return {
+					counters,
+					nextStartAfter: truncated && last ? `${last.day}:${last.resource}` : null,
+					truncated,
+				}
			},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/test-support/user-meter.ts` around lines 112 - 125,
Update exportCounters in the user-meter test stub to accept the real contract’s
optional pageSize and startAfter parameters. Order the in-memory counter rows
consistently, resume after the provided cursor, limit the result to pageSize,
and return the correct nextStartAfter and truncated values while preserving the
existing counter mapping.
packages/worker/src/mcp/tools/execute.node.test.ts (1)

65-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring describes the pre-UserMeter enforcement mechanism.

This comment says the execute entitlement "issues one conditional upsert (allowed when meta.changes > 0)". That describes the old D1-based conditional-upsert enforcement. Per the entitlements docs in this PR, execute-call enforcement now goes through UserMeter.consumeDailyEntitlement, which performs an atomic check-and-increment inside the Durable Object with revision-checked updates, not a D1 conditional upsert.

Update the comment to describe the current UserMeter-based enforcement so it does not mislead future readers of this test file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/tools/execute.node.test.ts` around lines 65 - 70,
Update the docstring above the minimal environment stub to describe the current
UserMeter-based execute entitlement flow: the handler calls
UserMeter.consumeDailyEntitlement, which performs an atomic check-and-increment
using revision-checked Durable Object updates. Remove the outdated reference to
conditional D1 upserts while preserving the note that plan lookup does not
access D1 for caller contexts without an account email.
docs/contributing/architecture/primitives.yaml (1)

643-643: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clarify the invariant reference against the documented D1 mirror write.

The no-per-event-shared-writes invariant states per-event writes go "to Analytics Engine or per-user DO SQLite, never the shared D1 writer." entitlements.md (lines 134-141 in this PR) documents a best-effort, non-awaited mirror write to the shared D1 table entitlement_daily_counters after every consume, refund, and inbound claim.

The authoritative write does land in the per-user UserMeter DO, so the invariant's intent holds, but the compatibility mirror is still a per-event write to the shared D1 writer. Consider a one-line caveat in the invariant summary noting that expand-phase best-effort D1 mirrors are a documented, time-bounded exception, so future readers don't assume this doc reference implies zero D1 writes per event.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contributing/architecture/primitives.yaml` at line 643, Update the
no-per-event-shared-writes invariant summary in primitives.yaml to add a concise
caveat that documented, time-bounded, best-effort expand-phase mirrors to the
shared D1 writer are an exception. Preserve the invariant’s statement that
authoritative per-event writes use Analytics Engine or per-user DO SQLite, and
align the wording with the documented mirror behavior in entitlements.md.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/contributing/architecture/entitlements.md`:
- Around line 110-113: Replace every reference to the invalid
`#user-meter-expand-phase` anchor with `#usermeter-expand-phase`, including the link
in the entitlements documentation and all occurrences in data-storage.md. Keep
the existing UserMeter (expand phase) heading unchanged.

In `@packages/worker/src/app/handlers/account-email.node.test.ts`:
- Around line 301-324: Freeze the UTC system time in both date-scoped tests
before deriving or seeding date-based data, and restore real timers after each
test. Apply this to packages/worker/src/app/handlers/account-email.node.test.ts
lines 301-324 and 454-503; update the tests around their existing test bodies
and utcDayKey usage, with no direct changes needed elsewhere.

In `@packages/worker/src/email/inbound-entitlements.workers.test.ts`:
- Around line 207-242: Update withFailingReceiveStartedInsert so its patched
prepare returns the original statement immediately when isDeliveryInsert is
false, preserving normal run, first, all, raw, and batch behavior for unrelated
queries. Keep the existing bind wrapper and failure injection only for the
targeted receive_started delivery insert.

In `@packages/worker/src/email/inbound.workers.test.ts`:
- Around line 866-871: Update readUserTotalReceiveCount to follow the
exportCounters pagination cursor: repeatedly call userMeterRpc with the returned
nextStartAfter while truncated is true, accumulate all pages, then filter
email_receives_per_day rows and sum their counts.

In `@packages/worker/src/entitlements/user-meter-do.ts`:
- Around line 555-559: Update the purge method to execute the storage deletion
and initializeSchema calls inside this.ctx.blockConcurrencyWhile, ensuring no
other RPC can observe the schema between those operations; return { ok: true }
after the concurrency-blocked callback completes.

In `@packages/worker/src/mcp/capabilities/email/email-usage-get.workers.test.ts`:
- Around line 137-144: Compute const day = utcDayKey() once at the beginning of
the test body, pass that day value to every seedDailyCounter call, and reuse it
for all meter reads and response assertions. Remove the later day declaration
and replace any inline utcDayKey() calls so every seeded row and lookup uses the
same key.

In `@packages/worker/src/mcp/fetch-gateway.workers.test.ts`:
- Around line 60-91: Update the test’s deferred-task handling around
executeGatewayFetch to reuse createWaitUntilDrain from the user-meter test
support, replacing the one-time Promise.all wait with the helper’s drain flow.
Ensure drain() runs after all allowed and denied gateway calls so every promise
added to waitUntilTasks settles before the test completes.

---

Outside diff comments:
In `@docs/contributing/architecture/entitlements.md`:
- Line 1: Update every reference to the UserMeter expand-phase heading to use
the valid anchor `#usermeter-expand-phase`, including the self-link in
entitlements.md and all four links in data-storage.md. Preserve the existing
link text and correct the extra closing parenthesis in the final reference.

---

Nitpick comments:
In `@docs/contributing/architecture/primitives.yaml`:
- Line 643: Update the no-per-event-shared-writes invariant summary in
primitives.yaml to add a concise caveat that documented, time-bounded,
best-effort expand-phase mirrors to the shared D1 writer are an exception.
Preserve the invariant’s statement that authoritative per-event writes use
Analytics Engine or per-user DO SQLite, and align the wording with the
documented mirror behavior in entitlements.md.

In `@packages/worker/src/app/account-usage-data.node.test.ts`:
- Around line 85-207: Split loadAccountUsageData coverage into three independent
tests for the empty baseline, D1 bootstrap, and warm UserMeter precedence
scenarios so failures do not mask later assertions. In the baseline test, assert
entitlementConsumption has exactly the 13 resources declared by
accountUsageEntitlementResources and explicitly verify the daily resource value
is zero. Preserve the existing assertions for plan, date, saved packages,
bootstrap counts, and authoritative warm-meter counts.

In `@packages/worker/src/email/inbound-delivery.ts`:
- Around line 455-495: Reuse the existing ensureUserMeterCounterInitialized
helper from the entitlements service in readUserInboundReceiveCount and
chargeUserInboundDeliveryOnce, replacing their duplicated baseline-read,
initialize, retry, and needs_bootstrap error handling. Preserve each function’s
existing meter resource, day, timestamp, and return behavior while centralizing
the bootstrap contract in that shared helper.

In `@packages/worker/src/email/inbound-entitlements.workers.test.ts`:
- Around line 74-105: The three test files duplicate UserMeter daily-counter
seeding and hardcode revision 1. In
packages/worker/src/test-support/user-meter.ts, add seedUserMeterDailyCounter
that performs the existing Durable Object initialization and upsert while
preserving or incrementing the current revision. Replace the inline blocks in
packages/worker/src/email/inbound-entitlements.workers.test.ts:74-105,
packages/worker/src/email/outbound.workers.test.ts:83-104, and
packages/worker/src/mcp/fetch-gateway.workers.test.ts:16-38 with calls using
their respective resources; retain the inbound test’s additional D1 mirror
write.

In `@packages/worker/src/mcp/fetch-gateway.ts`:
- Around line 132-139: Instrument the consumeDailyEntitlement call in the
sandbox fetch path with a duration metric, recording latency for every
invocation including failures. Use the project’s existing metrics conventions
and add a tail-latency alert for this metric, with thresholds appropriate to
detect serialized per-user Durable Object or regional latency before fetches
degrade.

In `@packages/worker/src/mcp/tools/execute.node.test.ts`:
- Around line 65-70: Update the docstring above the minimal environment stub to
describe the current UserMeter-based execute entitlement flow: the handler calls
UserMeter.consumeDailyEntitlement, which performs an atomic check-and-increment
using revision-checked Durable Object updates. Remove the outdated reference to
conditional D1 upserts while preserving the note that plan lookup does not
access D1 for caller contexts without an account email.

In `@packages/worker/src/mcp/tools/execute.ts`:
- Around line 265-269: The execute tool configuration redundantly rebinds
agent.waitUntil instead of reusing the existing bound callback. In the resource
object near the execute call, replace the agent.waitUntil?.bind(agent)
expression with the enclosing waitUntil local already created and used by
runExecuteTool/runModuleWithRegistry, preserving a single binding per call.

In `@packages/worker/src/test-support/user-meter.ts`:
- Around line 112-125: Update exportCounters in the user-meter test stub to
accept the real contract’s optional pageSize and startAfter parameters. Order
the in-memory counter rows consistently, resume after the provided cursor, limit
the result to pageSize, and return the correct nextStartAfter and truncated
values while preserving the existing counter mapping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a683222-bbce-437c-9296-214ce4a04601

📥 Commits

Reviewing files that changed from the base of the PR and between 96b41d9 and e6622a4.

📒 Files selected for processing (42)
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/architecture/entitlements.md
  • docs/contributing/architecture/primitives.yaml
  • packages/worker/src/account/export.node.test.ts
  • packages/worker/src/account/export.ts
  • packages/worker/src/account/user-owned-surfaces.node.test.ts
  • packages/worker/src/account/user-owned-surfaces.ts
  • packages/worker/src/admin/user-usage-data.node.test.ts
  • packages/worker/src/admin/user-usage-data.ts
  • packages/worker/src/app/account-deletion.node.test.ts
  • packages/worker/src/app/account-deletion.ts
  • packages/worker/src/app/account-email-data.ts
  • packages/worker/src/app/account-usage-data.node.test.ts
  • packages/worker/src/app/account-usage-data.ts
  • packages/worker/src/app/handlers/account-email.node.test.ts
  • packages/worker/src/email/inbound-delivery.ts
  • packages/worker/src/email/inbound-entitlements.workers.test.ts
  • packages/worker/src/email/inbound.ts
  • packages/worker/src/email/inbound.workers.test.ts
  • packages/worker/src/email/outbound.ts
  • packages/worker/src/email/outbound.workers.test.ts
  • packages/worker/src/entitlements/entitlements.node.test.ts
  • packages/worker/src/entitlements/service.ts
  • packages/worker/src/entitlements/user-meter-client.ts
  • packages/worker/src/entitlements/user-meter-do.ts
  • packages/worker/src/entitlements/user-meter.workers.test.ts
  • packages/worker/src/index.ts
  • packages/worker/src/mcp/capabilities/admin/admin-capabilities.node.test.ts
  • packages/worker/src/mcp/capabilities/email/email-usage-get.ts
  • packages/worker/src/mcp/capabilities/email/email-usage-get.workers.test.ts
  • packages/worker/src/mcp/capabilities/openapi-provider/operation-request.node.test.ts
  • packages/worker/src/mcp/fetch-gateway.node.test.ts
  • packages/worker/src/mcp/fetch-gateway.ts
  • packages/worker/src/mcp/fetch-gateway.workers.test.ts
  • packages/worker/src/mcp/observability.node.test.ts
  • packages/worker/src/mcp/tools/execute.node.test.ts
  • packages/worker/src/mcp/tools/execute.ts
  • packages/worker/src/test-support/user-meter.ts
  • packages/worker/src/user-scoped-durable-object-name.node.test.ts
  • packages/worker/src/user-scoped-durable-object-name.ts
  • packages/worker/worker-configuration.d.ts
  • packages/worker/wrangler.jsonc

Comment thread docs/contributing/architecture/entitlements.md
Comment thread packages/worker/src/app/handlers/account-email.node.test.ts
Comment thread packages/worker/src/email/inbound-entitlements.workers.test.ts
Comment thread packages/worker/src/email/inbound.workers.test.ts
Comment thread packages/worker/src/entitlements/user-meter-do.ts
Comment thread packages/worker/src/mcp/capabilities/email/email-usage-get.workers.test.ts Outdated
Comment thread packages/worker/src/mcp/fetch-gateway.workers.test.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/email/inbound.ts
# Conflicts:
#	docs/contributing/architecture/data-storage.md
#	packages/worker/src/account/export.node.test.ts
#	packages/worker/src/account/export.ts

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/entitlements/service.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/src/test-support/run-log-usage.ts`:
- Around line 11-22: Update the RUN_LOG test stub so idFromName-derived Durable
Object IDs map to independent active workflow counts, and make RUN_LOG.get use
the requested ID when returning countActiveWorkflowProjections. Replace the
global setActiveWorkflowCount behavior with a per-ID test setter that accepts
the Durable Object ID and updates only that entry, preserving count sanitization
for each user.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a62118ec-4116-4a72-bce0-a180c1b8fcd8

📥 Commits

Reviewing files that changed from the base of the PR and between 7628aaf and 468ee51.

📒 Files selected for processing (11)
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/architecture/entitlements.md
  • packages/worker/src/account/export.node.test.ts
  • packages/worker/src/account/export.ts
  • packages/worker/src/account/user-owned-surfaces.node.test.ts
  • packages/worker/src/account/user-owned-surfaces.ts
  • packages/worker/src/admin/user-usage-data.node.test.ts
  • packages/worker/src/app/account-usage-data.node.test.ts
  • packages/worker/src/entitlements/entitlements.node.test.ts
  • packages/worker/src/entitlements/service.ts
  • packages/worker/src/test-support/run-log-usage.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/worker/src/account/user-owned-surfaces.node.test.ts
  • packages/worker/src/account/export.node.test.ts
  • docs/contributing/architecture/entitlements.md
  • packages/worker/src/account/export.ts
  • packages/worker/src/app/account-usage-data.node.test.ts
  • packages/worker/src/account/user-owned-surfaces.ts
  • packages/worker/src/entitlements/entitlements.node.test.ts
  • docs/contributing/architecture/data-storage.md
  • packages/worker/src/admin/user-usage-data.node.test.ts

Comment thread packages/worker/src/test-support/run-log-usage.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a8cba2b. Configure here.

Comment thread packages/worker/src/account/export.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot merged commit 59c6575 into main Aug 1, 2026
10 checks passed
@kody-bot
kody-bot deleted the cursor/meter-do-38c8 branch August 1, 2026 06:04
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.

3 participants