staging - #12
Draft
dasturchioka wants to merge 74 commits into
Draft
Conversation
Polar brands webhook secrets with polar_whs_ prefix but the Svix Go SDK expects whsec_. Also strip surrounding quotes from env var value. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Polar secrets (polar_whs_...) are unpadded base64. Svix StdEncoding requires padding — add trailing = based on len % 4. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ensureAppConfig skipped updates when a non-empty value already existed in app_config (ReplaceSeedPlaceholder=false). Adding AlwaysOverwriteFromEnv flag so polar_webhook_secret and revenuecat_webhook_secret always sync from the env var on every container restart, preventing stale DB values from overriding updated secrets. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove AlwaysOverwriteFromEnv (reverts ae1c162) and drop env var fallback from webhook secret helpers. Bootstrap seeds the DB row on first deploy; after that admin updates via app_config. Code reads ConfigCache only — no dual-source confusion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POLAR_WEBHOOK_SECRET and REVENUECAT_WEBHOOK_SECRET removed from env and bootstrap. Secrets are set once in app_config via admin panel and read from ConfigCache only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a Postgres-backed generic job system so voice parse, text parse,
insight generation, webhook processing, and email delivery are no longer
synchronous in the HTTP request cycle.
Key changes:
- jobs schema, sqlc queries, domain package (service, claimer, runtime, retry, metrics)
- object storage abstraction (local adapter for dev)
- AsyncJobWorker with per-kind concurrency pools and Gemini circuit breaker
- voice, text parse, insights return 202 + job_id; result via GET /api/jobs/:id
- webhooks ingested fast, processed asynchronously with dedupe by event ID
- email sends queued from billing worker instead of inline
- polar startup sync moved off boot critical path
- admin DLQ endpoints: list/inspect/replay terminal jobs
- per-user Redis sliding window admission on AI routes (middlewares/limits.go)
- fixed BodyLimit("1M") vs 10MB voice file mismatch
- async_voice_parse_enabled / async_text_parse_enabled / async_insights_enabled /
async_webhooks_enabled feature flags in app_config
- distributed admission control: per-kind queue depth caps, Gemini circuit breaker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- errcheck: wrap file.Close() in defer func with error check (voice_async) - goimports: reformat all files flagged with spacing/import issues - gosec G109: use strconv.ParseInt with int64 + MaxInt32 guard (admin_jobs) - gosec G404: suppress math/rand jitter with #nosec G404 comment (retry) - gosec G301: tighten MkdirAll permissions 0755 → 0750 (storage/local) - gosec G304: use os.OpenRoot for scoped file read, eliminating variable path (storage/local) - staticcheck S1016: replace struct literals with direct type conversions (insights) - staticcheck SA9003: remove empty if block in voice_process - unused: delete three dead handlePolarSubscription* methods from webhook.go (superseded by handlePolarSubscription*Event in webhook_async.go) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dler Admin-triggered store sync is an infrequent, admin-only operation that doesn't need async queueing. Restore direct runPolarStoreProductsSync call (which is patchable in tests) and return the summary synchronously. The startup-path async enqueue (EnqueuePolarStartupSync) is unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Run goimports on 6 files flagged by CI (jobs/errors, jobs/runtime_test, jobs/service_test, models/job, services/limits_test, workers/billing) - Delete queuePolarSyncResponse from admin_sync_async — became dead code after AdminSyncPolarStoreProductsHandler was restored to synchronous path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add LogBroadcaster (ring buffer + fan-out to SSE clients) - Wire broadcaster into logger middleware and stdlib log via MultiWriter - Add GET /api/admin/logs/stream endpoint (jwt + admin protected) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Echo's 30s WriteTimeout kills long-lived SSE connections. Use http.NewResponseController to clear the deadline per-request. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
s3.go was committed without go.mod entry, breaking Docker build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without set -e, docker compose build failure was silently ignored — up -d started old image and prune exited 0, making CI report success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Enforce encryption for debt bundles, add update/delete, relink converted transactions, and expose parse/voice bundle candidates.
- Pass user email to Polar checkout for autofill - Reject empty title/body in insight JSON parser - Clean Gemini response and retry once on parse failure - Log safe failure reasons without payload content
…liability fix: prefill polar checkout email and harden insight json generation
- start-fresh: reset private data without deleting account or billing\n- ai: fail over eligible Gemini keys and surface busy responses
Error: internal/clients/gemini.go:124:53: SA1019: netErr.Temporary has
been deprecated since Go 1.18 because it shouldn't be used: Temporary
errors are not well-defined. Most "temporary" errors are timeouts, and
the few exceptions are surprising. Do not use this method. (staticcheck)
if errors.As(err, &netErr) && (netErr.Timeout() || netErr.Temporary()) {
^
1 issues:
* staticcheck: 1
Error: internal/handlers/voice_prompt_test.go:89:5: QF1001: could apply
De Morgan's law (staticcheck)
if !(openDebtsAt < userContextAt && userContextAt < recentMerchantsAt) {
^
1 issues:
deleted parser_prompt.go removed runtime CATEGORY FAIL-SAFE append handlers now fail closed if active DB prompt is empty seed data.sql is prompt source: includes debt_bundles in output schema category guidance lives there removed stale “Pro users only” wording for recent_merchants
Balance create and sync write placeholder plaintext only. Encrypted balance fields and snapshot payloads become source of truth.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
.