Each visibility run queries every keyword against every provider (e.g., 50 keywords × 3 providers = 150 API calls). There’s no caching, so scheduled runs re-query everything even if results haven’t changed. Default models are expensive (claude-sonnet-4-6, gpt-4o). Combined, this makes monitoring costly for users with many keywords.
packages/provider-claude/src/normalize.ts:12 — change DEFAULT_MODEL from 'claude-sonnet-4-6' to 'claude-haiku-4-5'packages/provider-openai/src/normalize.ts:11 — change DEFAULT_MODEL from 'gpt-4o' to 'gpt-4o-mini'packages/provider-claude/src/normalize.ts:60 — reduce max_tokens from 4096 to 1024 (citation detection doesn’t need long answers)packages/provider-claude/src/normalize.ts:65 — reduce max_uses from 5 to 3 (3 web searches is sufficient for a single keyword)Skip API calls when a recent snapshot exists for the same keyword+provider.
3a. Add cacheTtlHours to config
packages/canonry/src/config.ts — add cacheTtlHours?: number to CanonryConfig (default: 6)3b. Add forceRefresh to API
packages/api-routes/src/runs.ts:16 — add forceRefresh?: boolean to Body typepackages/api-routes/src/runs.ts:9 — add forceRefresh to onRunCreated callback signatureforceRefresh through onRunCreated calls in packages/canonry/src/server.ts3c. Add --force flag to CLI
packages/canonry/src/commands/run.ts — pass forceRefresh: true when --force is set3d. Cache logic in JobRunner (packages/canonry/src/job-runner.ts)
opts?: { forceRefresh?: boolean; cacheTtlHours?: number } to executeRunquerySnapshots for same (keywordId, provider) where createdAt >= cutoffrunId, skip API call3e. Add composite DB index
packages/db/src/schema.ts — add index('idx_snapshots_kw_provider_created').on(table.keywordId, table.provider, table.createdAt) to querySnapshots3f. UI force-refresh option
apps/web/Keywords are currently processed sequentially. Process N keywords concurrently.
packages/canonry/src/job-runner.ts — replace sequential for loop with concurrency-limited poolkeywordConcurrency?: number to CanonryConfig (default: 3)withConcurrencyLimit helper (no external deps)packages/provider-openai/src/normalize.ts:91 — change buildPrompt to just return keyword (matches Gemini approach). tool_choice: 'required' already forces web search.package.json and packages/canonry/package.json (minor version — new features: cache TTL, force refresh, concurrency config)pnpm run typecheck — ensure no type errorspnpm run test — existing tests passpnpm run lint — no lint issues--force, verify fresh API calls.packages/canonry/src/job-runner.ts — core orchestration (cache, concurrency)packages/canonry/src/config.ts — config typespackages/canonry/src/server.ts — threading forceRefreshpackages/provider-claude/src/normalize.ts — model + param changespackages/provider-openai/src/normalize.ts — model + prompt changespackages/api-routes/src/runs.ts — forceRefresh API parampackages/db/src/schema.ts — cache index