Skip to content

feat(cli): remind about unclaimed projects before they expire#1580

Closed
shapirodaniel wants to merge 2 commits into
stack/mint-and-claim/a1/02-mint-corefrom
stack/mint-and-claim/a1/03-claim-reminders
Closed

feat(cli): remind about unclaimed projects before they expire#1580
shapirodaniel wants to merge 2 commits into
stack/mint-and-claim/a1/02-mint-corefrom
stack/mint-and-claim/a1/03-claim-reminders

Conversation

@shapirodaniel

@shapirodaniel shapirodaniel commented Jul 22, 2026

Copy link
Copy Markdown

Description

this PR adds escalating nudges reminding humans + agents to claim their project before ~72hr expiry is up, via the ledger mechanism leveraged by #1582. high level,

  • we store mappings of projectId to claims metadata
  • we periodically check rate-limited claims status api endpoints, falling back to local state and preferring false positives (claim this it's about to expire!) to false negatives (silently allowing project expiry to pass without a nudge)
  • we prune local state on successful claim, or project expiry

notably, we skip sanity new | sanity projects mint as these commands are geared specifically toward new projects, where the human/agent isn't in a claim motion, ex. when reviving and redeploying content + schemas from an expired project

Stack

(each PR reviews against its base; merge bottom-up only):

  1. stack/mint-and-claim/a1/01-terminal-presentation
  2. stack/mint-and-claim/a1/02-mint-core
  3. stack/mint-and-claim/a1/03-claim-reminders ← this diff
  4. stack/mint-and-claim/a1/04-init-signpost
  5. stack/mint-and-claim/a1/05-remint-guardrail
  6. stack/mint-and-claim/a1/06-ambient-reminder
  7. stack/mint-and-claim/a1/07-logout-env-token

What to review

util/claimNudges.ts tier/dedupe/self-clean logic and the prerun hook ordering in oclif.config.js.

Testing

Layer-scoped unit tests included (test:source ≥ 1:1); every layer validated standalone (full typecheck + targeted tests at each checkout).

pr1580-tier-nudge

🤖 Generated with Claude Code


Note

Low Risk
User-local config and stderr-only UX; reminders are best-effort (swallowed errors, fail-open lookups) and do not alter command outcomes or credentials handling.

Overview
Adds escalating claim reminders for minted-but-unclaimed projects: after projects:mint / sanity new, metadata is stored in user config (unclaimedProjects), and a new prerun hook (claimReminders) prints at most one stderr nudge per CLI run (skipped for mint/new commands so stdout/--json stay clean).

util/claimNudges.ts drives a four-tier waterfall (48h → 24h → 8h → 2h left), dedupes via lastNudgeTier, picks the most urgent project, and self-heals malformed registry entries. Before nudging or saying goodbye, it calls lookupClaimState on the provision API (new in mintProject.ts); network failures fail open on local expiry, with claimed/expired cleanup and merge-on-write so concurrent mints are not clobbered. Copy includes an explicit AI agent CTA to surface the claim link.

recordMintedProject is wired into the mint command after a successful mint; config write failures never fail the mint.

Reviewed by Cursor Bugbot for commit e029e40. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against stack/mint-and-claim/a1/02-mint-core (c561aaee)

@sanity/cli

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 2.2 KB -
Internal (gzip) 838 B -
Bundled (raw) 11.20 MB -
Bundled (gzip) 2.11 MB -
Import time 856ms -13ms, -1.5%

bin:sanity

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.90 MB -
Bundled (gzip) 1.78 MB -
Import time 2.05s -6ms, -0.3%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against stack/mint-and-claim/a1/02-mint-core (c561aaee)

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 114.1 KB -
Internal (gzip) 29.2 KB -
Bundled (raw) 21.76 MB -
Bundled (gzip) 3.46 MB -
Import time 764ms +2ms, +0.2%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-build

Compared against stack/mint-and-claim/a1/02-mint-core (c561aaee)

@sanity/cli-build/_internal/build

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 113.8 KB -
Internal (gzip) 28.7 KB -
Bundled (raw) 17.76 MB -
Bundled (gzip) 3.56 MB -
Import time 1.10s -0ms, -0.0%

@sanity/cli-build/_internal/env

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 1.8 KB -
Internal (gzip) 644 B -
Bundled (raw) 1.31 MB -
Bundled (gzip) 333.8 KB -
Import time 123ms +2ms, +1.6%

@sanity/cli-build/_internal/extract

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 8.6 KB -
Internal (gzip) 2.7 KB -
Bundled (raw) 155.0 KB -
Bundled (gzip) 39.5 KB -
Import time 240ms +3ms, +1.1%

🗺️ ./_internal/env · ./_internal/extract · @sanity/cli-build:./_internal/build treemap too large to embed · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against stack/mint-and-claim/a1/02-mint-core (c561aaee)

Metric Value vs stack/mint-and-claim/a1/02-mint-core (c561aae)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli/src/commands/new.ts 100.0% (new)
packages/@sanity/cli/src/commands/projects/mint.ts 100.0% (new)
packages/@sanity/cli/src/hooks/prerun/claimReminders.ts 88.9% (new)
packages/@sanity/cli/src/services/mintProject.ts 68.4% (new)
packages/@sanity/cli/src/util/claimNudges.ts 100.0% (new)
packages/@sanity/cli/src/util/envFile.ts 100.0% (new)
packages/@sanity/cli/src/util/flowOutput.ts 78.9% (new)
packages/@sanity/cli/src/util/newCommandSplash.ts 100.0% (new)
packages/@sanity/cli/src/util/terminalLink.ts 100.0% (new)

Comparing 9 changed files against main @ 56af732566885616d68473cf55737d67b85bf467

Overall Coverage

Metric Coverage
Statements 78.5% (+ 0.3%)
Branches 69.9% (+ 0.3%)
Functions 74.4% (+ 0.4%)
Lines 79.0% (+ 0.3%)

@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/02-mint-core branch from d607f3c to dfb4b5e Compare July 22, 2026 16:01
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 4d6a8f4 to 72e3837 Compare July 22, 2026 16:01
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
Comment thread packages/@sanity/cli/src/util/claimNudges.ts
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/02-mint-core branch from e2b5c60 to ed783ab Compare July 22, 2026 17:03
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 03a5f1c to 8f81526 Compare July 22, 2026 17:03
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/oclif.config.js
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from e43123f to 3a00415 Compare July 22, 2026 19:58
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/02-mint-core branch from 49bbfe0 to 631a12a Compare July 22, 2026 21:04
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 9182188 to 585e0e5 Compare July 22, 2026 21:04
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts Outdated
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from e81a97c to 66a782f Compare July 22, 2026 21:14
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 6aa0e48 to 603ef2b Compare July 22, 2026 21:21
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/02-mint-core branch from c098f12 to 84b409e Compare July 22, 2026 21:26
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 1a26343 to f254af8 Compare July 22, 2026 21:26
Third layer of the mint-and-claim stack: a machine-global mint ledger in
CLI user config, escalating stderr nudges (48/24/8/2h) via a prerun hook,
claim-state lookup on the provision API, and the mint command recording
each mint into the ledger.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shapirodaniel
shapirodaniel force-pushed the stack/mint-and-claim/a1/03-claim-reminders branch from 7020c9b to 05a316a Compare July 22, 2026 21:33
@shapirodaniel

Copy link
Copy Markdown
Author

bugbot run

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 05a316a. Configure here.

@shapirodaniel
shapirodaniel marked this pull request as ready for review July 22, 2026 22:47
@shapirodaniel
shapirodaniel requested a review from a team as a code owner July 22, 2026 22:47

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

Cursor Bugbot has reviewed your changes 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 e029e40. Configure here.

Comment thread packages/@sanity/cli/src/util/claimNudges.ts
)

const urgent = tier >= 3
const headline =

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.

The body copy changes phrasing across tiers, with three different sentences for "it gets deleted". It could appear as noise rather than escalation, and it's three surfaces to maintain instead of one. I'd keep a constant shape and escalate with colour and the urgency word only. Something like:

You have an unclaimed Sanity project <id> which expires in <N> hours.
Claim it with <url>
Everything keeps working after you claim, including the token in .env.

Future idea: If we know a user is sanity logind could we have a sanity claim command to claim programatically (not sure how often this'll happen though)

// No expiry from the server: keep the record untouched; the next run re-checks.
} else {
announce(
`⌛ Unclaimed Sanity project ${record.projectId} expired on ${record.expiresAt}. Run \`sanity new\` to mint a new one — where old credentials remain in .env, the guard will walk you through it.`,

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.

"where old credentials remain in .env, the guard will walk you through it" - nobody outside this stack will know what "the guard" is. Maybe:
Unclaimed Sanity project expired on and has been deleted.
Run sanity new to create a new project, and remember to claim it within 72 hours

@shapirodaniel

Copy link
Copy Markdown
Author

all feedback addressed on #1592, canceling this PR to consolidate after offline sync

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