chore: feat(seo): fold Google Analytics into the SEO report (lib/ga.sh) #331
No reviewers
Labels
No labels
Agent
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
2 participants
Notifications
Total time spent: 53 seconds
Due date
igor
53 seconds
No due date set.
Dependencies
No dependencies set
Reference
joshtronic/igor!331
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/329-feat-fold-google-analytics-into-the-seo-report"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this PR does
lib/ga.sh:ga_access_token,ga_property_for_domain(dynamic accountSummaries lookup, no static map),ga_run_report(GA4 Data API runReport, matchesgsc_query's empty-rows error posture)lib/seo-analysis.sh: newseo_ga_metricsparser;seo_build_reportthreads an optionalgafield (defaults tonull); bothseo_render_markdownandseo_render_htmladd an "On-site behavior (GA)" section only when.ga != nullbin/tick.sh: sourceslib/ga.sh;do_seo_tickresolves the GA property for the analyzed domain (after the zero-opportunity early-return, so a no-GA-property domain never makes a wasted GA API call) and merges GA metrics into the report before renderingagent-settings.json,systemd/,.forgejo/workflows/untouchedTest plan
bash bin/test-ga.sh-- new suite:ga_property_for_domain(match/no-match/non-JSON/curl-failure),ga_run_report(request shape incl. empty-dims, happy parse, failure),seo_ga_metrics(full set, keyEvents-absent, empty-rows),seo_build_reportga passthrough, and both renderers' GA section (present with a property, absent/unchanged without one)bash bin/check-sync.shpasses (fullmake testsuite, allbin/test-*.shgreen including the new one)Closes #329
🤖 Review —
COMMENT(automated)CI for
7405e0c0: successAdds a GA4 client (
lib/ga.sh), folds optional on-site-behavior metrics into the SEO report (lib/seo-analysis.sh), wires it intodo_seo_tick, and ships a fixture-driven test suite. The change is well-scoped (~370 lines), the checklist matches the diff, CI is green, and the "additive/optional, degrade to GSC-only" design is clean. One integration point I can't verify from the diff keeps me from a full APPROVE.The one thing to check before merging
lib/ga.sh:ga_access_tokendelegates togoogle_sa_access_token "$GA_SCOPE", but that function is defined inlib/google-auth.sh, which is not in this diff. The test suite stubsga_access_tokenoutright (bin/test-ga.sh:31), so nothing here exercises the real binding. If the shared minter is actually named something else, the feature silently no-ops in production (rc 1 →ga_property=""→ GA section never renders) and every test still passes. Please confirmgoogle_sa_access_tokenexists and takes a scope arg the waylib/gsc.shuses it. This is safe-by-design (no breakage, just dead feature), which is why it's a comment and not a block.Correctness notes (looked fine)
ga_property_for_domainreturns empty+rc0 on no-match and empty+rc1 on fetch/parse failure;tick.sh:1704handles both via|| ga_property=""then the-nguard. Good.ga_run_reportandseo_ga_metricsboth fall back to well-formed JSON ({"rows":[]}/null), sojq --argjson gaattick.sh:1710can't blow up on a bad fetch.seo_ga_metricstranspose correctly dropskeyEventswhen the metric header is absent (covered by theNO_KEY_EVENTStest), and both renderers guard.ga.keyEvents != null.Coverage gap worth a follow-up
ga_access_token → google_sa_access_tokenseam (see above) nordo_seo_tick's end-to-end merge. Given the graceful-degradation design this is acceptable, but the real auth path is entirely untested.No security concerns:
domainis passed to jq via--arg, the property id interpolated into the runReport URL is the numeric API-suppliedpropertyfield (not user/display input), and the bearer token isn't logged.Independent review by the harness on
claude-opus-4-8(effort: high). The human reviewer is requested once Igor has reviewed; a human still merges.