484 Commits

Author SHA1 Message Date
Ben Senescu
cfc8456767
feat(mcp): migrate to MCP SDK v2 stateless handler (#464) 2026-08-08 12:44:48 -04:00
Ben Senescu
45403aa06f
fix(ga4): hide every GA4 connect surface until the OAuth app is approved (#466) 2026-08-07 22:56:31 -04:00
Ben Senescu
eec998a762
feat(rank-tracking): raise scheduler throughput 5x with rate-limit-derived sizing (#465)
* feat(rank-tracking): raise scheduler budget to 2000 units/tick

The 200-unit budget used ~2-6% of DataForSEO's 2,000 req/min account
cap and would take days to drain the post-#462 backlog. Scheduled checks
run through the task queue (1 task_post per 100 units + free task_get
polls), so a full 2,000-unit tick peaks around 1,200 req/min — still
leaving headroom for the other DataForSEO products on the account.

Also raise the due-config fetch limit to 500 so skip-heavy stretches
(free orgs, keywordless configs) drain more than 200 rows per tick.

* fix(rank-tracking): retune budget to 1000 with accurate sizing and a tick deadline

Review corrections to the 10x bump: task_get polling is one call per unit
per round and rounds wake synchronized per tick, with up to three ~15-min
poll windows overlapping the */5 cron — 2000 units/tick could saturate
DataForSEO's 2000 req/min cap, silently aging throttled polls into the
~3x-cost live fallback billed to customers. 1000/tick keeps real headroom
and is still ~45x steady-state demand.

Add a 3-minute wall-clock deadline to the per-config loop (stoppedByDeadline
in the tick summary): a skip-heavy 500-candidate tick pays serial Autumn
round-trips per distinct org and could otherwise run into the 15-minute cron
kill. Name the fetch limit (DUE_CONFIGS_PER_TICK) and correct its comment.

Test fixtures now derive from MAX_KEYWORDS_PER_CONFIG instead of asserting
an unreachable 1500-keyword config.

* fix(cron): run the audit watchdog before the rank loop

reconcileStaleAudits ran after runScheduledRankChecks in the same
invocation, so a slow rank tick would delay the watchdog and a wall-clock
kill would skip it entirely.

* fix(cron): preserve watchdog failure signal; codex review polish

Rethrow a caught reconcileStaleAudits error after the rank loop so the
invocation still reports failed (matching pre-reorder semantics), use an
inclusive deadline comparison, and note overlapping-tick poll residue in
the sizing comment.
2026-08-07 16:44:16 -04:00
Ben Senescu
16b6d38ea8
fix(dashboard): hide GA4 connect card until OAuth app is approved (#463)
* fix(dashboard): hide GA4 connect card until OAuth app is approved

* fix(ai-mcp): hide Google Analytics tool category until OAuth app is approved
2026-08-07 14:21:01 -04:00
Ben Senescu
f048dc3bd1
fix(rank-tracking): drain due configs deterministically (clean-room alternative to #456) (#462) 2026-08-07 13:53:30 -04:00
Ben Senescu
e2c84803f2
feat: add GA4 MCP insights and rank tracking management (#461) 2026-08-07 13:47:18 -04:00
Ben Senescu
c40a04459c
fix(billing): retry missing Autumn balance (#450) 2026-08-06 21:33:25 -04:00
Ben Senescu
211907ae32
fix(audit): make Lighthouse billing retry-safe (#460) 2026-08-06 21:19:39 -04:00
Ben Senescu
189d5bfa1f
fix(onboarding): isolate cached routing state per request (#459) 2026-08-06 12:24:31 -04:00
Ben Senescu
8a728563d4
fix(mcp): stop breaking public OAuth clients at token refresh (#420)
* fix(mcp): stop breaking public OAuth clients at token refresh

The DCR shim force-upgraded every public client (token_endpoint_auth_method
"none" or omitted) to client_secret_post so Perplexity would accept the
registration response. That made the stored client confidential, so the
token endpoint demanded client authentication on every grant — and MCP
clients that discard the secret (Codex) lost their session at first token
expiry with "invalid_client: missing client_secret".

Register those clients as true public clients instead (PKCE + the
provider's grant-to-client binding secure that flow), and satisfy
Perplexity by decorating only the registration response with a placeholder
client_secret and client_secret_post. The provider skips secret validation
for public clients, so clients that send the placeholder and clients that
never store it both keep working, including on refresh.

* refactor(mcp): only rebuild DCR requests that actually change

* fix(mcp): satisfy type-aware lint in DCR shims

oxlint --type-aware rejected the Record<string, unknown> assertions used to
read untrusted DCR payloads. Parse both with loose Zod schemas instead, per
the repo's trust-boundary convention, which also replaces the hand-rolled
object guards.
2026-08-05 16:10:28 -04:00
Ben Senescu
30948a2a1d
Increase pricing calculator website limit to 500 (#454) 2026-08-01 17:28:04 -04:00
Ben Senescu
187d3fd9c1
fix(db): widen backlink snapshot counts (#448) 2026-08-01 17:10:43 -04:00
Ben Senescu
f6ddf654df
fix(ci): make crawl backpressure lintable (#452) 2026-08-01 14:50:49 -04:00
Ben Senescu
b8253fb083
Free backlink checker at /backlink-checker (#444) 2026-08-01 13:50:50 -04:00
Ben Senescu
145324138e
Cap site-audit crawl memory: byte-budgeted window, 1 MiB read cap, persist backpressure (#445) 2026-08-01 13:22:26 -04:00
Ben Senescu
acd28749c8
Auto-evict the oldest search tab at capacity, raise the limit to 20 (#442) 2026-08-01 10:34:31 -04:00
Ben Senescu
32b1b1d38c
Remove Product Hunt launch banner from marketing site (#440) 2026-07-31 13:07:44 -04:00
Ben Senescu
8d6e99385f
audit: drop the audit_links table (#416) 2026-07-30 20:51:32 -04:00
Ben Senescu
1e8a924c4c
Site Audit - Improve reliability and performance with Durable Objects 2026-07-30 00:51:50 -04:00
Ben Senescu
9d19e43990
release: v0.1.3 (#433) 2026-07-30 00:23:36 -04:00
Amine Benboubker
a710b560d5
perf(docker): reuse the build across restarts instead of rebuilding on every start (#123) 2026-07-30 00:16:18 -04:00
Timur Isachenko
4a82bf0803
Document reproducible contributor checks (#105) 2026-07-29 23:32:56 -04:00
Nordalux
4f45a04961
fix(docker): forward OPENROUTER_API_KEY into the self-host container (#152) 2026-07-29 23:32:37 -04:00
Ben Senescu
ffb5c9d9e8
feat(deploy): alchemy-based Cloudflare self-hosting (#431) 2026-07-29 23:22:33 -04:00
Ben Senescu
625b76ebee
Make self hosting better (#428) 2026-07-29 23:10:01 -04:00
Ben Senescu
b0e850db2b
Backlinks tab: default sort by most recent (firstSeen) instead of rank (#432) 2026-07-29 22:41:31 -04:00
Ben Senescu
68fdcd9bf9
feat(skills): add seo-audit agent skill (#430)
* feat(skills): add beginner-seo-audit agent skill

A portable agent skill that audits a domain with OpenSEO and delivers a
one-page, plain-language HTML report for a non-SEO owner, centered on a
single do-this-week action. Ships with a frozen report template and a
docs page under Agent Skills.

* feat(skills): add starting-focus keyword step; cross-link from coach and project setup

- beginner-seo-audit now proposes a 'Where to focus first' area on healthy
  sites: one research_keywords call, one theme, 3-5 winnable keywords with
  what to make for each. Skipped entirely when the site is down.
- seo-coach and seo-project-setup now recommend beginner-seo-audit as the
  first workflow for a beginner with an existing site.
- Docs pages updated to match.

* refactor(skills): rename beginner-seo-audit to seo-audit

Audience-neutral name; the plain-language, one-action framing moves to
the description. Docs slug, meta.json, and coach/project-setup
cross-references updated to match.
2026-07-29 20:47:14 -04:00
Ben Senescu
86cbba3cdb
Project switcher: fix viewport overflow + searchable combobox (#427) 2026-07-29 16:19:16 -04:00
Ben Senescu
c81fd7a130
Rank positions: report organic rank (rank_group), not absolute SERP slot (#429)
A user's #1 organic result showed as position 3-4 because rank_absolute
counts SERP features (local pack, PAA, AI overviews) as positions. Rank
tracker, keyword-research SERP list, and onboarding market snapshot now
prefer rank_group; MCP get-serp-results keeps rank_absolute since it
returns all SERP item types.
2026-07-29 10:46:15 -04:00
Ben Senescu
72c1cf0b8e
Fix modals taller than the viewport being unscrollable (#425) 2026-07-26 08:30:20 -07:00
Ben Senescu
be6a34e766
Remove Search Everywhere interview post (#424) 2026-07-23 15:52:13 -07:00
Ben Senescu
b97d381493
Include /library pages in the sitemap (derived from route files) (#423) 2026-07-23 15:20:21 -07:00
Ben Senescu
f56972639f
content-review skill + revert unfinished redesign + some copy changes (#422) 2026-07-23 14:39:56 -07:00
Jeremy Rivera
a4ede67ebb
Keyword Research Strategy Library + feature-page guides section + Search Everywhere interview (#128) 2026-07-23 11:37:58 -07:00
Ben Senescu
633876c0ef
release: v0.1.2 (#421) 2026-07-23 11:11:53 -04:00
Ben Senescu
a16fbd17a8 fix(ui): proper loading states for rank tracking list, detail, and dashboard cards (#419) 2026-07-23 10:31:53 -04:00
Ben Senescu
67ed8d3c77 feat(web): add Product Hunt #1 Product of the Day laurel to the hero (#415) 2026-07-23 10:31:53 -04:00
Amine Benboubker
b0b5528a3a
feat(mcp): add create_project tool (#114) 2026-07-23 10:07:02 -04:00
Slim Bouras
2c153f9a20
feat(mcp): accept locationCode/languageCode on Labs research tools (#113)
Co-authored-by: isatimur <isatimur@users.noreply.github.com>
2026-07-23 10:05:43 -04:00
Xiaonan Cui
d3c62aa5a2
fix(search-tabs): keep tabs without explicit location across reloads (#108) 2026-07-23 10:00:26 -04:00
Shuvam Kumar
3edbd0c11e
fix(gsc): clamp month-based date ranges instead of overflowing short months (#133) 2026-07-23 09:57:25 -04:00
Shuvam Kumar
d7cfbec796
fix(backlinks): keep truncateMiddle within maxLength (#134) 2026-07-23 09:56:09 -04:00
Shuvam Kumar
8ca2e0b332
fix(domain): base pagination hasMore on fetched rows, not filtered rows (#135) 2026-07-23 09:51:31 -04:00
Xiaonan Cui
0d654c06e6
test(e2e): update landing redirect assertions for dashboard landing page (#109) 2026-07-23 09:50:09 -04:00
Jeremy Rivera
95e95d48cb
Add blog post: The Dark Query Problem (#110) 2026-07-21 16:40:07 -04:00
Ben Senescu
b41092788a
release: v0.1.1 (#413) 2026-07-20 23:23:15 -04:00
Ben Senescu
3a94f02a55
fix(self-host): unbreak the Deploy to Cloudflare button and its build (#412) 2026-07-20 23:03:09 -04:00
Ben Senescu
7990c4db7f
fix(billing): confirm credit depletion before refusing chat turns (#407) 2026-07-20 18:36:02 -04:00
Ben Senescu
278b19836d
feat(db): retry transient connection errors in the Postgres client (#408) 2026-07-20 18:16:17 -04:00
Ben Senescu
6f2eb05211
release: v0.1.0 (#404) 2026-07-19 02:19:28 -04:00