checking 658d5f112307bd6aa285e5d34d51b754acc4c19f (gitleaks range: c835213c4af3416e03b12c7a09877aff6505e247..658d5f112307bd6aa285e5d34d51b754acc4c19f) == api: npm ci added 252 packages in 11s == api: npm ci: ok in 11s == api: typecheck == api: typecheck: ok in 28s == api: test  RUN  v4.1.2 /data/work/apps/api โœ“ tests/sprint2/schema-metadata.test.ts (154 tests | 1 todo) 189ms stderr | tests/routes/shopify-oauth.test.ts > shopify oauth install > GET /shopify/oauth/callback > still completes the install when a webhook subscription fails [shopify-oauth] acme.myshopify.com installed, but 11 webhook topic(s) could not be subscribed: products/create, products/update, products/delete, orders/create, orders/updated, orders/cancelled, fulfillments/create, fulfillments/update, refunds/create, inventory_levels/update, app/uninstalled. The store still syncs on the crons. โœ“ tests/routes/shopify-oauth.test.ts (17 tests) 213ms stderr | tests/sprint7/amazon-targets.test.ts > runAmazonSyncAcrossTargets > runs every account even when one of them throws, and still reports the failure [test-job] Sync failed for SELLER-A (conn-a): token expired โœ“ tests/sprint7/amazon-targets.test.ts (28 tests) 93ms stderr | tests/sprint7/shopify-targets.test.ts > runShopifySyncAcrossTargets > a failing store does not stop the ones after it [test-job] Sync failed for store-a.myshopify.com (conn-a): store a is down stderr | tests/sprint7/shopify-targets.test.ts > runShopifySyncAcrossTargets > still throws for a single store, preserving the old failure signal [test-job] Sync failed for store-a.myshopify.com (conn-a): boom stdout | tests/sprint7/shopify-targets.test.ts > a scoped sync run > does nothing for a workspace with no stores, rather than syncing someone elseโ€™s [test-job] Skipped โ€” no Shopify connection configured โœ“ tests/sprint7/shopify-targets.test.ts (25 tests) 177ms โœ“ tests/sprint2/isolation-live.test.ts (9 tests | 7 skipped) 120ms stderr | tests/middleware/require-permission.test.ts > requirePermission โ€” proxy-trust enforcement (forged header defence) > fails closed when the deployment has no TSC_PROXY_SECRET configured at all [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity โœ“ tests/middleware/require-permission.test.ts (16 tests) 282ms stderr | tests/services/shopify-webhook-multistore.test.ts > an OAuth-installed store > still rejects a delivery signed with the environment secret [shopify-webhook] Invalid HMAC for webhook wh-1 topic=products/update stderr | tests/services/shopify-webhook-multistore.test.ts > a store this deployment does not know > is rejected, and never borrows the environment secret or its company id [shopify-webhook] Webhook wh-1 from unknown store stranger.myshopify.com stderr | tests/services/shopify-webhook-multistore.test.ts > a store this deployment does not know > is answered identically to a bad signature, so the response confirms nothing [shopify-webhook] Webhook wh-1 from unknown store stranger.myshopify.com stderr | tests/services/shopify-webhook-multistore.test.ts > a store this deployment does not know > is answered identically to a bad signature, so the response confirms nothing [shopify-webhook] Invalid HMAC for webhook wh-1 topic=products/update stderr | tests/services/shopify-webhook-multistore.test.ts > app/uninstalled > still requires a valid signature [shopify-webhook] Invalid HMAC for webhook wh-uninstall topic=app/uninstalled stderr | tests/services/shopify-webhook-multistore.test.ts > app/uninstalled > acks even when the connection cannot be disabled, so Shopify stops retrying [shopify-webhook] Could not disable connection conn-acme after uninstall: already archived โœ“ tests/services/shopify-webhook-multistore.test.ts (15 tests) 18ms Sourcemap for "/data/work/apps/api/node_modules/node-cron/dist/esm/node-cron.js" points to missing source files stdout | tests/routes/amazon-connect.test.ts > POST /amazon/connect > stores the three secrets, writes the account row and activates once SP-API accepts [sync-amazon-products] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-inventory] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-orders] Starting sync for amazon A1SELLERTOKEN... stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > stores the three secrets, writes the account row and activates once SP-API accepts [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > stores the three secrets, writes the account row and activates once SP-API accepts [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function stdout | tests/routes/amazon-connect.test.ts > POST /amazon/connect > defaults the US marketplace coordinates when they are not supplied [sync-amazon-products] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-inventory] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-orders] Starting sync for amazon A1SELLERTOKEN... stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > defaults the US marketplace coordinates when they are not supplied [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > defaults the US marketplace coordinates when they are not supplied [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function stdout | tests/routes/amazon-connect.test.ts > POST /amazon/connect > adopts the existing connection and keeps its company_id when a seller reconnects [sync-amazon-products] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-inventory] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-orders] Starting sync for amazon A1SELLERTOKEN... stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > adopts the existing connection and keeps its company_id when a seller reconnects [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect > adopts the existing connection and keeps its company_id when a seller reconnects [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function stdout | tests/routes/amazon-connect.test.ts > POST /amazon/connect โ€” a seller belongs to exactly one workspace > never writes organization_id on conflict, and only updates a row this workspace owns [sync-amazon-products] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-inventory] Starting sync for amazon A1SELLERTOKEN... [sync-amazon-orders] Starting sync for amazon A1SELLERTOKEN... stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect โ€” a seller belongs to exactly one workspace > never writes organization_id on conflict, and only updates a row this workspace owns [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function [event-bus] Webhook dispatch error: subs.filter is not a function stderr | tests/routes/amazon-connect.test.ts > POST /amazon/connect โ€” a seller belongs to exactly one workspace > never writes organization_id on conflict, and only updates a row this workspace owns [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function [amazon/connect] first sync failed for connection=conn-amazon-1: __vite_ssr_import_1__.db.update is not a function โœ“ tests/routes/amazon-connect.test.ts (10 tests) 188ms stderr | tests/routes/developer-auth.test.ts > developer key creation scope escalation > allows admin scoped key to POST /developer/keys [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity stderr | tests/routes/developer-auth.test.ts > developer key routes are scoped to the caller organization (S6-03) > refuses to mint a key for another organization [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity stderr | tests/routes/developer-auth.test.ts > developer key routes are scoped to the caller organization (S6-03) > mints for the caller organization when tenant_id matches [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity stderr | tests/routes/developer-auth.test.ts > developer key routes are scoped to the caller organization (S6-03) > binds the key to the caller organization, not the QB_TENANT_ID fallback [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity stderr | tests/routes/developer-auth.test.ts > developer key routes are scoped to the caller organization (S6-03) > returns 404 โ€” not 403 โ€” updating a key outside the caller organization [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity stderr | tests/routes/developer-auth.test.ts > developer key routes are scoped to the caller organization (S6-03) > returns 404 revoking a key outside the caller organization [rbac] TSC_PROXY_SECRET is not configured โ€” refusing to trust any proxy-asserted member identity โœ“ tests/routes/developer-auth.test.ts (15 tests) 270ms โœ“ tests/sprint7/health-probe.test.ts (19 tests) 22ms stderr | tests/dashboard-tsc-proxy.test.ts > dashboard /api/tsc proxy > returns 401 when the session token is rejected by Basefyio [tsc api proxy] session verification rejected by issuer: undefined stderr | tests/dashboard-tsc-proxy.test.ts > dashboard /api/tsc proxy > returns 502 on upstream failure without leaking the key in the error body [tsc api proxy] Error: connect ECONNREFUSED using super-secret-dashboard-key-should-never-leak at /data/work/apps/api/tests/dashboard-tsc-proxy.test.ts:205:30 at file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:302:11 at file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:1893:26 at file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:2316:20 at new Promise () at runWithCancel (file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:2313:10) at file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:2295:20 at new Promise () at runWithTimeout (file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:2262:10) at file:///data/work/apps/api/node_modules/@vitest/runner/dist/chunk-artifact.js:2945:64 โœ“ tests/dashboard-tsc-proxy.test.ts (11 tests) 480ms โœ“ returns 401 without ever calling upstream when there is no session  320ms โœ“ tests/sprint3/vault-crypto.test.ts (17 tests) 127ms โœ“ tests/routes/amazon-read-scope.test.ts (7 tests) 136ms โœ“ tests/proxy-write-path.test.ts (5 tests) 129ms โœ“ tests/routes/commerce-products.test.ts (14 tests) 17ms stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks customer create (POST /customers) with no credential at all [qbo-write-gate] blocked POST /customers โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks customer create even when authenticated as the global key [qbo-write-gate] blocked POST /customers โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks customer create even with a fully-scoped tenant key [qbo-write-gate] blocked POST /customers โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks item update (PUT /items/5291) with no credential at all [qbo-write-gate] blocked PUT /items/5291 โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks item update even when authenticated as the global key [qbo-write-gate] blocked PUT /items/5291 โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks item update even with a fully-scoped tenant key [qbo-write-gate] blocked PUT /items/5291 โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks invoice write (POST /invoices) with no credential at all [qbo-write-gate] blocked POST /invoices โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks invoice write even when authenticated as the global key [qbo-write-gate] blocked POST /invoices โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks invoice write even with a fully-scoped tenant key [qbo-write-gate] blocked POST /invoices โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks payment write (POST /payments) with no credential at all [qbo-write-gate] blocked POST /payments โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks payment write even when authenticated as the global key [qbo-write-gate] blocked POST /payments โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks payment write even with a fully-scoped tenant key [qbo-write-gate] blocked POST /payments โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks refund-receipt delete (DELETE /refund-receipts/74524) with no credential at all [qbo-write-gate] blocked DELETE /refund-receipts/74524 โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks refund-receipt delete even when authenticated as the global key [qbo-write-gate] blocked DELETE /refund-receipts/74524 โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-write-gate.test.ts > Q0.1 โ€” QuickBooks write kill switch > blocks refund-receipt delete even with a fully-scoped tenant key [qbo-write-gate] blocked DELETE /refund-receipts/74524 โ€” QBO_WRITES_ENABLED is not 'true' โœ“ tests/sprint7/require-operator.test.ts (23 tests) 216ms โœ“ tests/security/qbo-write-gate.test.ts (42 tests) 402ms โ†“ tests/sprint4/queue-live.test.ts (4 tests | 4 skipped) โœ“ tests/sprint3/schema-metadata.test.ts (17 tests) 107ms โœ“ tests/sprint7/amazon-account-context.test.ts (14 tests) 25ms โœ“ tests/routes/finance.test.ts (11 tests) 15ms stderr | tests/routes/system-health.test.ts > GET /system/health > never echoes the raw DB error back to an unauthenticated caller (this route has no auth) [system-health] DB check failed: connection to server at "10.0.0.5", port 5432 failed: FATAL: password authentication failed for user "prod_admin" โœ“ tests/routes/system-health.test.ts (10 tests) 115ms โœ“ tests/commerce-query-shaping.test.ts (21 tests) 77ms stderr | tests/security/qbo-mutation-audit.test.ts > Q0 follow-up โ€” QuickBooks mutation audit > records a refused attempt โ€” the event the gate would otherwise hide [qbo-write-gate] blocked POST /customers โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-mutation-audit.test.ts > Q0 follow-up โ€” QuickBooks mutation audit > audits before the gate, so ordering in index.ts cannot silently regress [qbo-write-gate] blocked POST /invoices โ€” QBO_WRITES_ENABLED is not 'true' stderr | tests/security/qbo-mutation-audit.test.ts > Q0 follow-up โ€” QuickBooks mutation audit > refuses the request when the attempt cannot be recorded (fail closed) [qbo-mutation-audit] refusing request โ€” cannot record attempt: audit store unavailable โœ“ tests/security/qbo-mutation-audit.test.ts (10 tests) 388ms โœ“ tests/routes/gateway.test.ts (8 tests) 214ms โœ“ tests/sprint7/bootstrap-amazon-account.test.ts (21 tests) 168ms โœ“ tests/cron/sync-amazon-products.test.ts (8 tests) 103ms โœ“ tests/index-rate-limit-coverage.test.ts (9 tests) 12ms stderr | tests/lib/rate-limit-store-pg.test.ts > Postgres rate limit store > FAILS OPEN when Postgres is unreachable, rather than blocking every caller {"level":"error","msg":"rate_limit_store_pg_error","timestamp":"2026-10-14T07:11:35.632Z","error":"connection refused"} stderr | tests/lib/rate-limit-store-pg.test.ts > rate limit store selection > falls back to memory on an unrecognised value rather than booting unlimited {"level":"warn","msg":"rate_limit_store_unknown_kind","timestamp":"2026-10-14T07:11:35.637Z","value":"redis","fallback":"memory"} {"level":"warn","msg":"rate_limit_store_unknown_kind","timestamp":"2026-10-14T07:11:35.637Z","value":"typo","fallback":"memory"} โœ“ tests/lib/rate-limit-store-pg.test.ts (13 tests) 17ms stderr | tests/middleware/rate-limit-classes.test.ts > rate-limit policy classes > fails OPEN when the store throws, rather than taking the route down {"level":"error","msg":"rate_limit_store_failure","timestamp":"2026-10-14T07:11:36.841Z","policy":"standard","error":"store unreachable"} โœ“ tests/middleware/rate-limit-classes.test.ts (9 tests) 218ms stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > THE ACCEPTANCE TEST โ€” tenant A credential against tenant B realm is rejected before any QuickBooks request [qbo-realm-binding] refused POST /customers: REALM_MISMATCH โ€” This deployment is bound to a different QuickBooks realm than the caller's organization stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the organization has no QuickBooks connection โ€” no environment fallback [qbo-realm-binding] refused POST /customers: NO_QBO_CONNECTION โ€” No QuickBooks connection exists for this organization in the sandbox environment stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the connection exists but is not active [qbo-realm-binding] refused POST /customers: CONNECTION_NOT_ACTIVE โ€” QuickBooks connection is pending, which cannot authorize a write stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the connection exists but is not active [qbo-realm-binding] refused POST /customers: CONNECTION_NOT_ACTIVE โ€” QuickBooks connection is degraded, which cannot authorize a write stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the connection exists but is not active [qbo-realm-binding] refused POST /customers: CONNECTION_NOT_ACTIVE โ€” QuickBooks connection is disabled, which cannot authorize a write stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the connection exists but is not active [qbo-realm-binding] refused POST /customers: CONNECTION_NOT_ACTIVE โ€” QuickBooks connection is revoked, which cannot authorize a write stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the connection exists but is not active [qbo-realm-binding] refused POST /customers: CONNECTION_NOT_ACTIVE โ€” QuickBooks connection is archived, which cannot authorize a write stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when the organization has more than one active realm [qbo-realm-binding] refused POST /customers: REALM_MISMATCH โ€” Organization has 2 active QuickBooks realms; the request does not identify which one to write to stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > refuses when no organization context is resolved [qbo-realm-binding] refused POST /customers: NO_ORGANIZATION_CONTEXT โ€” No organization context resolved for this request stderr | tests/security/qbo-realm-binding.test.ts > Q0 acceptance ยง5.1 โ€” tenant-to-realm binding > fails closed when the connection lookup errors [qbo-realm-binding] refused POST /customers: LOOKUP_FAILED โ€” connection pool exhausted โœ“ tests/security/qbo-realm-binding.test.ts (9 tests) 285ms stdout | tests/middleware/require-api-key.test.ts > requireApiKey > 401s on unknown key that does not match env and lacks tsc_live_ prefix [api-key] 401 mismatch: unknown key format (len=11) stderr | tests/middleware/organization-context.test.ts > resolveOrganizationContext > leaves organizationContext unset when the default tenant row is missing [organization-context] transitional default tenant slug="us-gourmet" not found in tenants table โœ“ tests/middleware/organization-context.test.ts (9 tests) 275ms โœ“ tests/middleware/require-api-key.test.ts (11 tests) 288ms โœ“ tests/routes/route-structure.test.ts (32 tests) 187ms stderr | tests/sprint3/service-account-token.test.ts > mintToken / verifyToken round trip > returns null when the audience does not match [service-account] token rejected: wrong audience stderr | tests/sprint3/service-account-token.test.ts > mintToken / verifyToken round trip > returns null when the token has expired [service-account] token rejected: expired stderr | tests/sprint3/service-account-token.test.ts > mintToken / verifyToken round trip > returns null when the signature has been tampered with [service-account] token rejected: bad signature stderr | tests/sprint3/service-account-token.test.ts > mintToken / verifyToken round trip > returns null when the signing key for the claimed key_version is not configured [service-account] token rejected: no signing key for key_version=2 stderr | tests/sprint3/service-account-token.test.ts > mintToken / verifyToken round trip > returns null when the service account is deny-listed for that key_version [service-account] token rejected: deny-listed for key_version=1 stderr | tests/sprint3/service-account-token.test.ts > requireServiceAccount middleware > responds 401 for an invalid/malformed token [service-account] token rejected: malformed (expected 3 segments) stderr | tests/sprint3/service-account-token.test.ts > requireServiceAccount middleware > responds 401 when the token was minted for a different audience [service-account] token rejected: wrong audience โœ“ tests/sprint3/service-account-token.test.ts (10 tests) 187ms stdout | tests/routes/auth-workspace-binding.test.ts > GET /auth/callback > records the company against the workspace the state token carries [auth] Tokens saved to DB successfully { realmId: '123145825371274' } stderr | tests/routes/auth-workspace-binding.test.ts > GET /auth/callback > records no connection when the state carries no workspace, rather than guessing one [auth] Realm 123145825371274 authorized through the unbound /auth redirect โ€” no workspace to attribute it to. Start from the dashboard Connections screen to bind it to one. stdout | tests/routes/auth-workspace-binding.test.ts > GET /auth/callback > records no connection when the state carries no workspace, rather than guessing one [auth] Tokens saved to DB successfully { realmId: '123145825371274' } stdout | tests/routes/auth-workspace-binding.test.ts > GET /auth/callback > adopts an existing connection for the same realm instead of creating a second [auth] Tokens saved to DB successfully { realmId: '123145825371274' } stderr | tests/routes/auth-workspace-binding.test.ts > GET /auth/callback > saves nothing when the state token is rejected [auth] OAuth state rejected: already_consumed โœ“ tests/routes/auth-workspace-binding.test.ts (4 tests) 194ms โœ“ tests/middleware/dashboard-cors.test.ts (13 tests) 175ms โœ“ tests/lib/connections/status.test.ts (13 tests) 10ms โœ“ tests/cron/sync-items-diff.test.ts (15 tests) 16ms โœ“ tests/sprint5/rollback-triggers.test.ts (16 tests) 77ms โœ“ tests/sprint4/enqueue-and-dead-letter.test.ts (5 tests) 84ms โœ“ tests/storage-credentials.test.ts (6 tests) 219ms โœ“ tests/routes/me.test.ts (7 tests) 128ms stderr | tests/services/sync-tracker-dead-letters.test.ts > resolveDeadLettersNowPresent > treats a missing table as a skip rather than a failed sync [sync-tracker] dead-letter reconciliation skipped for not_migrated_yet: relation "not_migrated_yet" does not exist โœ“ tests/services/sync-tracker-dead-letters.test.ts (7 tests) 85ms โœ“ tests/routes/tenants-org-isolation.test.ts (10 tests) 199ms โœ“ tests/routes/items-brand.test.ts (7 tests) 100ms โœ“ tests/sprint7/client-factory.test.ts (9 tests) 69ms โœ“ tests/sprint5/oauth-state.test.ts (8 tests) 98ms โœ“ tests/sprint3/credential-provider.test.ts (9 tests) 25ms โ†“ tests/sprint3/credential-store-live.test.ts (3 tests | 3 skipped) โœ“ tests/lib/connections/quickbooks-targets.test.ts (6 tests) 73ms โœ“ tests/routes/commerce-orders.test.ts (7 tests) 10ms โœ“ tests/routes/connections-disconnect.test.ts (4 tests) 108ms โœ“ tests/routes/commerce-product-detail.test.ts (5 tests) 12ms โœ“ tests/routes/connections.test.ts (4 tests) 108ms stderr | tests/sprint5/auth-route.test.ts > GET /auth (src/routes/auth.ts) โ€” hardened OAuth initiation > sends the person back to the connector page, saying so, when the app is not configured [auth] QuickBooks OAuth is not configured: set QB_CLIENT_ID and QB_CLIENT_SECRET, and register https://api.tsmartconnect.com/auth/callback as a redirect URI on the Intuit app. stderr | tests/sprint5/auth-route.test.ts > GET /auth (src/routes/auth.ts) โ€” hardened OAuth initiation > treats a missing client secret the same as a missing client id [auth] QuickBooks OAuth is not configured: set QB_CLIENT_ID and QB_CLIENT_SECRET, and register https://api.tsmartconnect.com/auth/callback as a redirect URI on the Intuit app. stderr | tests/sprint5/auth-route.test.ts > GET /auth/callback (src/routes/auth.ts) โ€” one-time state verification > rejects an invalid/expired/replayed state without ever exchanging the code [auth] OAuth state rejected: expired stderr | tests/sprint5/auth-route.test.ts > GET /auth/callback (src/routes/auth.ts) โ€” one-time state verification > proceeds to token exchange only once the state is confirmed valid [auth] QB token exchange failed: stop here โ€” this test only checks that exchange was attempted โœ“ tests/sprint5/auth-route.test.ts (6 tests) 103ms โœ“ tests/scripts/drizzle-migration-guards.test.ts (12 tests) 77ms โœ“ tests/scripts/bootstrap-shopify-store.test.ts (14 tests) 78ms โœ“ tests/sprint4/webhook-watermark.test.ts (11 tests) 82ms โœ“ tests/sprint5/qb-connector.test.ts (9 tests) 85ms โœ“ tests/dashboard-no-exposed-api-key.test.ts (131 tests) 190ms stdout | tests/cron/sync-item-stock.test.ts > pollItemStock > rewrites only items whose quantity moved, with the same SyncToken [sync-item-stock] checked=2 updated=1 failed=0 stdout | tests/cron/sync-item-stock.test.ts > pollItemStock > emits item.updated only when the item itself was edited, not for a quantity that moved [sync-item-stock] checked=2 updated=2 failed=0 stdout | tests/cron/sync-item-stock.test.ts > pollItemStock > skips the tick while the nightly item sync is running [sync-item-stock] Nightly item sync is running โ€” skipping this tick stdout | tests/cron/sync-item-stock.test.ts > pollItemStock > keeps going after one item fails to write [sync-item-stock] checked=2 updated=1 failed=1 โœ“ tests/cron/sync-item-stock.test.ts (6 tests) 11ms โœ“ tests/routes/items-detail.test.ts (3 tests) 102ms stderr | tests/lib/rate-limit-metrics.test.ts > rate limit telemetry > counts fail-open when the store throws โ€” the silent-outage signal {"level":"error","msg":"rate_limit_store_failure","timestamp":"2026-10-14T07:12:02.047Z","policy":"standard","error":"unreachable"} stderr | tests/lib/rate-limit-metrics.test.ts > rate limit telemetry > counts fail-open when the store throws โ€” the silent-outage signal {"level":"error","msg":"rate_limit_store_failure","timestamp":"2026-10-14T07:12:02.055Z","policy":"standard","error":"unreachable"} stderr | tests/lib/rate-limit-metrics.test.ts > rate limit telemetry > counts the Postgres store's INTERNAL fail-open, which never reaches the middleware catch {"level":"error","msg":"rate_limit_store_pg_error","timestamp":"2026-10-14T07:12:02.117Z","error":"connection refused"} โœ“ tests/lib/rate-limit-metrics.test.ts (7 tests) 313ms โœ“ tests/security/qbo-write-gate-coverage.test.ts (5 tests) 106ms โœ“ tests/middleware/security-headers.test.ts (12 tests) 104ms โœ“ tests/dashboard/gateway-operator-action.test.ts (12 tests) 86ms โœ“ tests/routes/commerce-customers.test.ts (6 tests) 75ms โœ“ tests/security/shopify-read-scope-coverage.test.ts (6 tests) 78ms โœ“ tests/routes/commerce-inventory.test.ts (6 tests) 11ms โœ“ tests/sprint3/logger.test.ts (7 tests) 106ms โœ“ tests/billing-plans.test.ts (8 tests) 11ms โœ“ tests/routes/cohorts.test.ts (7 tests) 74ms stderr | tests/cron/catalog-webhook-dispatcher.test.ts > deliverWithRetry > retries 3 times on failure (4 total attempts) with 1s/5s/30s backoff [catalog-webhook-dispatcher] Delivery failed after 3 retries to https://x.example.com: HTTP 500 โœ“ tests/cron/catalog-webhook-dispatcher.test.ts (5 tests) 12ms stderr | tests/routes/tenants-members.test.ts > DELETE /tenants/:id/members/:memberId > removes an existing member [tenants] audit log write failed for member.remove: dup โœ“ tests/routes/tenants-members.test.ts (7 tests) 298ms โœ“ tests/sprint4/registry-and-concurrency.test.ts (13 tests) 101ms โœ“ tests/routes/sync-status-summary.test.ts (3 tests) 109ms โœ“ tests/sprint3/connection-status.test.ts (9 tests) 87ms stdout | tests/qb-client-boot.test.ts > loadTokensFromDb > loads and seeds tokens when the table exists and has a row [qb-client] Loaded tokens from database stderr | tests/qb-client-boot.test.ts > loadTokensFromDb > loads and seeds tokens when the table exists and has a row [token-vault] read a plaintext token โ€” it will be sealed on next refresh { realmId: 'r1' } [token-vault] read a plaintext token โ€” it will be sealed on next refresh { realmId: 'r1' } stderr | tests/qb-client-boot.test.ts > loadTokensFromDb > degrades gracefully (no throw) on Postgres undefined_table (42P01), and flags schema missing [qb-client] oauth_tokens table not found (relation does not exist) โ€” schema not yet migrated. Starting with QuickBooks disconnected. stderr | tests/qb-client-boot.test.ts > loadTokensFromDb > starts disconnected when the stored token cannot be decrypted, rather than taking the process down [qb-client] Stored token for realm r1 could not be decrypted (Unsupported state or unable to authenticate data). Starting with QuickBooks disconnected โ€” reconnect at https://api.tsmartconnect.com/auth to re-seal it under this key. โœ“ tests/qb-client-boot.test.ts (6 tests) 1686ms โœ“ loads and seeds tokens when the table exists and has a row  1287ms stdout | tests/cron/sync-invoices-paging.test.ts > syncInvoices paging > stores every invoice of a page before fetching the next page [sync-invoices] Starting sync... stdout | tests/cron/sync-invoices-paging.test.ts > syncInvoices paging > stores every invoice of a page before fetching the next page [sync-invoices] Sync complete. created=0 updated=0 failed=0 stdout | tests/cron/sync-invoices-paging.test.ts > syncInvoices paging > counts invoices across all pages and completes the run [sync-invoices] Starting sync... stdout | tests/cron/sync-invoices-paging.test.ts > syncInvoices paging > counts invoices across all pages and completes the run [sync-invoices] Sync complete. created=0 updated=0 failed=0 stdout | tests/cron/sync-invoices-paging.test.ts > syncInvoices paging > keeps the pages already stored when a later page fails, and fails the run [sync-invoices] Starting sync... โœ“ tests/cron/sync-invoices-paging.test.ts (3 tests) 178ms โœ“ tests/middleware/rate-limit.test.ts (4 tests) 284ms โœ“ tests/routes/webhooks-deliveries.test.ts (5 tests) 194ms โœ“ tests/routes/tenants-list-scoping.test.ts (3 tests) 290ms โœ“ tests/storage-public-url.test.ts (4 tests) 92ms โœ“ tests/no-quickbooks-deletion.test.ts (2 tests) 9ms โœ“ tests/developer-openapi.test.ts (8 tests) 20ms โœ“ tests/sprint5/connector-errors.test.ts (7 tests) 79ms โœ“ tests/index-organization-context-wiring.test.ts (6 tests) 71ms โœ“ tests/sprint7/schema-metadata.test.ts (6 tests) 22ms stderr | tests/token-vault.test.ts > token vault > still reads a legacy plaintext row [token-vault] read a plaintext token โ€” it will be sealed on next refresh { realmId: '9341454000000000' } โœ“ tests/token-vault.test.ts (6 tests) 29ms โœ“ tests/sprint3/plaintext-scan.test.ts (4 tests | 1 todo) 26ms โœ“ tests/sprint4/error-classifier.test.ts (11 tests) 69ms โœ“ tests/routes/health-version.test.ts (4 tests) 41ms โœ“ tests/middleware/cors.test.ts (6 tests) 93ms โœ“ tests/middleware/scope.test.ts (11 tests) 197ms โœ“ tests/routes/sync-status-history.test.ts (3 tests) 27ms โœ“ tests/middleware/read-only.test.ts (5 tests) 86ms โœ“ tests/sprint4/cron-job-metadata-completeness.test.ts (4 tests) 14ms โœ“ tests/lib/rbac/permissions.test.ts (6 tests) 10ms โœ“ tests/routes/reports-inventory-valuation.test.ts (4 tests) 28ms โœ“ tests/security/amazon-read-scope-coverage.test.ts (4 tests) 9ms โœ“ tests/routes/export.test.ts (4 tests) 101ms stderr | tests/routes/sync-status-retry.test.ts > POST /sync-status/retry/:entity (Sprint 6 RBAC v1: gated + audited resync) > dispatches the sync and writes an audit log entry, without crashing when the audit write fails [retry] audit log write failed for customers: audit_logs table missing โœ“ tests/routes/sync-status-retry.test.ts (3 tests) 193ms stdout | tests/services/boot-sync.test.ts > bootSync > skips the sync when a success is fresher than the window [sync-items] Recent successful sync โ€” skipping the boot-time run stderr | tests/services/boot-sync.test.ts > bootSync > never lets a failing boot sync reject out of the caller [sync-items] Initial sync failed: 429 โœ“ tests/services/boot-sync.test.ts (4 tests) 81ms โœ“ tests/routes/sync-trigger.test.ts (2 tests) 92ms โœ“ tests/shopify-scopes.test.ts (4 tests) 11ms โœ“ tests/queue/cancel-dead-letter.test.ts (3 tests) 76ms โœ“ tests/routes/qb-error.test.ts (5 tests) 11ms โœ“ tests/sprint3/connection-lifecycle.test.ts (31 tests) 16ms โœ“ tests/lib/connections/credential-expiry.test.ts (5 tests) 5ms โœ“ tests/lib/connections/webhook-lag.test.ts (4 tests) 7ms โœ“ tests/middleware/require-organization-context.test.ts (2 tests) 39ms  Test Files  110 passed | 2 skipped (112)  Tests  1288 passed | 1 expected fail | 14 skipped | 2 todo (1305)  Start at  07:11:09  Duration  84.95s (transform 10.61s, setup 0ms, import 81.33s, tests 14.03s, environment 21ms) == api: test: ok in 86s == api: build > @tsmart/commerce-connect@1.0.0 build > tsc == api: build: ok in 34s == dashboard: npm ci npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. npm warn deprecated @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me npm warn deprecated @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead npm warn deprecated eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options. added 348 packages in 14s == dashboard: npm ci: ok in 15s == dashboard: typecheck == dashboard: typecheck: ok in 23s == dashboard: lint > tsmartconnect-dashboard@0.1.0 lint > next lint `next lint` is deprecated and will be removed in Next.js 16. For new projects, use create-next-app to choose your preferred linter. For existing projects, migrate to the ESLint CLI: npx @next/codemod@canary next-lint-to-eslint-cli . โœ” No ESLint warnings or errors == dashboard: lint: ok in 10s == TEMP diagnostic: google fonts css /* cyrillic-ext */ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/l/font?kit=UcCB3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfU5NJeWUdIOjBeTGiXZdAD2MVsiEu&skey=c491285d6722e4fa&v=v20) format('woff2'); unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/l/font?kit=UcCB3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfU5NJeWUdIOjBeTGiXZdJD2MVsiEu&skey=c491285d6722e4fa&v=v20) format('woff2'); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/l/font?kit=UcCB3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfU5NJeWUdIOjBeTGiXZdBD2MVsiEu&skey=c491285d6722e4fa&v=v20) format('woff2'); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/l/font?kit=UcCB3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfU5NJeWUdIOjBeTGiXZdOD2MVsiEu&skey=c491285d6722e4fa&v=v20) format('woff2'); unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; } /* vietnamese */ @font-face { font-family: 'Inter'; fontcurl: (23) Failure writing output to destination, passed 1376 returned 0 173.194.221.95 STREAM fonts.googleapis.com 173.194.221.95 DGRAM fonts.googleapis.com 2a00:1450:4010:c01::5f STREAM fonts.googleapis.com == TEMP diagnostic: google fonts css: ok in 0s == dashboard: build > tsmartconnect-dashboard@0.1.0 build > next build โ–ฒ Next.js 15.5.23 Creating an optimized production build ... โœ“ Compiled successfully in 53s Linting and checking validity of types ... Collecting page data ... Generating static pages (0/57) ... Generating static pages (14/57) Generating static pages (28/57) Generating static pages (42/57) โœ“ Generating static pages (57/57) Finalizing page optimization ... Collecting build traces ... Route (app) Size First Load JS โ”Œ โ—‹ / 143 B 103 kB โ”œ โ—‹ /_not-found 143 B 103 kB โ”œ ฦ’ /api/basefyio/[...path] 143 B 103 kB โ”œ ฦ’ /api/tsc/[...path] 143 B 103 kB โ”œ โ—‹ /dashboard 10.8 kB 126 kB โ”œ โ—‹ /dashboard/alerts 3.71 kB 116 kB โ”œ โ—‹ /dashboard/alerts/amazon 2.34 kB 108 kB โ”œ โ—‹ /dashboard/alerts/quickbooks 5.6 kB 119 kB โ”œ โ—‹ /dashboard/alerts/shopify 2.32 kB 108 kB โ”œ โ—‹ /dashboard/analytics 6.55 kB 123 kB โ”œ โ—‹ /dashboard/analytics/amazon 3.49 kB 120 kB โ”œ โ—‹ /dashboard/analytics/quickbooks 6.49 kB 120 kB โ”œ โ—‹ /dashboard/analytics/shopify 3.51 kB 120 kB โ”œ โ—‹ /dashboard/billing 5.35 kB 121 kB โ”œ โ—‹ /dashboard/channels 5.26 kB 118 kB โ”œ โ—‹ /dashboard/collections 1.18 kB 122 kB โ”œ โ—‹ /dashboard/connectors 4.66 kB 120 kB โ”œ โ—‹ /dashboard/connectors/amazon 3.5 kB 120 kB โ”œ โ—‹ /dashboard/connectors/quickbooks 16 kB 132 kB โ”œ โ—‹ /dashboard/connectors/shopify 6.67 kB 120 kB โ”œ ฦ’ /dashboard/control-tower 143 B 103 kB โ”œ โ—‹ /dashboard/customers 6.27 kB 124 kB โ”œ ฦ’ /dashboard/customers/[id] 4.32 kB 120 kB โ”œ โ—‹ /dashboard/dev/primitives 2.22 kB 115 kB โ”œ โ—‹ /dashboard/developer 10.1 kB 120 kB โ”œ โ—‹ /dashboard/developer/amazon 3.43 kB 117 kB โ”œ โ—‹ /dashboard/developer/quickbooks 3.62 kB 117 kB โ”œ โ—‹ /dashboard/developer/shopify 3.53 kB 117 kB โ”œ โ—‹ /dashboard/discounts 1.47 kB 123 kB โ”œ โ—‹ /dashboard/events 2.92 kB 112 kB โ”œ โ—‹ /dashboard/events/amazon 5.9 kB 119 kB โ”œ โ—‹ /dashboard/events/quickbooks 3.59 kB 120 kB โ”œ โ—‹ /dashboard/events/shopify 5.89 kB 119 kB โ”œ โ—‹ /dashboard/growth 5.9 kB 122 kB โ”œ โ—‹ /dashboard/inventory 4.21 kB 125 kB โ”œ โ—‹ /dashboard/margin 5.33 kB 118 kB โ”œ โ—‹ /dashboard/orders 6.29 kB 124 kB โ”œ ฦ’ /dashboard/orders/[id] 4.48 kB 120 kB โ”œ โ—‹ /dashboard/organizations 2.69 kB 118 kB โ”œ โ—‹ /dashboard/payouts 4.45 kB 120 kB โ”œ โ—‹ /dashboard/products 11.3 kB 129 kB โ”œ ฦ’ /dashboard/products/[id] 5.25 kB 121 kB โ”œ โ—‹ /dashboard/segments 669 B 122 kB โ”œ โ—‹ /dashboard/settings 4.67 kB 118 kB โ”œ โ—‹ /dashboard/sync 5.62 kB 125 kB โ”œ โ—‹ /dashboard/sync/amazon 2.37 kB 121 kB โ”œ โ—‹ /dashboard/sync/dead-letters 3.03 kB 122 kB โ”œ โ—‹ /dashboard/sync/quickbooks 3.05 kB 122 kB โ”œ โ—‹ /dashboard/sync/shopify 2.44 kB 121 kB โ”œ โ—‹ /dashboard/tables 6.43 kB 109 kB โ”œ โ—‹ /dashboard/tables/amazon 1.18 kB 119 kB โ”œ โ—‹ /dashboard/tables/quickbooks 1.31 kB 119 kB โ”œ โ—‹ /dashboard/tables/shopify 1.12 kB 119 kB โ”œ โ—‹ /dashboard/tax 3.33 kB 116 kB โ”œ โ—‹ /dashboard/webhooks 4.58 kB 120 kB โ”œ โ—‹ /dashboard/workspace 5.03 kB 123 kB โ”œ โ—‹ /docs 8.74 kB 115 kB โ”œ โ—‹ /icon.ico 0 B 0 B โ”œ ฦ’ /invite/[token] 1.82 kB 113 kB โ”œ โ—‹ /sign-in 3.49 kB 109 kB โ”” โ—‹ /sign-up 4.51 kB 110 kB + First Load JS shared by all 103 kB โ”œ chunks/1255-a20f6c5e797a687b.js 46.4 kB โ”œ chunks/4bd1b696-f785427dddbba9fb.js 54.2 kB โ”” other shared chunks (total) 1.93 kB โ—‹ (Static) prerendered as static content ฦ’ (Dynamic) server-rendered on demand == dashboard: build: ok in 140s == no secrets under NEXT_PUBLIC_ (S1-11) OK: no credential-shaped NEXT_PUBLIC_ variables found. == no secrets under NEXT_PUBLIC_ (S1-11): ok in 0s == secret scan (gitleaks) 7:16AM INF 1 commits scanned. 7:16AM INF scan completed in 9.69ms 7:16AM INF no leaks found == secret scan (gitleaks): ok in 0s == result: success