Triage full-eval findings into Current state: work queue / known debt / deferred

This commit is contained in:
Keysat
2026-06-12 21:58:43 -05:00
parent ac2aa85b7e
commit be7dfa5d8c
+55 -24
View File
@@ -117,28 +117,59 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
product's sales to them. The data model + resolver fully support it; only the product's sales to them. The data model + resolver fully support it; only the
product→profile **write path** is missing. **This is the gating piece for product→profile **write path** is missing. **This is the gating piece for
multi-profile** — see the scoped slice below. multi-profile** — see the scoped slice below.
- **Next, in priority order**: - **Triage from `EVALUATION.md` (full-eval, 2026-06-13)** — P0/P1 = work queue,
(1) **Product→merchant-profile picker** (gating piece above). Slice: P2 = known debt, P3+ = deferred. The report at repo root has file:line evidence.
add `merchant_profile_id: Option<String>` to the `Product` model + its SELECT
column mapping in `repo.rs`; add a `set_product_merchant_profile` follow-up - **Work queue (P0/P1 — do first, in this order)**:
writer mirroring `set_product_entitlements_catalog`; add the field to 1. **[P1] Provider-injection test seam — BEFORE #2's fix.** The prod purchase/
`CreateProductReq`/`UpdateProductReq` (`api/admin.rs`) applied as a post-create/ settle path has no mock seam (the mock injects into the dead `state.payment`
update follow-up; add a profile `<select>` (populated from singleton, not `resolve_provider_for_profile_rail` — this is how the `:52`
`GET /v1/admin/merchant-profiles`) to the create + edit product forms in 500 shipped). Add an always-compiled `Option<Arc<dyn PaymentProvider>>`
`web/index.html`, rendered only when >1 profile exists. No migration (column override on `AppState`, checked first in `resolve_provider_for_profile_rail`.
exists since 0020). Default/None → stays on default profile. Greens the two `paid_purchase_*` red tests and gives #2 a regression harness.
(2) resolve the 3 red tests — **delete** the dead **Delete** the dead `payment_provider_preference_round_trip` in the same pass.
`payment_provider_preference_round_trip`; for the two `paid_purchase_*`, add a 2. **[P0] Zaprite webhook forgery.** The settle-webhook is unauthenticated — a
provider-injection seam (recommended: an always-compiled forged `order.change`/`status=PAID` with a buyer-visible order id mints a
`Option<Arc<dyn PaymentProvider>>` override on `AppState`, checked first in signed license (the `externalUniqId` "trust anchor" in the comments is never
`resolve_provider_for_profile_rail`; alt: gate a mock behind a `test-mocks` read in the webhook path). Fix: on settle, re-fetch `get_invoice_status` from
cargo feature). See `docs/guides/testing.md`. the provider and require local `pending` state + matching amount/currency
(3) build the other 3 deferred UIs (rail picker, per-profile SMTP, rail-pref before issuing (mirror the reconciler's safe re-fetch). Closes the P1 below
editor) + add `unlimited_merchant_profiles` to master Pro/Patron policies; too. `payment/zaprite/provider.rs:234-362`, `api/webhook.rs:62-196,121-194`.
(4) re-register the master Zaprite webhook; (5) optional: run formatters as a 3. **[P1] Scoped API keys (`ks_…`) are non-functional** — issuable but 403 on
standalone commit. every admin endpoint; the `require_admin``require_scope` migration was never
done. Finish it, or stop advertising/issuing them. `api/api_keys.rs:14`.
- **Then resume feature work**: the **product→merchant-profile picker** (the GAP
above — slice: add `merchant_profile_id` to the `Product` model + `repo.rs`
SELECT mapping; a `set_product_merchant_profile` follow-up writer mirroring
`set_product_entitlements_catalog`; the field on `CreateProductReq`/
`UpdateProductReq` applied post-write; a profile `<select>` from
`GET /v1/admin/merchant-profiles` in the create+edit product forms, shown only
when >1 profile; no migration), the 3 other deferred UIs (rail picker,
per-profile SMTP, rail-pref editor), and `unlimited_merchant_profiles` on
master Pro/Patron policies.
- **Known debt (P2 — schedule, not urgent)**: no rate-limit on `/v1/purchase` +
`/v1/redeem`; rate-limit bucket keys on spoofable `X-Forwarded-For` (bypass
conditional on whether the StartOS proxy rewrites XFF — unverified); `422`/`415`
errors return plain-text not JSON (breaks SDK `JSON.parse`); product `slug` has
no validation (empty/300-char/meta chars stored); `GET /v1/admin/products`
returns 405 though OpenAPI documents it; dep advisories (`sqlx`→≥0.8.1
RUSTSEC-2024-0363, `rustls-webpki`→≥0.103.12); **4 StartOS submission blockers**
missing `instructions.md`, dead `packageRepo` (`…/keysat-startos``…/keysat`) +
`docsUrls` (`/docs/``/licensing-service/docs/`) manifest links, aarch64
declared-but-not-shipped; no CI + fmt/clippy/prettier unenforced.
- **Deferred (P3+ — bulk or later decision)**: `/v1/purchase` 400 vs
`/v1/btcpay/webhook` 503 for the same no-provider cause; undocumented required
`kind` on discount-codes; field-naming drift (`license_id`/`id`, machines `key`
vs `license_key`, `redeem`/`purchase` `product` vs `validate` `product_slug`);
migration self-heal `_sqlx_migrations` allowlist foot-gun; 2 KB unauth Zaprite
payload WARN-log; outbound-webhook SSRF (operator-only); stale
`versions/v0.2.0.ts:3-4` "NOT YET WIRED" comment; re-register the master Zaprite
webhook at the path-keyed URL; registry icon non-render (known platform limit);
optional fmt/prettier standalone commit.
- **Tests/build**: `cargo check` clean (1 intentional deprecation warning); api - **Tests/build**: `cargo check` clean (1 intentional deprecation warning); api
43 pass / 3 known-fail (test-debt), other suites green. No CI; fmt/prettier not 43 pass / 3 known-fail (now tracked in the work queue above), other suites
enforced or clean. FK enforcement **confirmed** the sqlx pool sets green. FK enforcement **confirmed** — sqlx pool sets `foreign_keys(true)` per
`foreign_keys(true)` per connection (`db/mod.rs`); the old "latent/unchecked" connection (`db/mod.rs`). CI/fmt status is in Known debt.
caveat is resolved.