Skip to content

staging - #12

Draft
dasturchioka wants to merge 74 commits into
mainfrom
staging
Draft

staging#12
dasturchioka wants to merge 74 commits into
mainfrom
staging

Conversation

@dasturchioka

@dasturchioka dasturchioka commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

.

dasturchioka and others added 6 commits April 14, 2026 21:45
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>
dasturchioka and others added 23 commits April 15, 2026 12:48
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.
@dasturchioka dasturchioka changed the title fix: normalize polar_whs_ prefix to whsec_ for Svix verification staging May 6, 2026
bclayn24 and others added 28 commits May 6, 2026 15:37
- 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants