- Add Server.Shutdown() method delegating to X402Server
- Wire main-server graceful shutdown with 15s bounded timeout
- Derive long-lived clients (bluesky, ollama) from backgroundCtx instead of
signal context to prevent poisoned sessions on SIGTERM during startup
- Remove empty errgroup goroutine that only logged on cancellation
- Bind readyz sidecar to configurable READYZ_ADDRESS (default 127.0.0.1)
instead of 0.0.0.0
- Validate port strings (ReadyzPort, MetricsPort, ListenPort) at config load
- Fix ready-check Ollama probe: use defer for body drain, guard nil resp
- config: MATRIX_WEBHOOK_URL / MATRIX_SERVER_ID via os.Getenv (optional),
keep hostname fallback for MatrixServerID, avoid Fatal when webhook
not configured (test-endpoints omits it, webhook.go nil-safe)
- auth: NewCDPAuth converts literal \n to real newlines before
pem.Decode, so Forgejo single-line secret
-----BEGIN EC PRIVATE KEY-----\n... works while malformed
still returns error and CI fails via log.Fatal (as required)
Analytics review fixes:
- Store Twitter analytics coverage as discrete intervals instead of a single
contiguous window so disjoint fetch ranges no longer mask inner gaps as
covered (fetch per uncovered range, add gap regression tests)
- Compute real AvgEngagement/Correlation in getEngagementDrivers instead of
all-zero placeholders
- Make parseAnalyticsTimeRange surface invalid timestamps, cap merged analytics
posts at limit, and normalize sentiment by timestamped posts only
- gofmt: fix misalignment in config.go and types.go
- Include Twitter in the default aggregate analytics scope; its API costs are
passed to the caller through paid tool pricing
x402 cost passing:
- computeTwitterStyleProfileCost now charges base cost ($0.10) + user lookup
($0.01) + $0.005 per Twitter post, guaranteeing the charge always clears the
underlying API cost (never break-even or loss)
- Return a $-prefixed dollar string for the settlement override, fixing the
previously off-by-factor-100 raw-atomic-unit amount
- Raise getStyleProfile base registration to $0.36 to authorize the max Twitter
charge
- Add tests for the cost formula, profitable floor, and dollar formatting
- config/models.env: EMBEDDING_MODEL and STYLE_MODEL defaults
- config.go: loads models.env by finding project root (go.mod), removes hardcoded defaults
- test-endpoints.yaml: sources config/models.env to set env vars for CI
- Works from any working directory (server, tests, CI)
- Instruction-tuned variant for reliable prompt following
- Q4_K_M quantization for CPU latency optimization
- Updated test workflow to pull correct model
- config.go: set EMBEDDING_MODEL=qwen3-embedding:0.6b, STYLE_MODEL=qwen3:4b as defaults
- ollama/client.go: remove hardcoded fallback logic, use passed values directly
- Single source of truth for model defaults in config package
- Add MATRIX_WEBHOOK_URL and MATRIX_SERVER_ID config fields (optional, defaults to hostname)
- Create internal/server/webhook.go: WebhookSender with exponential backoff retry (5 attempts),
first/last attempt logging, non-blocking goroutine integration
- Wire webhook into OnAfterSettle callback in server.go (goroutine, fire-and-forget)
- Migrate all persistent interface{} to any (Go 1.25.4 convention):
db.go: ...any in DBTX interface
models.go: Embedding any
FindSimilarUsers.sql.go: QueryEmbedding any, Embedding any
UpsertStyleProfiles.sql.go: Embedding any
- Migrate server.go map[string]interface{} to map[string]any in webhook payload
- Add TestStoreSaveTransaction integration test (skipped without TEST_DATABASE_URL)
- Add TestSettleHook unit test for param mapping
- Add dependency-free in-process schema migrator (internal/store/migrate.go) that applies
sql/schema/*.sql in order, tracking versions in schema_migrations. Gated by MIGRATE_ON_START
and MIGRATIONS_DIR (default sql/schema).
- Fix migration ordering: 002 now creates the 'vector' extension before defining the
user_style_profiles table (previously the extension was only created later in 003).
- Bound concurrent background enrichment with a 4-slot semaphore so bursts of live
getStyleProfile calls cannot overwhelm Ollama.
- buildStyleProfile degrades gracefully when Ollama is down: stores a zero embedding and a
keyword-based fallback summary instead of failing the whole request.
- Add a /readyz side server (READYZ_PORT, default 9090) that checks DB connectivity and
Ollama availability.
Co-Authored-By: Claude (AI assistant) <noreply@anthropic.com>
- refactor requestStyleProfile into a shared requestStyleProfileForced core (resolve -> cache
check -> build) so a specific platform can be targeted regardless of identifier format
- on a live getStyleProfile request, fire-and-forget enrichCrossPlatform: bounded fan-out
(<=3 goroutines) tries the same identifier on every other platform, logs/skips mismatches,
and still obeys the 1-week update gate; uses a detached context (context.WithoutCancel +
5m timeout) so it outlives the request
- trigger only on live tool calls (not the prewarmer) and gated by ENRICH_CROSS_PLATFORM
(default true)
- add platformFromString helper
- Nostr client (fiatjaf.com/nostr) fetching kind:1 notes from relays
- Twitter client using raw Twitter API v2 (no external X library), optional (no token = lazy error)
- getStyleProfile dispatches by platform; a profile is updated only when a request is
made and the cached profile is older than a week (served from cache otherwise)
- shared requestStyleProfile path used by both the tool and the prewarmer
- StartPrewarm discovers interesting authors (from a feed URI or seed list) and
pre-fetches at most one profile per minute; fresh profiles are never re-embedded