create-qa-architect 5.13.5 → 5.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,157 @@
1
+ # Polar.sh Deployment Guide
2
+
3
+ Deploy `webhook-handler.js` to process Polar.sh subscription events and issue signed Pro licenses automatically.
4
+
5
+ ## Overview
6
+
7
+ The payment flow:
8
+
9
+ 1. Customer purchases Pro at your landing page → Polar.sh hosted checkout
10
+ 2. Polar fires a `subscription.created` (and shortly after, `subscription.active`) webhook to your server
11
+ 3. `webhook-handler.js` verifies the signature, generates a signed license key, and saves it to Vercel Blob
12
+ 4. Customer runs `npx create-qa-architect@latest --activate-license` and enters their key
13
+ 5. CLI fetches the public signed registry from Vercel Blob, verifies the signature offline using the bundled `public-key.pem`, and unlocks Pro features
14
+
15
+ ## Prerequisites
16
+
17
+ - A [Polar.sh](https://polar.sh) organization
18
+ - A [Vercel](https://vercel.com) account (for Blob storage and hosting the webhook)
19
+ - Node.js >= 20
20
+ - The Ed25519 private key used to sign licenses (paired with the `public-key.pem` bundled in the npm package)
21
+
22
+ ---
23
+
24
+ ## Step 1: Create Polar products
25
+
26
+ In the [Polar Dashboard](https://polar.sh/dashboard) → your org → Products → New Product:
27
+
28
+ - Name: **QA Architect Pro**
29
+ - Description: _(your marketing copy)_
30
+ - Type: Subscription
31
+ - Prices: add **two** recurring prices under the same product
32
+ - $29.00 USD / month (recurring monthly)
33
+ - $290.00 USD / year (recurring yearly)
34
+
35
+ Save the **product ID** — you'll need it for `POLAR_PRO_PRODUCT_ID` below. Single product, two prices means one tier mapping regardless of billing cadence.
36
+
37
+ ## Step 2: Create the Polar webhook
38
+
39
+ In the Polar Dashboard → Settings → Webhooks → New Webhook:
40
+
41
+ - URL: `https://<your-vercel-domain>/webhook` (we'll deploy this next)
42
+ - Events to subscribe to:
43
+ - `subscription.created`
44
+ - `subscription.active`
45
+ - `subscription.updated`
46
+ - `subscription.canceled`
47
+ - `subscription.revoked`
48
+ - Save the **signing secret** (starts with `whsec_`) — you'll need it for `POLAR_WEBHOOK_SECRET`.
49
+
50
+ ## Step 3: Set up Vercel Blob storage
51
+
52
+ ```bash
53
+ npm install -g vercel
54
+ vercel link
55
+ # Dashboard → Storage → Create → Blob → name it "qa-architect-licenses"
56
+ # Copy the BLOB_READ_WRITE_TOKEN
57
+ ```
58
+
59
+ ## Step 4: Deploy `webhook-handler.js` to Vercel
60
+
61
+ `webhook-handler.js` exports an Express app compatible with Vercel serverless.
62
+
63
+ Create `vercel.json` in the repo root:
64
+
65
+ ```json
66
+ {
67
+ "version": 2,
68
+ "builds": [{ "src": "webhook-handler.js", "use": "@vercel/node" }],
69
+ "routes": [{ "src": "/(.*)", "dest": "/webhook-handler.js" }]
70
+ }
71
+ ```
72
+
73
+ Set environment variables in the Vercel dashboard (Project → Settings → Environment Variables):
74
+
75
+ | Variable | Value |
76
+ | ------------------------------ | -------------------------------------------------------------------- |
77
+ | `POLAR_WEBHOOK_SECRET` | `whsec_...` from Step 2 |
78
+ | `POLAR_PRO_PRODUCT_ID` | Pro product ID from Step 1 |
79
+ | `LICENSE_REGISTRY_PRIVATE_KEY` | Contents of `private-key.pem` (paired with bundled `public-key.pem`) |
80
+ | `LICENSE_REGISTRY_KEY_ID` | An ID for this signing key (e.g., `prod-2026-05`) |
81
+ | `BLOB_READ_WRITE_TOKEN` | From Step 3 |
82
+ | `STATUS_API_TOKEN` | _(optional)_ Bearer token for `/status` debug endpoint |
83
+
84
+ Then deploy:
85
+
86
+ ```bash
87
+ vercel --prod
88
+ ```
89
+
90
+ Update the Polar webhook URL (Step 2) to point at the deployed Vercel URL.
91
+
92
+ ## Step 5: Wire your landing page to Polar checkout
93
+
94
+ On `buildproven.ai/qa-architect`, replace any old Stripe checkout buttons with Polar checkout links:
95
+
96
+ - Monthly: `https://polar.sh/<org-slug>/<product-slug>?price=<price-id-monthly>`
97
+ - Annual: `https://polar.sh/<org-slug>/<product-slug>?price=<price-id-annual>`
98
+
99
+ Or use Polar's embeddable checkout component for an in-page experience.
100
+
101
+ ## Step 6: Smoke test
102
+
103
+ ```bash
104
+ # In the Polar dashboard, send a test webhook event for subscription.created
105
+ # (Settings → Webhooks → [your endpoint] → Send Test Event)
106
+
107
+ # Verify the license appeared:
108
+ curl https://<your-vercel-domain>/health
109
+ # → { "status": "ok", "database": "exists" }
110
+
111
+ curl https://<your-vercel-domain>/api/licenses/qa-architect.json
112
+ # → JSON with one signed license entry
113
+
114
+ # Activate locally:
115
+ npx create-qa-architect@latest --activate-license
116
+ # Enter the test license key. Should report PRO tier.
117
+ ```
118
+
119
+ For a real end-to-end test, use Polar's test mode (every webhook can be replayed from the dashboard) before going live.
120
+
121
+ ## Step 7: Verify revocation works
122
+
123
+ ```bash
124
+ # Cancel a test subscription in Polar (or use "Send Test Event" with subscription.canceled, then subscription.revoked)
125
+
126
+ # Verify license moved out of public registry:
127
+ curl https://<your-vercel-domain>/api/licenses/qa-architect.json
128
+ # → revoked key should be absent
129
+ ```
130
+
131
+ An **already-activated** CLI re-checks the signed registry on a cadence
132
+ (default every 7 days; override with `QAA_LICENSE_REVALIDATE_DAYS`). On the
133
+ next re-check after revocation it downgrades to FREE and prints a reactivation
134
+ message. The re-check **fails open when offline** — a paying user is never
135
+ locked out without a network connection; only a successful, signature-verified
136
+ fetch that finds the key missing or `status: "revoked"` causes a downgrade.
137
+
138
+ ### Optional: nudge users onto the latest CLI
139
+
140
+ Set `minRecommendedVersion` in the signed registry `_metadata` (server-side,
141
+ when you build the registry) to a semver string. On the periodic re-check, any
142
+ Pro CLI older than that prints a loud but **non-fatal** "update available"
143
+ warning. Leave it unset to disable. It never blocks execution.
144
+
145
+ ## Rollback
146
+
147
+ If you need to roll back to Stripe-direct: restore the previous `webhook-handler.js` from git (it's the same Express app + Blob layout, just a different event source). Existing signed licenses in Vercel Blob keep working — they're product-agnostic.
148
+
149
+ ## Common issues
150
+
151
+ **Signature verification fails** — the raw body parser must come _before_ `express.json()` for the `/webhook` route. `webhook-handler.js` does this on line ~125 (`app.use('/webhook', express.raw(...))`).
152
+
153
+ **`POLAR_PRO_PRODUCT_ID` mismatch** — log `event.data.product?.id || event.data.product_id` in the handler to confirm the exact ID Polar sends. Update the env var to match.
154
+
155
+ **License doesn't unlock Pro features** — check that `public-key.pem` bundled in your published npm package matches the private key in `LICENSE_REGISTRY_PRIVATE_KEY` on the server. They must be a pair.
156
+
157
+ **Customer canceled but key still works** — Polar fires `subscription.canceled` immediately, but the key stays valid until `subscription.revoked` fires (typically at `current_period_end`). This is intentional: the customer paid through the end of the period. After `subscription.revoked` removes the key from the registry, an activated CLI drops to FREE on its next periodic re-check (see Step 7) — not necessarily on the customer's very next run.
@@ -5,6 +5,7 @@ Deploy the `webhook-handler.js` server to process real Stripe payments and issue
5
5
  ## Overview
6
6
 
7
7
  The payment flow:
8
+
8
9
  1. Customer purchases Pro at `buildproven.ai/qa-architect`
9
10
  2. Stripe fires a `checkout.session.completed` webhook to your server
10
11
  3. `webhook-handler.js` validates the event, generates a signed license key, and saves it to Vercel Blob
@@ -25,10 +26,10 @@ The payment flow:
25
26
 
26
27
  In the [Stripe Dashboard](https://dashboard.stripe.com/products) → Products → Add product:
27
28
 
28
- | Product | Price | Billing | Price ID (example) |
29
- |---|---|---|---|
30
- | QA Architect Pro | $49.00 | Monthly | `price_1St9K2Gv7Su9XNJbdYoH3K32` |
31
- | QA Architect Pro | $490.00 | Yearly | `price_1St9KGGv7Su9XNJbrwKMsh1R` |
29
+ | Product | Price | Billing | Price ID (example) |
30
+ | ---------------- | ------- | ------- | -------------------------------- |
31
+ | QA Architect Pro | $49.00 | Monthly | `price_1St9K2Gv7Su9XNJbdYoH3K32` |
32
+ | QA Architect Pro | $490.00 | Yearly | `price_1St9KGGv7Su9XNJbrwKMsh1R` |
32
33
 
33
34
  The price IDs above are already mapped in `webhook-handler.js:315-318`. If your actual Stripe price IDs differ, update `mapPriceToTier()` in `webhook-handler.js`.
34
35
 
@@ -78,15 +79,15 @@ Your webhook URL will be: `https://your-project.vercel.app/webhook`
78
79
 
79
80
  In the Vercel dashboard → Project → Settings → Environment Variables, add:
80
81
 
81
- | Variable | Value | Notes |
82
- |---|---|---|
83
- | `STRIPE_SECRET_KEY` | `sk_live_...` | From Stripe Dashboard → Developers → API keys |
84
- | `STRIPE_WEBHOOK_SECRET` | `whsec_...` | Generated in Step 5 below |
85
- | `LICENSE_REGISTRY_PRIVATE_KEY` | Base64-encoded ED25519 private key | See note below |
86
- | `LICENSE_REGISTRY_KEY_ID` | `default` | Or a versioned ID like `v1` |
87
- | `BLOB_READ_WRITE_TOKEN` | `vercel_blob_...` | From Vercel Blob store settings |
88
- | `STATUS_API_TOKEN` | A strong random string | Protects the `/status` endpoint |
89
- | `NODE_ENV` | `production` | Enables HSTS and production error handling |
82
+ | Variable | Value | Notes |
83
+ | ------------------------------ | ---------------------------------- | --------------------------------------------- |
84
+ | `STRIPE_SECRET_KEY` | `sk_live_...` | From Stripe Dashboard → Developers → API keys |
85
+ | `STRIPE_WEBHOOK_SECRET` | `whsec_...` | Generated in Step 5 below |
86
+ | `LICENSE_REGISTRY_PRIVATE_KEY` | Base64-encoded ED25519 private key | See note below |
87
+ | `LICENSE_REGISTRY_KEY_ID` | `default` | Or a versioned ID like `v1` |
88
+ | `BLOB_READ_WRITE_TOKEN` | `vercel_blob_...` | From Vercel Blob store settings |
89
+ | `STATUS_API_TOKEN` | A strong random string | Protects the `/status` endpoint |
90
+ | `NODE_ENV` | `production` | Enables HSTS and production error handling |
90
91
 
91
92
  **Private key format:** The key must be a PEM-encoded ED25519 private key, base64-encoded as a single line (no newlines):
92
93
 
@@ -157,6 +158,7 @@ Switch to live mode keys once the test flow works end-to-end.
157
158
  ## Step 8: Connect Your Checkout Page
158
159
 
159
160
  Your Stripe Checkout session must:
161
+
160
162
  - Use one of the two price IDs mapped in `mapPriceToTier()`
161
163
  - Be a **subscription** mode checkout (not one-time payment)
162
164
  - Collect a customer email
@@ -168,7 +170,8 @@ const session = await stripe.checkout.sessions.create({
168
170
  mode: 'subscription',
169
171
  payment_method_types: ['card'],
170
172
  line_items: [{ price: 'price_1St9K2Gv7Su9XNJbdYoH3K32', quantity: 1 }],
171
- success_url: 'https://buildproven.ai/qa-architect/success?session_id={CHECKOUT_SESSION_ID}',
173
+ success_url:
174
+ 'https://buildproven.ai/qa-architect/success?session_id={CHECKOUT_SESSION_ID}',
172
175
  cancel_url: 'https://buildproven.ai/qa-architect',
173
176
  })
174
177
  ```
@@ -199,10 +202,10 @@ When a subscription is canceled in Stripe, the `customer.subscription.deleted` e
199
202
 
200
203
  ## Troubleshooting
201
204
 
202
- | Symptom | Likely cause | Fix |
203
- |---|---|---|
204
- | Webhook returns 400 | Wrong `STRIPE_WEBHOOK_SECRET` | Re-copy the signing secret from Stripe Dashboard |
205
- | License not created after payment | Unknown price ID | Update `mapPriceToTier()` with your actual Stripe price IDs |
206
- | CLI can't find license | Wrong blob URL | Check `BLOB_PATHS` in `lib/blob-storage.js` matches your Vercel Blob store |
207
- | `sk_test_` warning in logs | Test key in production | Replace with `sk_live_...` key |
208
- | `/status` returns 503 | `STATUS_API_TOKEN` not set | Add the env var in Vercel settings |
205
+ | Symptom | Likely cause | Fix |
206
+ | --------------------------------- | ----------------------------- | -------------------------------------------------------------------------- |
207
+ | Webhook returns 400 | Wrong `STRIPE_WEBHOOK_SECRET` | Re-copy the signing secret from Stripe Dashboard |
208
+ | License not created after payment | Unknown price ID | Update `mapPriceToTier()` with your actual Stripe price IDs |
209
+ | CLI can't find license | Wrong blob URL | Check `BLOB_PATHS` in `lib/blob-storage.js` matches your Vercel Blob store |
210
+ | `sk_test_` warning in logs | Test key in production | Replace with `sk_live_...` key |
211
+ | `/status` returns 503 | `STATUS_API_TOKEN` not set | Add the env var in Vercel settings |
@@ -0,0 +1,130 @@
1
+ # Plan: Vibe-Code Auditor
2
+
3
+ **Created:** 2026-05-27
4
+ **Status:** Active
5
+ **Branch:** feat/vibe-code-auditor (new from feat/polar-migration)
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ QA Architect currently covers 2/7 security audit categories (secrets via Gitleaks Pro, and CVEs via npm audit free). The product is positioned as a quality bootstrap tool, but the 2026 market opportunity is "vibe-code security auditor" — a self-serve CLI that catches OWASP Top-10 patterns, injection vectors, auth gaps, production misconfigs, and hallucinated packages in AI-generated code. No CLI tool in the market does this for solo/indie developers. New web-based competitors (VibeDoctor, Vibe App Scanner) are filling the web-SaaS gap but not the CLI gap. The current product needs a new `audit` subcommand powered by semgrep, covering 5/7 categories in free tier, repositioned to match the buyer persona: "worried vibe coder about to ship."
12
+
13
+ Additionally the pricing needs a drop from $49/mo to $29/mo to match market expectations, and the README/help text needs a full repositioning.
14
+
15
+ ---
16
+
17
+ ## Options Considered
18
+
19
+ ### Option A: Add `audit` subcommand via semgrep (chosen)
20
+
21
+ Add `lib/commands/audit.js` that runs semgrep with an extended security ruleset covering SQL injection, XSS, command injection, auth bypass, production misconfigs, hardcoded secrets, and hallucinated package checks. Integrate as free-tier feature (basic 5 categories) with Pro extension (hallucination check + full OWASP pack + `--fix` prompt generation).
22
+
23
+ **Pros:**
24
+
25
+ - Semgrep rules already exist in `.semgrep/defensive-patterns.yaml` — extend rather than build from scratch
26
+ - Consistent with existing spawnSync/arg-array security pattern
27
+ - CLI-native = key differentiator vs web-based competitors
28
+ - Adds credible "auditor" positioning without changing existing ship-check/pr-check Pro features
29
+
30
+ **Cons:**
31
+
32
+ - Semgrep must be installed on user machine (document in README, add detection+guidance)
33
+ - Semgrep rules won't catch everything — must be honest about coverage
34
+
35
+ ### Option B: Use eslint-plugin-security only
36
+
37
+ Run eslint with security plugin, no new dependency.
38
+
39
+ **Pros:** No semgrep required
40
+
41
+ **Cons:** Much weaker coverage — misses SQL injection, auth bypass, command injection patterns. eslint-plugin-security already part of existing setup flow so no net new value for "audit" use case.
42
+
43
+ ### Option C: Bundle semgrep binary (like gitleaks)
44
+
45
+ Pin and cache semgrep binary like gitleaks v8.28.0.
46
+
47
+ **Cons:** Semgrep binary is 50-100MB — not viable for npm package. Document as prerequisite instead.
48
+
49
+ ---
50
+
51
+ ## Decision
52
+
53
+ **Approach:** Option A — semgrep-powered `audit` subcommand + pricing update + README repositioning
54
+
55
+ **Rationale:** Semgrep is the de-facto OSS SAST engine with a large rule library and a simple CLI. The existing `.semgrep/defensive-patterns.yaml` gives a head start. The CLI gap in the market is real — web SaaS competitors all require uploading code to their service; this runs entirely locally, which is a trust advantage for security-conscious builders. Pricing drop from $49 to $29 reduces conversion friction without undermining value.
56
+
57
+ ---
58
+
59
+ ## Implementation Plan
60
+
61
+ ### New Files to Create
62
+
63
+ - `lib/commands/audit.js` — main audit command handler
64
+ - `.semgrep/vibe-audit-rules.yaml` — extended security ruleset (SQL, XSS, command injection, auth, CORS, debug mode, hardcoded secrets, missing validation, hallucinated packages)
65
+ - `tests/audit.test.js` — unit tests for audit command
66
+
67
+ ### Files to Modify
68
+
69
+ - `setup.js` — add `--audit` flag parsing + routing to `handleAudit()`, add to help text
70
+ - `lib/licensing.js` — add `auditBasic` (free) and `auditPro` (Pro) feature flags; update pricing from $49→$29
71
+ - `README.md` — full repositioning: vibe-code security auditor, lead with `audit` command, update pricing table
72
+ - `package.json` — update description, add `audit` to scripts if helpful
73
+
74
+ ### Execution Order
75
+
76
+ 1. **Create branch** `feat/vibe-code-auditor` from `feat/polar-migration`
77
+ 2. **Create `.semgrep/vibe-audit-rules.yaml`** — the audit categories (SQL/XSS/cmd injection, auth bypass, CORS/debug misconfigs, hardcoded creds, missing rate limits). Build on existing defensive-patterns.yaml, add new categories.
78
+ 3. **Create `lib/commands/audit.js`** — the audit command:
79
+ - Detect if semgrep is installed, provide install guidance if not
80
+ - Run semgrep with both defensive-patterns.yaml and vibe-audit-rules.yaml
81
+ - Run npm audit (CVEs, free)
82
+ - Check for hallucinated packages (Pro: verify package names against npm registry)
83
+ - Produce structured output: Critical/High/Medium/Low findings with file:line, what's wrong, why it matters, suggested fix
84
+ - Pro: `--fix` flag generates Claude Code prompts for each finding
85
+ - Support `--json` output flag
86
+ - Support `--out <path>` to write markdown report to file
87
+ 4. **Update `setup.js`** — add `--audit` and `--audit-fix` flags, routing, and help text
88
+ 5. **Update `lib/licensing.js`** — add feature flags, change Pro price from 4900 to 2900 (cents)
89
+ 6. **Create `tests/audit.test.js`** — tests for: semgrep not installed handling, result parsing, severity mapping, json output, markdown output
90
+ 7. **Update `README.md`** — full rewrite of positioning sections: new tagline, lead with `audit`, update pricing table ($29/mo), update command reference
91
+ 8. **Run full test suite** — `npm test`, `npm run lint`
92
+ 9. **Version bump** to 5.14.0 (new feature, not patch)
93
+ 10. **Commit + PR** with `/bs:quality --merge`
94
+
95
+ ### Out of Scope
96
+
97
+ - Supabase RLS gap detection (requires DB schema access — defer to v2)
98
+ - GDPR/soft-delete/audit-log checks (high false-positive risk — defer)
99
+ - Python audit rules (defer — focus on JS/TS/Next.js stack for v1)
100
+ - IDE plugin (CLI only)
101
+ - Web dashboard
102
+ - Auto-fix (only generates Claude Code prompts, doesn't apply them)
103
+ - Agentic workflow scanning (LangGraph/CrewAI — different product)
104
+ - Renaming the product (not worth it at this stage)
105
+
106
+ ---
107
+
108
+ ## Verification Steps
109
+
110
+ 1. `node setup.js --audit` on this repo — should detect 0 critical findings (or real ones in test fixtures)
111
+ 2. `node setup.js --audit --json` — valid JSON output
112
+ 3. `node setup.js --audit --out /tmp/report.md` — file written
113
+ 4. Test with semgrep not installed — should show clear install guidance, not crash
114
+ 5. `node tests/audit.test.js` — all pass
115
+ 6. `npm test` — all 50+ tests still pass
116
+ 7. `npm run lint` — clean
117
+ 8. `node setup.js --license-status` — confirms Pro price shows $29/mo
118
+
119
+ ---
120
+
121
+ ## Notes / Gotchas
122
+
123
+ - Semgrep detection: use `which semgrep` or `semgrep --version` via spawnSync, handle ENOENT gracefully
124
+ - Semgrep output is JSON (`semgrep --json`), parse `results[].path`, `.start.line`, `.check_id`, `.message`, `.severity`, `.extra.message`
125
+ - Semgrep severity levels: `ERROR` → Critical, `WARNING` → High/Medium depending on rule metadata
126
+ - The existing `.semgrep/defensive-patterns.yaml` rules are already good for SQL injection, command injection, auth bypass, CORS, hardcoded secrets — extend rather than duplicate
127
+ - Hallucinated package check (Pro): hit `https://registry.npmjs.org/<package>` for each dep in package.json, flag 404s. Cache results to avoid rate limits.
128
+ - Pricing: `lib/licensing.js` stores price in cents. Change from `4900` to `2900`. Also update any string references to "$49" → "$29".
129
+ - The `quality.yml` template-as-product invariant: if audit is added to the project's own CI, ensure it uses `npx @latest` pattern and not `node_modules/` references.
130
+ - `--fix` flag for Pro: format as "Copy this prompt into Claude Code:" followed by a structured prompt that includes the file path, line number, issue, and the recommended fix pattern.
@@ -0,0 +1,111 @@
1
+ # Polar.sh Migration Plan
2
+
3
+ **Status:** in progress
4
+ **Date:** 2026-05-17
5
+ **Replaces:** Stripe-direct billing
6
+
7
+ ## Why
8
+
9
+ We're migrating billing from **Stripe-direct** to **Polar.sh** (Merchant-of-Record).
10
+
11
+ | | Stripe direct | Polar.sh (MoR) |
12
+ | ---------------------------------------- | ------------------------------------------------------------------------- | ----------------------- |
13
+ | Effective fee on $49/mo | ~3.5% | ~4.8% (~1.3% premium) |
14
+ | Global sales tax / VAT / GST | **We owe it** (~$200-400/mo for Anrok at scale, plus state registrations) | Polar collects + remits |
15
+ | Customer portal (cancel/update/invoices) | Build it | Built-in |
16
+ | Dunning / failed payment retries | Build it | Built-in |
17
+ | Effort to ship v1 | High (tax, portal, dunning) | Low (webhook swap only) |
18
+
19
+ Crossover where Stripe-direct wins: ~$50K MRR. Until then, Polar is **cheaper in total cost** and ships faster.
20
+
21
+ ## Architecture: what stays, what changes
22
+
23
+ ### Stays (the moat)
24
+
25
+ - `lib/license-signing.js` — Ed25519 signing primitives (re-exports `@buildproven/license-core`)
26
+ - `lib/license-validator.js` — offline signature verification
27
+ - `lib/licensing.js` — tier definitions, feature gates, activation flow
28
+ - `public-key.pem` bundled in the npm package — CLI verifies signed payloads offline
29
+ - `lib/blob-storage.js` — Vercel Blob layout for private DB + public signed registry
30
+ - `--activate-license` CLI flow — fetches public registry, falls back to cached offline data
31
+ - License key format `QAA-XXXX-XXXX-XXXX-XXXX`, deterministic from customer ID
32
+
33
+ ### Changes
34
+
35
+ - `webhook-handler.js` — event source swap: Stripe `checkout.session.completed` → Polar `subscription.created/active/canceled/revoked`. Same Vercel Blob writes, same signing.
36
+ - `docs/STRIPE-LIVE-MODE-DEPLOYMENT.md` → archived. Replaced by `docs/POLAR-DEPLOYMENT.md`.
37
+ - `lib/billing-dashboard.html` → archived. Polar's hosted customer portal replaces it.
38
+ - Buy URLs (`buildproven.ai/qa-architect`) → Polar checkout URL.
39
+ - `admin-license.js` → kept, comments updated to clarify it's a manual fallback only.
40
+
41
+ ### New
42
+
43
+ - `subscription.canceled` / `subscription.revoked` → revocation list. Signed JSON at known URL, cached by CLI with grace period so offline CI doesn't break. Closes the "cancel but keep Pro forever" loophole.
44
+ - `COMMERCIAL.md` — paid-tier terms gated by the license-key check at runtime.
45
+ - `LICENSE` swap: custom EULA → standard **Apache-2.0**.
46
+
47
+ ## Why we don't use Polar's built-in license keys
48
+
49
+ Polar's built-in license-key benefit requires online validation against `/v1/customer-portal/license-keys/validate`. Our CLI is designed for **offline verification** — `npx create-qa-architect` must work in CI sandboxes with no outbound HTTP. We use Polar only for billing/MoR/checkout/portal/dunning. Our existing Ed25519 signing + Vercel Blob + offline-verifying CLI stays as-is.
50
+
51
+ ## Polar webhook events we handle
52
+
53
+ | Event | Action |
54
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
55
+ | `subscription.created` | Generate license key, sign payload, write to private DB + public signed registry |
56
+ | `subscription.active` | Idempotent re-issue (covers renewal + late activations) |
57
+ | `subscription.updated` | If `product_id` changed (plan switch), update tier in DB |
58
+ | `subscription.canceled` | Mark as `pending_cancel` in private DB. Subscription is still active until `current_period_end`. **Do not** revoke yet. |
59
+ | `subscription.revoked` | Move license key to revocation list. CLI will refuse it after next registry pull (with cache + grace period). |
60
+
61
+ ## Polar product setup (manual, user does this)
62
+
63
+ 1. Create Polar org at https://polar.sh
64
+ 2. Create product **"QA Architect Pro"** with two prices:
65
+ - $49/mo (recurring monthly)
66
+ - $490/yr (recurring yearly)
67
+ 3. Save the `product_id` — single product, two prices.
68
+ 4. Configure webhook endpoint: `https://<your-vercel-domain>/webhook`
69
+ 5. Webhook secret → env var `POLAR_WEBHOOK_SECRET`
70
+ 6. API token (for any server-side Polar API calls) → env var `POLAR_ACCESS_TOKEN`
71
+
72
+ ## Env vars (replaces Stripe vars)
73
+
74
+ | Old (Stripe) | New (Polar) |
75
+ | ------------------------------ | ----------------------------------------- |
76
+ | `STRIPE_SECRET_KEY` | `POLAR_ACCESS_TOKEN` |
77
+ | `STRIPE_WEBHOOK_SECRET` | `POLAR_WEBHOOK_SECRET` |
78
+ | `LICENSE_REGISTRY_PRIVATE_KEY` | (unchanged) |
79
+ | `LICENSE_REGISTRY_KEY_ID` | (unchanged) |
80
+ | `BLOB_READ_WRITE_TOKEN` | (unchanged) |
81
+ | (none) | `POLAR_PRO_PRODUCT_ID` — maps to PRO tier |
82
+
83
+ ## Verification
84
+
85
+ Before declaring done:
86
+
87
+ 1. `npm test` — full suite passes
88
+ 2. `npm run lint` — clean
89
+ 3. Manual smoke test: simulate a `subscription.created` event with Polar's CLI / Postman → verify license appears in Blob → run `npx . --activate-license` with the issued key → confirm Pro feature unlocks.
90
+ 4. Manual revocation test: simulate `subscription.revoked` → confirm license appears in revocation list → confirm CLI rejects key after registry pull.
91
+
92
+ ## Replicate to claude-kit-pro
93
+
94
+ Same migration, same architecture. Differences:
95
+
96
+ - License key prefix differs (`CKP-` instead of `QAA-`)
97
+ - Webhook URL differs (deploy under claude-kit-pro's Vercel project)
98
+ - `POLAR_PRO_PRODUCT_ID` env var points at claude-kit-pro's Polar product
99
+
100
+ Follow this doc step-by-step in `../claude-kit-pro/`.
101
+
102
+ ## Rollback
103
+
104
+ If Polar fails for any reason, the migration is reversible in ~half a day:
105
+
106
+ 1. Restore `webhook-handler.js` from git (it was just an event-source swap)
107
+ 2. Restore `docs/STRIPE-LIVE-MODE-DEPLOYMENT.md`
108
+ 3. Repoint Vercel webhook to Stripe events
109
+ 4. Existing signed licenses in Vercel Blob keep working — they're product-agnostic.
110
+
111
+ The signing/verification layer never depended on the billing provider. That's why this swap is cheap.
@@ -0,0 +1,159 @@
1
+ # Pro Feature Expansion — May 2026
2
+
3
+ Spec for 4 new Pro-tier commands. Positioning: "quality gate for AI-assisted small teams."
4
+
5
+ ## 1. `--ship-check` (unified release readiness)
6
+
7
+ **CLI**: `npx create-qa-architect --ship-check [--json] [--out <path>]`
8
+
9
+ **Module**: `lib/commands/ship-check.js`
10
+
11
+ **Gating**: requires Pro tier (proxy: `hasFeature('coverageThresholds')`).
12
+
13
+ **Behavior**: orchestrates existing Pro checks, never re-implements them. For each check:
14
+
15
+ - run the existing command/validator in a child process or via direct import
16
+ - capture pass/fail + summary
17
+ - never fail-fast; collect all results
18
+
19
+ Checks (in order):
20
+
21
+ 1. Lint (`npm run lint` if script exists)
22
+ 2. Tests (`npm test` if script exists — short timeout, allow opt-out via `--skip-tests`)
23
+ 3. Security scan (gitleaks current-files, plus `npm audit --omit=dev` if package.json)
24
+ 4. Coverage thresholds (read `coverage/coverage-summary.json` if present, compare to `.qualityrc.json` thresholds)
25
+ 5. Bundle size (run `size-limit` if configured)
26
+ 6. Lighthouse thresholds (skip if not configured — info only)
27
+ 7. Env validation (check `.env.example` vs documented env vars)
28
+ 8. CI cost summary (call existing `analyze-ci` module functions in `--summary` mode)
29
+ 9. Docs validation (existing docs validator)
30
+
31
+ **Output**:
32
+
33
+ - Default: human-readable terminal report with section headers + final verdict.
34
+ - `--json`: machine-readable JSON (for CI consumption).
35
+ - `--out report.md`: write markdown suitable for PR comments.
36
+
37
+ **Verdict logic**:
38
+
39
+ - `SHIP`: zero failures, zero critical warnings.
40
+ - `REVIEW`: warnings but no failures.
41
+ - `BLOCK`: any failure.
42
+
43
+ **Exit code**: 0 on SHIP/REVIEW, 1 on BLOCK.
44
+
45
+ ---
46
+
47
+ ## 2. `--pr-check` (diff-aware risk classifier)
48
+
49
+ **CLI**: `npx create-qa-architect --pr-check [--base <branch>] [--json] [--out <path>]`
50
+
51
+ **Module**: `lib/commands/pr-check.js`
52
+
53
+ **Gating**: requires Pro tier.
54
+
55
+ **Behavior**:
56
+
57
+ 1. Determine base branch (default: `main`, fallback `master`).
58
+ 2. Get diff: `git diff --name-status <base>...HEAD`.
59
+ 3. Classify each changed file by path patterns + content sniff:
60
+ - **HIGH risk**: auth, crypto, payments, env files, db migrations, GitHub workflows, security headers, license logic, anything matching `/auth|crypto|payment|stripe|webhook|migration|secret|token|key/i`.
61
+ - **MEDIUM risk**: config (package.json, tsconfig, eslint), public API surface (`index.ts`, `lib/**` exports), dependency changes (`package*.json`, `requirements.txt`).
62
+ - **LOW risk**: docs (`*.md`), tests (`*.test.*`, `tests/**`), comments-only.
63
+ 4. For each non-test source file changed, check if a matching test file changed too. Flag missing tests.
64
+ 5. For HIGH-risk files: check if covered by CODEOWNERS (if file exists).
65
+ 6. Emit risk summary + per-file table.
66
+
67
+ **Output**: markdown report (PR-comment-ready) with:
68
+
69
+ - Risk summary (counts per tier)
70
+ - Missing tests warning
71
+ - High-risk file list with reasons
72
+ - Verdict: SHIP / REVIEW / BLOCK (BLOCK only if HIGH + no tests + no codeowner)
73
+
74
+ **Exit code**: 0 on SHIP/REVIEW, 1 on BLOCK (configurable via `--no-fail`).
75
+
76
+ ---
77
+
78
+ ## 3. CI Doctor (expand `--analyze-ci`)
79
+
80
+ **CLI**: existing `--analyze-ci` adds new `--doctor` flag for the extra checks. Default still shows cost analysis.
81
+
82
+ **Module**: extend `lib/commands/analyze-ci.js` — add a `runDoctor(workflows)` function.
83
+
84
+ **New findings** (each with concrete fix suggestion):
85
+
86
+ 1. **Duplicated jobs**: detect jobs with identical `runs-on` + steps signature → suggest reusable workflow.
87
+ 2. **Missing path filters**: workflows triggered on every push without `paths:` or `paths-ignore:` → suggest path filters for monorepos / docs-only changes.
88
+ 3. **Expensive matrix**: matrix with >10 combinations → suggest pruning or `include`/`exclude`.
89
+ 4. **Cache mistakes**: `actions/setup-node` without `cache:` parameter → suggest enabling.
90
+ 5. **Unnecessary scheduled runs**: cron more frequent than weekly with no obvious need → suggest reducing.
91
+ 6. **Flaky test detection**: parse `gh run list --json` if `gh` CLI available, look for jobs with success rate <90% over last 30 runs. Skip gracefully if `gh` not authenticated.
92
+
93
+ **Output**: appended section to existing report. Each finding shows: title, affected workflow/job, fix suggestion (1-2 lines), estimated savings if applicable.
94
+
95
+ ---
96
+
97
+ ## 4. `--history-scan` (historical secrets)
98
+
99
+ **CLI**: `npx create-qa-architect --history-scan [--depth <N>] [--json]`
100
+
101
+ **Module**: `lib/commands/history-scan.js`
102
+
103
+ **Gating**: requires `hasFeature('securityScanning')`.
104
+
105
+ **Behavior**:
106
+
107
+ 1. Reuse `resolveGitleaksBinary()` from `lib/validation/config-security.js`.
108
+ 2. Run: `<gitleaks> detect --no-banner --redact --report-format=json --report-path=<tmp> --log-opts="--all"` (or `HEAD~<depth>` if `--depth` given).
109
+ 3. Parse JSON output, deduplicate by `{secret, file, commit}`.
110
+ 4. Group findings by commit SHA, list files, secret types.
111
+ 5. Report counts per secret type + top 10 oldest exposures.
112
+
113
+ **Output**: terminal report + optional JSON. Markdown export for PR.
114
+
115
+ **Exit code**: 0 if zero findings, 1 if any.
116
+
117
+ **Safety**: pass `--all` only when explicitly requested; default `HEAD~1000` to bound cost on huge repos.
118
+
119
+ ---
120
+
121
+ ## Licensing changes
122
+
123
+ Add flags to `lib/licensing.js` FEATURES map:
124
+
125
+ - `shipCheck`: PRO only
126
+ - `prCheck`: PRO only
127
+ - `ciDoctor`: PRO only (expansion of existing `ciCostAnalysis`)
128
+ - `historicalSecretsScan`: PRO only (under existing `securityScanning` umbrella)
129
+
130
+ Update PRO roadmap array. Add FREE roadmap line: "❌ No release readiness gate (--ship-check), risk-aware PR review, CI doctor, or historical secrets scan".
131
+
132
+ ---
133
+
134
+ ## Testing
135
+
136
+ One test file per command:
137
+
138
+ - `tests/ship-check.test.js`
139
+ - `tests/pr-check.test.js`
140
+ - `tests/ci-doctor.test.js`
141
+ - `tests/history-scan.test.js`
142
+
143
+ Each covers:
144
+
145
+ - Free tier blocked (proper upgrade message)
146
+ - Pro tier runs (via `QAA_DEVELOPER=true` or stub license)
147
+ - Output format validates (markdown structure / JSON shape)
148
+ - Edge cases: empty diff (pr-check), no workflows (ci-doctor), no .git history (history-scan), no coverage report (ship-check)
149
+
150
+ Goal: keep coverage ≥75% lines / 70% functions per project standard.
151
+
152
+ ---
153
+
154
+ ## Out of scope (deferred)
155
+
156
+ - LLM-powered fix suggestions (depends on API key, adds cost dimension)
157
+ - Monorepo selective CI (deeper architecture change)
158
+ - Team dashboard (needs hosted component)
159
+ - PR inline annotations via Checks API (depends on GH App / token model; document for v6)
package/eslint.config.cjs CHANGED
@@ -25,6 +25,7 @@ const configs = [
25
25
  '**/build/**',
26
26
  '**/coverage/**',
27
27
  '**/*.html',
28
+ 'webhook-handler.js', // server-side deployment script, deps installed separately
28
29
  ],
29
30
  },
30
31
  js.configs.recommended,