Skip to content

Commit bb5a99a

Browse files
ianwiedsclaude
andcommitted
v5.0.195: Fix 24-hour cancel guard comparing ms to seconds timestamp
The 24-hour cancellation guard in payments/cancel/post.js was silently broken — it subtracted a UNIX seconds timestamp from Date.now() (milliseconds), producing an "age" of ~56 years for every subscription. The guard never fired, allowing users to cancel brand-new subscriptions. Fixed by multiplying startDateUNIX by 1000 before the subtraction. Also updated CLAUDE.md and README.md to use `npx mgr` consistently instead of the deprecated `npx bm` alias, and added notes to TODO-2.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6b873af commit bb5a99a

6 files changed

Lines changed: 103 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1414
- `Fixed` for any bug fixes.
1515
- `Security` in case of vulnerabilities.
1616

17+
# [5.0.195] - 2026-04-10
18+
### Fixed
19+
- 24-hour cancellation guard in `payments/cancel` was comparing `Date.now()` (milliseconds) against `startDateUNIX` (seconds), producing an "age" of ~56 years for every subscription — guard never fired and users could cancel brand-new subscriptions. Now multiplies `startDateUNIX` by 1000 before subtraction.
20+
### Changed
21+
- Standardized CLI examples in `CLAUDE.md` and `README.md` to use `npx mgr` instead of the deprecated `npx bm` alias
22+
1723
# [5.0.194] - 2026-04-08
1824
### Fixed
1925
- Fix email template data merge: caller's `settings.data` is now deep-merged at root of template data tree, removing the broken `data.` prefix indirection that caused empty order confirmation emails since 5.0.185

CLAUDE.md

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -600,30 +600,30 @@ The `POST /admin/post` route creates blog posts via GitHub's API. It handles ima
600600
### Running Tests
601601
```bash
602602
# Option 1: Two terminals
603-
npx bm emulator # Terminal 1 - keeps emulator running
604-
npx bm test # Terminal 2 - runs tests
603+
npx mgr emulator # Terminal 1 - keeps emulator running
604+
npx mgr test # Terminal 2 - runs tests
605605
606606
# Option 2: Single command (auto-starts emulator)
607-
npx bm test
607+
npx mgr test
608608
```
609609
610610
### Log Files
611611
BEM CLI commands automatically save all output to log files in `functions/` while still streaming to the console:
612-
- **`functions/serve.log`** — Output from `npx bm serve` (Firebase serve)
612+
- **`functions/serve.log`** — Output from `npx mgr serve` (Firebase serve)
613613
- **`functions/emulator.log`** — Full emulator output (Firebase emulator + Cloud Functions logs)
614614
- **`functions/test.log`** — Test runner output (when running against an existing emulator)
615-
- **`functions/logs.log`** — Cloud Function logs from `npx bm logs:read` or `npx bm logs:tail` (raw JSON for `read`, streaming text for `tail`)
615+
- **`functions/logs.log`** — Cloud Function logs from `npx mgr logs:read` or `npx mgr logs:tail` (raw JSON for `read`, streaming text for `tail`)
616616
617-
When `npx bm test` starts its own emulator, logs go to `emulator.log` (since it delegates to the emulator command). When running against an already-running emulator, logs go to `test.log`.
617+
When `npx mgr test` starts its own emulator, logs go to `emulator.log` (since it delegates to the emulator command). When running against an already-running emulator, logs go to `test.log`.
618618
619619
These files are overwritten on each run and are gitignored (`*.log`). Use them to search for errors, debug webhook pipelines, or review full function output after a test run.
620620
621621
### Filtering Tests
622622
```bash
623-
npx bm test rules/ # Run rules tests (both BEM and project)
624-
npx bm test bem:rules/ # Only BEM's rules tests
625-
npx bm test project:rules/ # Only project's rules tests
626-
npx bm test user/ admin/ # Multiple paths
623+
npx mgr test rules/ # Run rules tests (both BEM and project)
624+
npx mgr test bem:rules/ # Only BEM's rules tests
625+
npx mgr test project:rules/ # Only project's rules tests
626+
npx mgr test user/ admin/ # Multiple paths
627627
```
628628
629629
### Test Locations
@@ -716,7 +716,7 @@ assert.fail(message) // Explicit fail
716716
717717
## Stripe Webhook Forwarding
718718
719-
BEM auto-starts Stripe CLI webhook forwarding when running `npx bm serve` or `npx bm emulator`. This forwards Stripe test webhooks to the local server so the full payment pipeline works end-to-end during development.
719+
BEM auto-starts Stripe CLI webhook forwarding when running `npx mgr serve` or `npx mgr emulator`. This forwards Stripe test webhooks to the local server so the full payment pipeline works end-to-end during development.
720720
721721
**Requirements:**
722722
- `STRIPE_SECRET_KEY` set in `functions/.env`
@@ -725,7 +725,7 @@ BEM auto-starts Stripe CLI webhook forwarding when running `npx bm serve` or `np
725725
726726
**Standalone usage:**
727727
```bash
728-
npx bm stripe
728+
npx mgr stripe
729729
```
730730
731731
If any prerequisite is missing, webhook forwarding is silently skipped with an info message.
@@ -736,50 +736,50 @@ The forwarding URL is: `http://localhost:{hostingPort}/backend-manager/payments/
736736
737737
Quick commands for reading/writing Firestore and managing Auth users directly from the terminal. Works in any BEM consumer project (requires `functions/service-account.json` for production, or `--emulator` for local).
738738
739-
**IMPORTANT: All CLI commands (`npx mgr ...` / `npx bm ...`) MUST be run from the consumer project's `functions/` subdirectory** (e.g., `cd /path/to/my-project/functions && npx mgr ...`). The `mgr` binary lives in `functions/node_modules/.bin/` — running from the project root or any other directory will fail.
739+
**IMPORTANT: All CLI commands (`npx mgr ...`) MUST be run from the consumer project's `functions/` subdirectory** (e.g., `cd /path/to/my-project/functions && npx mgr ...`). The `mgr` binary lives in `functions/node_modules/.bin/` — running from the project root or any other directory will fail.
740740

741741
### Firestore Commands
742742

743743
```bash
744-
npx bm firestore:get <path> # Read a document
745-
npx bm firestore:set <path> '<json>' # Write/merge a document
746-
npx bm firestore:set <path> '<json>' --no-merge # Overwrite a document entirely
747-
npx bm firestore:query <collection> # Query a collection (default limit 25)
744+
npx mgr firestore:get <path> # Read a document
745+
npx mgr firestore:set <path> '<json>' # Write/merge a document
746+
npx mgr firestore:set <path> '<json>' --no-merge # Overwrite a document entirely
747+
npx mgr firestore:query <collection> # Query a collection (default limit 25)
748748
--where "field==value" # Filter (repeatable for AND)
749749
--orderBy "field:desc" # Sort
750750
--limit N # Limit results
751-
npx bm firestore:delete <path> # Delete a document (prompts for confirmation)
751+
npx mgr firestore:delete <path> # Delete a document (prompts for confirmation)
752752
```
753753

754754
### Auth Commands
755755

756756
```bash
757-
npx bm auth:get <uid-or-email> # Get user by UID or email (auto-detected via @)
758-
npx bm auth:list [--limit N] [--page-token T] # List users (default 100)
759-
npx bm auth:delete <uid-or-email> # Delete user (prompts for confirmation)
760-
npx bm auth:set-claims <uid-or-email> '<json>' # Set custom claims
757+
npx mgr auth:get <uid-or-email> # Get user by UID or email (auto-detected via @)
758+
npx mgr auth:list [--limit N] [--page-token T] # List users (default 100)
759+
npx mgr auth:delete <uid-or-email> # Delete user (prompts for confirmation)
760+
npx mgr auth:set-claims <uid-or-email> '<json>' # Set custom claims
761761
```
762762

763763
### Logs Commands
764764

765765
Fetch or stream Cloud Function logs from Google Cloud Logging. Requires `gcloud` CLI installed and authenticated. Auto-resolves the project ID from `service-account.json`, `.firebaserc`, or `GCLOUD_PROJECT`.
766766

767767
```bash
768-
npx bm logs:read # Read last 1h of logs (default: 300 entries, newest first)
769-
npx bm logs:read --fn bm_api # Filter by function name
770-
npx bm logs:read --fn bm_api --severity ERROR # Filter by severity (DEBUG, INFO, WARNING, ERROR, CRITICAL)
771-
npx bm logs:read --since 2d --limit 100 # Custom time range and limit
772-
npx bm logs:read --search "72.134.242.25" # Search textPayload for a string (IP, email, error, etc.)
773-
npx bm logs:read --fn bm_authBeforeCreate --search "ian@example.com" --since 7d # Combined filters
774-
npx bm logs:read --order asc # Oldest first (default: desc/newest first)
775-
npx bm logs:read --filter 'jsonPayload.level="error"' # Raw gcloud filter passthrough
776-
npx bm logs:tail # Stream live logs
777-
npx bm logs:tail --fn bm_paymentsWebhookOnWrite # Stream filtered live logs
768+
npx mgr logs:read # Read last 1h of logs (default: 300 entries, newest first)
769+
npx mgr logs:read --fn bm_api # Filter by function name
770+
npx mgr logs:read --fn bm_api --severity ERROR # Filter by severity (DEBUG, INFO, WARNING, ERROR, CRITICAL)
771+
npx mgr logs:read --since 2d --limit 100 # Custom time range and limit
772+
npx mgr logs:read --search "72.134.242.25" # Search textPayload for a string (IP, email, error, etc.)
773+
npx mgr logs:read --fn bm_authBeforeCreate --search "ian@example.com" --since 7d # Combined filters
774+
npx mgr logs:read --order asc # Oldest first (default: desc/newest first)
775+
npx mgr logs:read --filter 'jsonPayload.level="error"' # Raw gcloud filter passthrough
776+
npx mgr logs:tail # Stream live logs
777+
npx mgr logs:tail --fn bm_paymentsWebhookOnWrite # Stream filtered live logs
778778
```
779779

780780
Both commands save output to `functions/logs.log` (overwritten on each run). `logs:read` saves raw JSON; `logs:tail` streams text.
781781

782-
**Cloud Logs vs Local Logs:** These commands query **production** Google Cloud Logging. For **local/dev** logs, read `functions/serve.log` (from `npx bm serve`) or `functions/emulator.log` (from `npx bm test`) directly — they are plain text files, not gcloud.
782+
**Cloud Logs vs Local Logs:** These commands query **production** Google Cloud Logging. For **local/dev** logs, read `functions/serve.log` (from `npx mgr serve`) or `functions/emulator.log` (from `npx mgr test`) directly — they are plain text files, not gcloud.
783783

784784
| Flag | Description | Default | Commands |
785785
|------|-------------|---------|----------|
@@ -834,22 +834,22 @@ The `--fn` flag uses the **deployed Cloud Function name**, not the route path.
834834

835835
```bash
836836
# Read a user document from production
837-
npx bm firestore:get users/abc123
837+
npx mgr firestore:get users/abc123
838838

839839
# Write to emulator
840-
npx bm firestore:set users/test123 '{"name":"Test User"}' --emulator
840+
npx mgr firestore:set users/test123 '{"name":"Test User"}' --emulator
841841
842842
# Query with filters
843-
npx bm firestore:query users --where "subscription.status==active" --limit 10
843+
npx mgr firestore:query users --where "subscription.status==active" --limit 10
844844
845845
# Look up auth user by email
846-
npx bm auth:get user@example.com
846+
npx mgr auth:get user@example.com
847847
848848
# Set admin claims
849-
npx bm auth:set-claims user@example.com '{"admin":true}'
849+
npx mgr auth:set-claims user@example.com '{"admin":true}'
850850
851851
# Delete from emulator (no confirmation needed)
852-
npx bm firestore:delete users/test123 --emulator
852+
npx mgr firestore:delete users/test123 --emulator
853853
```
854854
855855
## Usage & Rate Limiting
@@ -1366,7 +1366,7 @@ Campaigns reference segments by SSOT key: `segments: ['subscription_free']`. Aut
13661366
13671367
### Seed Campaigns
13681368
1369-
Created by `npx bm setup` (idempotent, enforced fields checked every run):
1369+
Created by `npx mgr setup` (idempotent, enforced fields checked every run):
13701370
13711371
| ID | Type | Description |
13721372
|----|------|-------------|
@@ -1419,7 +1419,7 @@ marketing: {
14191419
14201420
8. **Increment usage before update** - Call `usage.increment()` then `usage.update()`
14211421
1422-
9. **Add Firestore composite indexes for new compound queries** - Any new Firestore query using multiple `.where()` clauses or `.where()` + `.orderBy()` requires a composite index. Add it to `src/cli/commands/setup-tests/helpers/required-indexes.js` (the SSOT). Consumer projects pick these up via `npx bm setup`, which syncs them into `firestore.indexes.json`. Without the index, the query will crash with `FAILED_PRECONDITION` in production.
1422+
9. **Add Firestore composite indexes for new compound queries** - Any new Firestore query using multiple `.where()` clauses or `.where()` + `.orderBy()` requires a composite index. Add it to `src/cli/commands/setup-tests/helpers/required-indexes.js` (the SSOT). Consumer projects pick these up via `npx mgr setup`, which syncs them into `firestore.indexes.json`. Without the index, the query will crash with `FAILED_PRECONDITION` in production.
14231423
14241424
## Key Files Reference
14251425
@@ -1463,7 +1463,7 @@ marketing: {
14631463
```javascript
14641464
assistant.isDevelopment() // true when ENVIRONMENT !== 'production' or in emulator
14651465
assistant.isProduction() // true when ENVIRONMENT === 'production'
1466-
assistant.isTesting() // true when running tests (via npx bm test)
1466+
assistant.isTesting() // true when running tests (via npx mgr test)
14671467
```
14681468
14691469
## Model Context Protocol (MCP)
@@ -1473,7 +1473,7 @@ BEM includes a built-in MCP server that exposes BEM routes as tools for Claude C
14731473
### Architecture
14741474
14751475
Two transport modes:
1476-
- **Stdio** (local): `npx bm mcp` — for Claude Code / Claude Desktop
1476+
- **Stdio** (local): `npx mgr mcp` — for Claude Code / Claude Desktop
14771477
- **Streamable HTTP** (remote): `POST /backend-manager/mcp` — for Claude Chat (stateless, Firebase Functions compatible)
14781478
14791479
### Available Tools (19)
@@ -1507,7 +1507,7 @@ Two transport modes:
15071507
15081508
### Hosting Rewrites
15091509
1510-
The `npx bm setup` command automatically adds required Firebase Hosting rewrites for MCP OAuth:
1510+
The `npx mgr setup` command automatically adds required Firebase Hosting rewrites for MCP OAuth:
15111511
```json
15121512
{
15131513
"source": "{/backend-manager,/backend-manager/**,/.well-known/oauth-protected-resource,/.well-known/oauth-authorization-server,/authorize,/token}",
@@ -1518,7 +1518,7 @@ The `npx bm setup` command automatically adds required Firebase Hosting rewrites
15181518
### CLI Usage
15191519
15201520
```bash
1521-
npx bm mcp # Start stdio MCP server (for Claude Code)
1521+
npx mgr mcp # Start stdio MCP server (for Claude Code)
15221522
```
15231523
15241524
### Claude Code Configuration

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ module.exports = function (assistant) {
8989
Run the setup command:
9090

9191
```bash
92-
npx bm setup
92+
npx mgr setup
9393
```
9494

9595
## Initialization Options
@@ -802,28 +802,28 @@ BEM includes an integration test framework that runs against the Firebase emulat
802802

803803
```bash
804804
# Option 1: Two terminals (recommended for development)
805-
npx bm emulator # Terminal 1 - keeps emulator running
806-
npx bm test # Terminal 2 - runs tests
805+
npx mgr emulator # Terminal 1 - keeps emulator running
806+
npx mgr test # Terminal 2 - runs tests
807807

808808
# Option 2: Single command (auto-starts emulator, shuts down after)
809-
npx bm test
809+
npx mgr test
810810
```
811811

812812
### Filtering Tests
813813

814814
```bash
815-
npx bm test rules/ # Run rules tests (both BEM and project)
816-
npx bm test bem:rules/ # Only BEM's rules tests
817-
npx bm test project:rules/ # Only project's rules tests
818-
npx bm test user/ admin/ # Multiple paths
815+
npx mgr test rules/ # Run rules tests (both BEM and project)
816+
npx mgr test bem:rules/ # Only BEM's rules tests
817+
npx mgr test project:rules/ # Only project's rules tests
818+
npx mgr test user/ admin/ # Multiple paths
819819
```
820820

821821
### Log Files
822822

823823
BEM CLI commands automatically save output to log files in the project directory:
824-
- **`emulator.log`** — Full emulator + Cloud Functions output (`npx bm emulator`)
825-
- **`test.log`** — Test runner output (`npx bm test`, when running against an existing emulator)
826-
- **`logs.log`** — Cloud Function logs (`npx bm logs:read` or `npx bm logs:tail`)
824+
- **`emulator.log`** — Full emulator + Cloud Functions output (`npx mgr emulator`)
825+
- **`test.log`** — Test runner output (`npx mgr test`, when running against an existing emulator)
826+
- **`logs.log`** — Cloud Function logs (`npx mgr logs:read` or `npx mgr logs:tail`)
827827

828828
Logs are overwritten on each run. Use them to debug failing tests or review function output.
829829

TODO-2.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,49 @@ payments/reactivate
1010
payments/upgrade
1111
* takes a subscription id and a new plan id and upgrades the user's subscription to the new plan. this can only be done if the user has an active subscription.
1212

13+
I'd call that cancellation-reversed — it pairs naturally with the existing cancellation-requested transition and clearly communicates the action (they reversed/undid the pending cancellation).
14+
15+
on the email we send, for payment recovered what is the category
16+
Let me check the handler.
17+
18+
Read payment-recovered.js
19+
The category is order/payment-recovered (line 13).
20+
21+
TODO-2.md
22+
waht about when they request a cancel
23+
Read cancellation-requested.js
24+
The category is order/cancellation-requested (line 13).
25+
26+
----
27+
add a dedicated BEM JSON field for usage to reset
28+
* this way we can have clear LIMITS with their definitions like
29+
* [
30+
{
31+
name: 'credits'
32+
reset: true,
33+
},
34+
{
35+
name: 'agents',
36+
reset: false,
37+
}
38+
]
39+
* mirrors: [
40+
{
41+
collection: 'agents',
42+
fields: ['usage.credits.daily', 'runs.replies.daily],
43+
}
44+
]
45+
46+
---
47+
MIRROR settigns in BEM JSON so that usage reset can properly get MIRRED DOCS liek slapform forms or chatsy agents DOCS
48+
1349
---
1450
GHOSTII REVAMP
1551
* better logic for generating posts. better model? claude?
1652

53+
---- MCP
54+
* ability for consuming prjec to specify MCP functions
55+
1756
-------
1857
UPSELL
1958
* products in BEM can have an UPSELL where you link another product ID and it allows you to add it to your cart OR shows you after checkout?

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backend-manager",
3-
"version": "5.0.194",
3+
"version": "5.0.195",
44
"description": "Quick tools for developing Firebase functions",
55
"main": "src/manager/index.js",
66
"bin": {

src/manager/routes/payments/cancel/post.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module.exports = async ({ assistant, user, settings }) => {
3535
// Guard: subscription younger than 24 hours
3636
const startDateUNIX = subscription.payment?.startDate?.timestampUNIX;
3737
if (startDateUNIX) {
38-
const ageMs = Date.now() - startDateUNIX;
38+
const ageMs = Date.now() - (startDateUNIX * 1000);
3939
const twentyFourHoursMs = 24 * 60 * 60 * 1000;
4040
if (ageMs < twentyFourHoursMs) {
4141
assistant.log(`Cancel rejected: uid=${uid}, subscription is only ${Math.round(ageMs / 1000 / 60)} minutes old`);

0 commit comments

Comments
 (0)