feat: add GSC top-query/top-page breakdown to the CEO board digest #401

Merged
joshtronic merged 1 commit from agent/399-ceo-gsc-read-add-top-query-top-page-breakdown-so into master 2026-07-20 21:03:33 +00:00
Collaborator

What this PR does

  • feat: add GSC top-query/top-page breakdown to the CEO board digest
  • Extend ceo_read_gsc to pull query- and page-dimension GSC data for the current 28-day window and render top-10-by-impressions tables (impressions/clicks/CTR/position) for each
  • Add a one-line concentration signal ("top-5 queries = N% of impressions across M distinct queries") so the board can tell head-term concentration from a long tail
  • Add a sentence pointing the board at the breakdown to self-diagnose the funnel instead of filing a question for it
  • Extend bin/test-ceo.sh's ceo_read_gsc block: mock gsc_query dispatches on the dimension arg, assert the top-queries/top-pages tables and concentration line render, and that an empty/failed breakdown fetch degrades to per-table "no data" notes without touching the aggregate scoreboard or crashing

Test plan

  • bin/test-ceo.sh passes (24 new/updated assertions in the ceo_read_gsc block)
  • make test passes (check-sync + every bin/test-*.sh)
  • shellcheck clean on lib/ceo.sh and bin/test-ceo.sh (verified via make lint's full run: zero findings on either changed file; the two pre-existing warnings make lint reports are on unrelated files -- bin/test-automerge.sh and bin/tick.sh -- and are present on master before this change)
  • No manual verification needed beyond the above; this is a read-only, scripted data addition to an existing on-demand GSC read with no new OAuth scope or config knob

Closes #399

## What this PR does - [x] feat: add GSC top-query/top-page breakdown to the CEO board digest - [x] Extend `ceo_read_gsc` to pull `query`- and `page`-dimension GSC data for the current 28-day window and render top-10-by-impressions tables (impressions/clicks/CTR/position) for each - [x] Add a one-line concentration signal ("top-5 queries = N% of impressions across M distinct queries") so the board can tell head-term concentration from a long tail - [x] Add a sentence pointing the board at the breakdown to self-diagnose the funnel instead of filing a question for it - [x] Extend `bin/test-ceo.sh`'s `ceo_read_gsc` block: mock `gsc_query` dispatches on the dimension arg, assert the top-queries/top-pages tables and concentration line render, and that an empty/failed breakdown fetch degrades to per-table "no data" notes without touching the aggregate scoreboard or crashing ## Test plan - [x] `bin/test-ceo.sh` passes (24 new/updated assertions in the `ceo_read_gsc` block) - [x] `make test` passes (check-sync + every `bin/test-*.sh`) - [x] `shellcheck` clean on `lib/ceo.sh` and `bin/test-ceo.sh` (verified via `make lint`'s full run: zero findings on either changed file; the two pre-existing warnings `make lint` reports are on unrelated files -- `bin/test-automerge.sh` and `bin/tick.sh` -- and are present on `master` before this change) - [x] No manual verification needed beyond the above; this is a read-only, scripted data addition to an existing on-demand GSC read with no new OAuth scope or config knob Closes #399
feat: add GSC top-query/top-page breakdown to the CEO board digest
All checks were successful
Lint / check-sync (pull_request) Successful in 4s
Lint / check-sync (push) Successful in 4s
f49c44c1f3
Author
Collaborator

🤖 Review — APPROVE (automated)

CI for f49c44c1: success

Adds a query/page breakdown (top-10 by impressions + a top-5 concentration line) to ceo_read_gsc, with matching test coverage. Focused, in scope (~90 lines), CI green.

Correctness spot-checks (all consistent with the asserted test output):

  • _ceo_gsc_render_top: sorts by -.impressions, top-N, renders ctr*10000|round/100 and position*100|round/100. For install widget (ctr 0.1 → 10%, pos 3) the output matches the | install widget | 400 | 40 | 10% | 3 | assertion. Empty .rows_no <label> data this cycle_, matching the empty-breakdown assertions.
  • _ceo_gsc_concentration: top-5 impressions 400+300+200+150+100 = 1150 of 1200 → 95.83%, across 6 rows — exactly the asserted line. $tot == 0 branch degrades to the "no query data this cycle" note.
  • Tab-delimited IFS=$'\t' read correctly preserves multi-word keys ("install widget").
  • Breakdown fetches (query/page dim) are independent of the aggregate date-dim fetch and each degrade to their own note, so an empty/failed breakdown leaves the scoreboard intact — verified by the "empty breakdown -> scoreboard still numbers" assertion. Token-mint failure returns before the breakdown, so Top queries is absent (asserted).

Robustness note (non-blocking): sort_by(-.impressions) / .ctr * 10000 will throw on a row with null impressions/ctr; the 2>/dev/null || true swallows it into an empty-rows "no data" note rather than a crash or partial render. Acceptable — real GSC responses include these fields — but if malformed rows ever appear you'd silently show "no data" instead of the surviving rows. Not worth blocking.

Checklist honesty: every checked item maps to a real change in the diff (render helper, concentration signal, prose nudge, and the extended test-ceo.sh block including the empty-breakdown and token-fail degradation cases). Test-plan claims are consistent with success CI.

Nothing blocking. Clean, well-tested, honestly described.


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.

### 🤖 Review — `APPROVE` _(automated)_ CI for `f49c44c1`: **success** Adds a query/page breakdown (top-10 by impressions + a top-5 concentration line) to `ceo_read_gsc`, with matching test coverage. Focused, in scope (~90 lines), CI green. **Correctness spot-checks (all consistent with the asserted test output):** - `_ceo_gsc_render_top`: sorts by `-.impressions`, top-N, renders `ctr*10000|round/100` and `position*100|round/100`. For `install widget` (ctr 0.1 → `10%`, pos 3) the output matches the `| install widget | 400 | 40 | 10% | 3 |` assertion. Empty `.rows` → `_no <label> data this cycle_`, matching the empty-breakdown assertions. - `_ceo_gsc_concentration`: top-5 impressions 400+300+200+150+100 = 1150 of 1200 → 95.83%, across 6 rows — exactly the asserted line. `$tot == 0` branch degrades to the "no query data this cycle" note. - Tab-delimited `IFS=$'\t'` read correctly preserves multi-word keys ("install widget"). - Breakdown fetches (`query`/`page` dim) are independent of the aggregate `date`-dim fetch and each degrade to their own note, so an empty/failed breakdown leaves the scoreboard intact — verified by the "empty breakdown -> scoreboard still numbers" assertion. Token-mint failure returns before the breakdown, so `Top queries` is absent (asserted). **Robustness note (non-blocking):** `sort_by(-.impressions)` / `.ctr * 10000` will throw on a row with null `impressions`/`ctr`; the `2>/dev/null || true` swallows it into an empty-rows "no data" note rather than a crash or partial render. Acceptable — real GSC responses include these fields — but if malformed rows ever appear you'd silently show "no data" instead of the surviving rows. Not worth blocking. **Checklist honesty:** every checked item maps to a real change in the diff (render helper, concentration signal, prose nudge, and the extended `test-ceo.sh` block including the empty-breakdown and token-fail degradation cases). Test-plan claims are consistent with `success` CI. Nothing blocking. Clean, well-tested, honestly described. --- <sub>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.</sub> <!-- review sha=f49c44c1f3b2d3b6300ce321dd6e4ba49904cf7c verdict=APPROVE ci=success -->
igor added spent time 2026-07-20 21:03:16 +00:00
40 seconds
joshtronic approved these changes 2026-07-20 21:03:30 +00:00
joshtronic deleted branch agent/399-ceo-gsc-read-add-top-query-top-page-breakdown-so 2026-07-20 21:03:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
2 participants
Notifications
Total time spent: 40 seconds
igor
40 seconds
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joshtronic/igor!401
No description provided.