9.0 KiB
9.0 KiB
POL-001 Free/Pro entitlement verification
Date: 2026-08-15
Status: IMPLEMENTED — NOT VERIFIED. The server policy, worker rechecks, Free locked states and automated regressions pass. Real-browser, Stripe lifecycle, MariaDB and production checks remain unavailable.
Canonical policy
- External plan names are
freeandproonly. - Free retains core non-AI job tracking, deterministic match scoring, manual profile/CV editing, saved drafts, exports and existing AI history, but cannot start AI work.
- Pro and Admin use AI and Pro CV themes. The persisted Identity role remains
Premium, andStripe:PricePremiumremains a compatibility key; neither is exposed as a public plan name. - Current database roles are authoritative on every explicit HTTP AI action. A stale role claim cannot preserve access after downgrade.
- A locked explicit action returns HTTP 403 with
{ "code": "pro_required", "message": "This AI feature requires Pro." }. - Existing 250-call/1,000,000-token Pro ceilings remain because they are defined in the existing implementation roadmap. Free ceilings are zero. A content-free
AiUsageRecordledger is authoritative for AI Workspace, durable Strategy/CV work and every user-scoped generation through the shared synchronous provider boundary; legacyAiInteractionusage is backfilled. Health probes and non-generative text extraction are intentionally excluded.
Entry-point inventory
| Capability | User entry / frontend | API or worker execution path | Admission and recheck | Usage accounting | Free behavior |
|---|---|---|---|---|---|
| AI Workspace modules | Job details → AI Workspace; AiWorkspacePanel |
POST /api/jobapplications/{jobId}/ai/generate → AiWorkspaceService → ISummarizerService |
Pro policy with live role lookup |
Ledger reservation before generation; actual estimate finalized on success | Generate disabled; existing history/read/delete remain available |
| Candidate fit | Job details Candidate Fit and Strategy Snapshot | GET .../{id}/candidate-fit → attachment/correspondence context → multiple summarizer calls |
Pro policy plus shared provider admission |
One ledger row per provider generation | Deterministic match-score remains available; AI narrative locked |
| Focus plan | Job details Focus Plan and Strategy Snapshot | Durable strategy.snapshot operation → summarizer |
Pro admission plus worker recheck |
Atomic operation-ledger reservation; successful input/output estimate finalized | Locked; no synthetic fallback presented as generated |
| Interview brief | Job details Interview Prep | GET .../{id}/interview-prep/brief → summarizer |
Pro policy plus shared provider admission |
One ledger row per provider generation | Editable non-AI interview board remains available; generated brief locked |
| Tailored CV generation | Add Job option and job Tailored CV tab | POST .../{id}/generate-tailored-cv-draft → shared generation helpers → summarizer |
Pro policy plus shared provider admission |
One ledger row per provider generation | Job creation and manual tailored-draft editing remain available; no operation is started |
| Application package | Job workspace drafts | POST .../{id}/generate-application-package → attachment/email context → summarizer |
Pro policy plus shared provider admission |
One ledger row per provider generation | Existing/manual package drafts remain readable and editable |
| Follow-up draft | Job Follow-up tab | GET .../{id}/followup-draft → context → summarizer |
Pro policy plus shared provider admission |
One ledger row per provider generation | Manual correspondence data remains available; AI draft is locked |
| Job summary refresh | Job overview | POST .../{id}/refresh-ai → SummarizeAsync |
Pro policy plus shared provider admission |
One ledger row per provider generation | Existing summary/tags remain visible; refresh locked |
| Automatic job summary | Job create/detail | Core POST /jobapplications and GET /{id} optional summarizer calls |
Live role condition plus shared provider admission | One ledger row when a provider generation runs | Core request succeeds without calling AI |
| CV import/parse | Career Profile upload/parse/reprocess | /profile-cv/upload, /parse, /reprocess → durable cv.process operation |
Pro policy before admission; queued run rechecks live roles |
Atomic conservative operation-ledger reservation; no raw CV content | Manual profile editing and previous review runs remain available |
| CV rebuild/improve/rewrite/PDF | Career Profile AI buttons | /rebuild, /improve, /rewrite-section, /rewrite-preview, /export-pdf |
Pro policy; queued work rechecks roles and synchronous generation uses shared admission |
Durable operation reservation or synchronous provider ledger row | AI controls locked; manual profile data remains available |
| CV Builder writing aid | CV Builder AI Tools | POST /api/cv/ai/assist → summarizer |
Pro policy plus shared provider admission |
One ledger row per provider generation | AI buttons disabled; CV editing/history remain available |
| Pro CV themes | CV Builder Customize | GET /api/cv/themes; create/save validates selected theme |
Live role lookup in theme catalog checks | Not applicable | Pro themes identified and unavailable; existing unchanged selection can still be saved |
| Job enrichment worker | No direct UI; disabled by default | JobEnrichmentHostedService per owner |
Live role recheck plus shared provider admission immediately before summary; deterministic tag detection still runs for Free | One ledger row per provider generation | No model call; core tag enrichment remains possible |
| Admin AI probe | Admin system diagnostics | /api/admin/system/ai/probe |
Admin role; Admin maps to Pro | Health metric only | Not a Free user path |
| Periodic service probe | No user entry | summarizer health probe | No private/user payload; operational health only | Health metric only | Not a user AI capability |
| Attachment storage | Add-job/files UI | AttachmentsController storage check |
Central Free/Pro storage entitlement | Bytes stored | 250 MB Free; 5 GB Pro (existing defined capability) |
Automated evidence
ProEntitlementAuthorizationTests: Pro/Admin success, stale-claim downgrade failure, stable 403 body, and reflection inventory of all explicit AI actions.BackgroundWorkerTenantTests: Pro owners use fake AI; Free owners never call it.ProfileCvControllerTests: a queued CV run fails withpro_requiredsemantics after downgrade and never reaches the model.AccountPlansTests: Free zero AI, Pro/Admin AI, and onlyfree/proexternal names.- AI Workspace UI test: Free locked state, disabled generation and upgrade link.
AiUsageMeterTests, operation integration, account export/deletion and SQLite compatibility tests cover idempotent reservation, limits, owner isolation, history-independent totals, Strategy finalization, CV conservative reservation and lifecycle handling.MeteredSummarizerServiceTestsprove synchronous success finalization, pre-provider quota rejection, Free-user rejection, workspace/operation double-count suppression and stable HTTP 429 problem details.BillingControllerTestsuse an in-process Stripe gateway fake to prove checkout price/user metadata, signed active → expired → canceled/replayed role transitions, non-AI data preservation and fail-closed rejection of aprod_product identifier in the price setting.- Full entitlement/billing slice: 33/33; full backend: 677/677.
- Full backend: 568/568.
- Full frontend: 47/47 suites, 157/157 tests.
- Production frontend build: pass.
git diff --check: no whitespace errors; existing line-ending notices only.
Limitations and remaining checks
- Browser localhost access is denied by the available browser policy, so 375/768/1440, keyboard, themes and actual navigation to the upgrade action are not claimed.
- The repository Stripe lifecycle is covered with a fake gateway and no network call. Actual Stripe Checkout, portal configuration, signature delivery and production role mapping still require the authorized external account.
- MariaDB and production were not changed or tested.
- PRODUCT-001 removed landing-page prices, the third “Bring your own key” tier, Free AI allowance and “Unlimited AI” claims. Public capability copy now comes from one two-plan catalogue; commercial terms remain in configured Stripe Checkout.
- The durable ledger spans AI Workspace, Strategy Snapshot, CV processing and all user-scoped calls through
ISummarizerService. Failed or empty provider attempts retain their conservative reservation because they may still have consumed provider capacity; successful generations replace it with measured input/output. Health probes and extraction-only calls are not user generation usage.
Rollback
Revert the policy registrations, action attributes, worker checks and frontend plan context together. No schema or dependency change is involved. Keep workers disabled during rollback; reverting only the worker rechecks would restore a downgrade bypass.