Skip to content

feat(entitlements): make UserMeter service authority - #1148

Merged
cursor[bot] merged 64 commits into
mainfrom
cursor/meter-do-38c8
Aug 2, 2026
Merged

cursor[bot] merged 64 commits into
mainfrom
cursor/meter-do-38c8

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Aug 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • make per-user UserMeter liveness state authoritative for package_services enforcement and usage
  • retain D1 package_service_states as discovery/export/deletion inventory and rollback mirror
  • keep D1-first lifecycle writes plus ordered UserMeter projection
  • make D1-only fresh-running rows explicit parity blockers; ignore stopped historical inventory for liveness authority

Validation

System recap

flowchart LR
  Start[service_start] --> Count[UserMeter running count]
  Count --> Meter[(UserMeter liveness authority)]
  Lifecycle[Start / stop / heartbeat] --> D1[(D1 service inventory)]
  Lifecycle -. ordered projection .-> Meter
  Parity[Admin parity] --> D1
  Parity --> Meter
Loading
Primitive Change Risk
UserMeter Running-count authority with existing 24h freshness and exclusion semantics Medium
PackageServiceInstance Existing D1-first dual-write retained; required-start hardening followed in #1149 Medium
D1 service inventory Retained for discovery/export/deletion, parity, and rollback Low

No table or column retirement in this PR.

Conductor report

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Enhancements
    • Package-service entitlement usage is now counted from the authoritative usage meter for more consistent limit enforcement.
    • Service-start checks use the same authoritative running-service information, including restart and maximum-plan scenarios.
    • Service lifecycle updates remain synchronized as services start and stop.
  • Bug Fixes
    • Improved handling of stale, excluded, or unavailable service records while preserving safe entitlement enforcement.
  • Documentation
    • Updated architecture and usage guidance to reflect authoritative service and storage state.

cursoragent and others added 30 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>
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>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# 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>
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>
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>
# Conflicts:
#	docs/contributing/architecture/data-storage.md

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts:
#	docs/contributing/architecture/data-storage.md

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>
cursoragent and others added 20 commits August 1, 2026 15:41
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>
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>
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/mcp/capabilities/admin/domain.ts
#	packages/worker/src/mcp/capabilities/registry.node.test.ts

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>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 023b6289-3549-43be-b9e0-fe8d4f80e462

📥 Commits

Reviewing files that changed from the base of the PR and between 1909a01 and 9dff125.

📒 Files selected for processing (2)
  • packages/worker/src/entitlements/package-services-user-meter.node.test.ts
  • packages/worker/src/mcp/capabilities/services/service-start.node.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/worker/src/entitlements/package-services-user-meter.node.test.ts
  • packages/worker/src/mcp/capabilities/services/service-start.node.test.ts

📝 Walkthrough

Walkthrough

UserMeter now provides authoritative running package-service counts and service_start enforcement. D1 counting remains available for parity and discovery. Tests and architecture documentation reflect the authority cutover, lifecycle dual writes, staleness handling, exclusions, and fail-closed behavior.

Changes

Package-service authority cutover

Layer / File(s) Summary
Authority contract and usage routing
docs/contributing/architecture/entitlements.md, packages/worker/src/entitlements/service.ts
The service separates countRunningPackageServicesFromD1 for parity from the UserMeter-based countRunningPackageServices. Current package-service usage now uses UserMeter.
Authoritative UserMeter state model
packages/worker/src/entitlements/user-meter-do.ts
Documentation identifies UserMeter state as authoritative and records lifecycle, counting, export, bootstrap, repair, and cleanup behavior.
Parity and service-start integration
packages/worker/src/admin/user-meter-parity.ts, packages/worker/src/mcp/capabilities/services/service-start.ts, packages/worker/src/mcp/capabilities/services/service-start.node.test.ts, packages/worker/src/test-support/user-meter.ts
Parity uses the D1-specific counter. Service-start passes the worker environment to UserMeter counting. Tests seed running services through the in-memory meter.
Entitlement counting validation
packages/worker/src/entitlements/entitlements.node.test.ts, packages/worker/src/entitlements/package-service-states.workers.test.ts, packages/worker/src/entitlements/package-services-user-meter.node.test.ts
Tests cover D1 parity calls, fresh and stale meter rows, exclusions, empty meters, missing bindings, stable results, and running-to-stopped lifecycle updates.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ServiceStart
  participant EntitlementService
  participant UserMeter
  ServiceStart->>EntitlementService: request current package_services usage
  EntitlementService->>UserMeter: count running services
  UserMeter-->>EntitlementService: return authoritative count
  EntitlementService-->>ServiceStart: enforce entitlement limit
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: making UserMeter authoritative for entitlements service state.
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.
✨ 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.

@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 1909a01. Configure here.

now: now.toISOString(),
})
return count
}

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.

Meter lags D1 on starts

Medium Severity

service_start now enforces package_services via countRunningPackageServices, which reads only the UserMeter DO. Lifecycle still awaits D1 upserts in projectServiceStateToD1 but schedules UserMeter updates through waitUntil, so a completed start can leave D1 fresh while the meter omits that service. Back-to-back starts can undercount running services and allow limits to be exceeded until the shadow write lands.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1909a01. Configure here.

@github-actions

github-actions Bot commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

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

Worker: kody-pr-1148
D1: kody-pr-1148-db
KV: kody-pr-1148-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: 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 `@docs/contributing/architecture/entitlements.md`:
- Around line 260-263: Update the lifecycle start path and its
upsertPackageServiceState handling so running-state projection is retried
synchronously and the service start fails if projection cannot succeed. Keep
projection best-effort only for transitions that cannot increase usage, ensuring
countRunningPackageServices never evaluates a newly runnable service without a
successful UserMeter update.
🪄 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: f1ac7aad-c539-46ba-bb3b-906ba321a9e8

📥 Commits

Reviewing files that changed from the base of the PR and between d6bc667 and 1909a01.

📒 Files selected for processing (10)
  • docs/contributing/architecture/entitlements.md
  • packages/worker/src/admin/user-meter-parity.ts
  • packages/worker/src/entitlements/entitlements.node.test.ts
  • packages/worker/src/entitlements/package-service-states.workers.test.ts
  • packages/worker/src/entitlements/package-services-user-meter.node.test.ts
  • packages/worker/src/entitlements/service.ts
  • packages/worker/src/entitlements/user-meter-do.ts
  • packages/worker/src/mcp/capabilities/services/service-start.node.test.ts
  • packages/worker/src/mcp/capabilities/services/service-start.ts
  • packages/worker/src/test-support/user-meter.ts

Comment on lines +260 to +263
**Authority after the flip:** `countRunningPackageServices` reads directly from
the UserMeter DO — no D1 access on the enforcement path. New lifecycle
dual-writes (`upsertPackageServiceState`) populate the meter so enforcement is
immediately consistent. D1 `package_service_states` remains:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make running-state projection required before a service becomes runnable.

countRunningPackageServices reads UserMeter only. If upsertPackageServiceState fails, D1 can contain a fresh running row while UserMeter remains missing or stale. The next service_start can undercount usage and admit a service beyond the entitlement limit.

Parity detection and later rollback do not protect the failed enforcement decision. Retry the running-state projection synchronously and fail the start if it cannot succeed. Keep best-effort projection only for transitions that cannot increase usage.

Also applies to: 325-328

🤖 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` around lines 260 - 263,
Update the lifecycle start path and its upsertPackageServiceState handling so
running-state projection is retried synchronously and the service start fails if
projection cannot succeed. Keep projection best-effort only for transitions that
cannot increase usage, ensuring countRunningPackageServices never evaluates a
newly runnable service without a successful UserMeter update.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@cursor
cursor Bot merged commit aafa600 into main Aug 2, 2026
9 checks passed
@cursor
cursor Bot deleted the cursor/meter-do-38c8 branch August 2, 2026 00:49
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.

2 participants