Skip to content

Cosmos Intelligence Playbook

Status: Live — Phase A0 + A1 + A2 shipped 12 May 2026 Owners: Sush + AI co-founder Scope: Cosmos-wide live counter + private analytics intelligence dashboard

The first deep cross-planet data layer. Single GA4 property + custom dimension tags every hit per-planet → public pill (cosmos-bar) + gated dashboard (CC Cosmos tab) + nightly summary worker. Future Phase B layers outcome events on top of this foundation.


TL;DR — what this project is

Sush wanted to "see who's in the cosmos right now" (live counter visible on every planet) AND a deeper "intelligence cockpit" he can open with Sunday coffee and walk away with 1–3 things to do (decisions, not just numbers). The cockpit is gated to Sush + AI only; the counter is a public vitality signal.

The architecture pivots on one event-scoped GA4 custom dimension (cosmos_planet) that every cosmos hit carries. Worker queries split data by it. Single property, single measurement ID, zero per-stream juggling.

Two rubber-duck passes + one SME code review shaped the design before any code shipped. The full critique-then-implement loop saved at least one BLOCKING bug per phase.


The 9 surfaces

Slug Domain Kind Identification rule
earth www.aguidetocloud.com planet host = www, path !startsWith /guided/
guided www.aguidetocloud.com/guided/ moon host = www, path startsWith /guided/
brainbar cmd.aguidetocloud.com planet host = cmd
shift shift.aguidetocloud.com planet host = shift
plainai plainai.aguidetocloud.com planet host = plainai, path !startsWith /learn
curriculum plainai.aguidetocloud.com/learn[/...] moon host = plainai, path startsWith /learn
agentic agents.aguidetocloud.com planet host = agents
claw claw.aguidetocloud.com planet host = claw
cosmos cosmos.aguidetocloud.com hub host = cosmos

The slug-detection logic is duplicated in TWO places (client-side gtag config in each repo + server-side resolvePlanet in _cosmos-shared.js). Both MUST stay in sync when a new planet ships. Critical gotcha: Plain AI uses pathname.startsWith('/learn') — original indexOf('/learn/')===0 failed on /learn (no trailing slash) and silently misattributed every curriculum landing-page visit. SME caught this; if you add a new path-based split, do the same startsWith (not indexOf).


Architecture

┌──────────────────────────────────────────────────────────────────────────┐
│ Every planet's BaseLayout / baseof.html                                  │
│   gtag G-2HWWZGWCD0 + cosmos_planet=<slug>                               │
│   Consent Mode v2 defaults: ad_*=denied, analytics_storage=granted       │
└─────────────────────────────────┬────────────────────────────────────────┘
              GA4 property 530486519
              Event-scoped custom dim: cosmos_planet (id 14859585669)
        ┌─────────────────────────┴───────────────────────────┐
        ▼                                                     ▼
┌────────────────────────────────┐     ┌────────────────────────────────────┐
│ /api/stats?realtime=cosmos     │     │ /api/cosmos-summary  (gated)       │
│   PUBLIC: returns {totalUnique}│     │   1× per ~24h, KV-cached, SWR.     │
│   GATED (intel=1): + byPlanet  │     │   Pulled by CC dashboard.          │
│   CORS *, 45s caches.default   │     │   ~6 GA4 queries, throttled to 3.  │
└──────────────┬─────────────────┘     │   Soft KV lock dedupes regens.     │
               │                       └─────────────┬──────────────────────┘
               ▼                                     │
┌─────────────────────────────────┐                  ▼
│ <cosmos-bar> live counter pill  │     ┌────────────────────────────────────┐
│   On every planet's shared nav  │     │ CC Dashboard — new "🌌 Cosmos" tab │
│   Polls every 45s · hides <2    │     │   4 sections: Pulse · Leaderboard  │
│   Hides after 3 fails           │     │   · Signals · Stars                │
│   Lifecycle-safe: 1 interval +  │     │   Auth: sessionStorage.cc-p Bearer │
│   AbortController + cleanup     │     │   Keyboard shortcut: c             │
└─────────────────────────────────┘     └────────────────────────────────────┘

File map (what lives where)

Worker (aguidetocloud-revamp/functions/api/)

File Purpose
_cosmos-shared.js SSOT for cosmos endpoints — auth, CORS, planet taxonomy, NZT week math, throttle. _ prefix = no auto-route.
stats.js Hosts ?realtime=cosmos[&intel=1]. Imports from _cosmos-shared.js.
cosmos-summary.js Nightly summary endpoint. Stale-while-revalidate via context.waitUntil().

Dashboard (aguidetocloud-revamp/)

File Change
layouts/cc/list.html New 🌌 Cosmos tab button + panel with 4 sections + warm-up banner.
static/js/command-centre.js cosmosData state + fetchCosmos + renderCosmos and 4 sub-renders + keyboard c + switchView wiring.
static/css/command-centre.css Cosmos KPI cards, table, sparkline SVG, signal cards, badge variants.
static/staticwebapp.config.json CSP connect-src got region1.google-analytics.com (some browsers route there). Generates public/_headers via scripts/generate-cf-config.py in CF build.
layouts/partials/analytics.html Earth gtag — added cosmos_planet=earth + Consent Mode v2 defaults.
brainbar/layouts/_default/baseof.html CMD gtag (NEW — Brain Bar had no analytics before).
hugo.toml cache_version bumped (CSS+JS changed).

Per-planet repos (all 7)

Every planet has gtag G-2HWWZGWCD0 + cosmos_planet=<slug> + Consent Mode v2 defaults injected at the bottom of <head>. Astro planets use is:inline directive; Hugo planets use {{ if hugo.IsProduction }}. PlainAI uses runtime path detection via startsWith('/learn') in the gtag('config', …) call.

Repo File
cosmos-atlas src/layouts/Base.astro
shift src/layouts/BaseLayout.astro
agentic-planet src/layouts/BaseLayout.astro
claw-planet src/layouts/BaseLayout.astro
plainai build-shared.mjs (pageHead template) + public/_headers (CSP relaxed)
guided src/layouts/BaseLayout.astro (existing gtag — only the cosmos_planet param + Consent Mode were added; SLA protocol was active for this commit).
aguidetocloud-revamp/brainbar layouts/_default/baseof.html (had no analytics).

Cosmos-bar shared component (cosmos-atlas/src/cosmos-bar/)

File Change
component.ts Pill DOM + lifecycle-safe polling (single setInterval, AbortController, partial DOM updates, hide after 3 fails or count < 2).
styles.css .live-pill + .live-pill-dot keyframes + mobile + reduced-motion variants.
icons.ts (untouched in this project)

Built by npm run cosmos-bar (or npm run deploy) which runs scripts/emit-cosmos-bar.mjs → emits public/cosmos-bar.js (~40KB minified IIFE). Served from https://cosmos.aguidetocloud.com/cosmos-bar.js and consumed by every planet via <script async src="…">.

Session-state artifacts (not deployed, just for reference)

~/.copilot/session-state/1ff2ba78-…/files/: - plan.md — full design plan + phase tracker - csp-audit.md — per-planet CSP findings - measurement-dictionary.md — 3 layers of events (Layer 1 shipped; Layers 2 + 3 are Phase B roadmap) - planet-semantics.json — SSOT for per-planet action modes (mirrored inline in _cosmos-shared.js for the worker) - plain-ai-constitution-diff.md — proposed diff with rationale (Sush-approved + applied)


Auth model (READ THIS BEFORE TOUCHING ANYTHING GATED)

Setup: - The CC password gate at /cc/ stores both sessionStorage.cc-ok='1' AND sessionStorage.cc-p=<plaintext> after the user types the right password. - The public hash that the gate compares against is hardcoded in the HTML at layouts/cc/list.html line 502: var H='0579d11899d8171a96c04302aa2f2f7250adfce591e1a118f332f40c70027be8'. This is SHA-256 of the admin password. - The SAME hash is the env var ADMIN_PASSWORD_HASH on the aguidetocloud-revamp Cloudflare Pages project.

Worker auth flow: 1. Dashboard sends Authorization: Bearer <sessionStorage.cc-p> — the plaintext password. 2. Worker isAuthedAsAdmin (in _cosmos-shared.js) hashes the plaintext server-side, then constant-time compares (timingSafeStrEqual) to env.ADMIN_PASSWORD_HASH. 3. Pre-hashed values are REJECTED. This was a SME-flagged issue: the hash is visible in public HTML, so accepting it as Bearer would mean anyone scraping /cc/ source could replay the hash as auth. Plaintext-only forces possession of the actual password.

If you ever need to: - Test the gated endpoints from CLI: you need the plaintext password. ~/.copilot/secrets/guided-admin-password is a DIFFERENT password (verified — different hash). Sush has the CC plaintext. - Change the password: update H in layouts/cc/list.html AND ADMIN_PASSWORD_HASH env var on aguidetocloud-revamp Pages (production + preview). They MUST match. Test BOTH the CC unlock UI AND the cosmos endpoints after rotation. - Add a new gated endpoint: import isAuthedAsAdmin and cosmosJsonRes from ./_cosmos-shared.js. Use cosmosJsonRes({error:'Unauthorized'}, 401, 'no-cache') for rejections.

Timing-safe compare: timingSafeStrEqual is in _cosmos-shared.js. Always use it for password / token comparisons in this codebase, never JS ===. JS === short-circuits on first char mismatch and leaks position info → enables char-by-char brute force.


GA4 setup (critical reference)

  • Property: 530486519 (single shared across whole cosmos)
  • Measurement ID: G-2HWWZGWCD0 (single, deployed everywhere)
  • Custom dimension: cosmos_planet — event-scoped, registered 12 May 2026 via Admin API
  • Dimension ID: 14859585669
  • Parameter name: cosmos_planet
  • Display name: "Cosmos Planet"
  • Allowed values: earth, guided, brainbar, shift, plainai, curriculum, agentic, claw, cosmos
  • Service account: analytics-dashboard@aguidetocloud-mcp.iam.gserviceaccount.com
  • Key: ~/.copilot/secrets/ga-service-account.json + CF Pages env var GOOGLE_SERVICE_ACCOUNT_KEY (base64-encoded)
  • Has Editor role (not just Viewer as the reference doc said — I successfully created the dimension via Admin API)
  • API scopes used:
  • analytics.readonly (Data API — for queries)
  • analytics.edit (Admin API — only used once to register the dimension; can be removed if Editor stays)

Consent Mode v2 defaults (set on every gtag in the cosmos):

gtag('consent','default',{
  ad_storage:'denied',
  ad_user_data:'denied',
  ad_personalization:'denied',
  analytics_storage:'granted'
});
This is aggregate-only analytics mode. Compliant with the Plain AI constitution (post-12 May narrowing) and a defensible 5-year posture.

GA4 Data API gotchas to remember: - Realtime API does NOT support hostName or pagePath dimensions. Only event-scoped custom dimensions (via customEvent:cosmos_planet syntax) work in runRealtimeReport. This killed the original v0 plan; the cosmos_planet dimension was the fix. - Historical runReport supports any dimension/metric combination GA4 considers compatible. Event-scoped dims + event-level metrics are safe. Event-scoped dim + session-level metrics (e.g. sessions, bounceRate, engagementRate) is risky — may return 4xx or empty. - Token quota: 250k/day per property. Our load (~6 queries/day from cosmos-summary, plus realtime polls every 45s cached at edge) is well under 5% of quota. - Concurrency limits: parallel runReport calls can 429 above ~5 in flight. Always throttle to 3 via the throttled() helper in _cosmos-shared.js. - New dimensions have a 24–72h propagation lag before historical aggregations populate. Live data is immediate; weekly trends warm up. The warm_up: true flag in the summary response signals this state to the dashboard. - 🪲 activeUsers inflates when you add a dimension and sum the rows (set 13 May 2026). runRealtimeReport({ dimensions: [unifiedScreenName], metrics: [activeUsers] }) returns activeUsers per dimension value. GA4 dedupes the user inside each row, NOT across rows — so a single visitor who tabbed between two pages is counted in both rows. sum(rows[].users) ≠ unique users; it's ~40 % higher in typical browsing patterns. Always run a separate no-dimension query for "unique active across the property", the way handleRealtimeCosmos and (post-fix) handleRealtime do. Caught on 13 May 2026: cosmos pill said 22, Site Analytics tile said 32, same GA4 property, same moment. Same trap also lurks in byPlanetSum inside the gated cosmos endpoint — that field is for the per-planet stack chart only, not the headline number.


Cosmos summary worker logic (the trickiest piece)

File: functions/api/cosmos-summary.js

Caching strategy

KV namespace COSMOS_SUMMARY_KV (id db3bd5e5a2aa4c22a453364ce2a25095), bound to aguidetocloud-revamp production + preview.

Key Value TTL
summary:current Full JSON summary 30 days (effectively never; freshness driven by payload generated_at)
summary:lock Timestamp of in-flight regen 120s

Freshness windows: - < FRESH_HOURS (24h): return as-is, browser cache 30 min - < STALE_HOURS (36h): return + context.waitUntil(buildAndStore()) background regen; mark stale:true, regenerating:!lockHeld - >= STALE_HOURS or no cache: try to acquire lock + return 202 + spawn regen

Soft KV lock pattern:

const lock = await kv.get(KV_LOCK);
if (!lock) {
  await kv.put(KV_LOCK, String(Date.now()), { expirationTtl: 120 });
  context.waitUntil(buildAndStore(env));
}
KV is not atomic, but for a single-user private dashboard this is enough. If we ever go multi-user, upgrade to Durable Objects.

Force-rebuild path: GET /api/cosmos-summary?refresh=1 (with auth) — synchronous full rebuild, no cache check, no lock. Use for debugging only.

GA4 query inventory (~6 queries per build)

All wrapped in throttled(tasks, 3):

# Query Returns
1 Cosmos totals, this week (no dim) users, sessions, views, userEngagementDuration
2 Cosmos totals, last week Same (for WoW math)
3 Per-planet totals, this week (cosmos_planet) Same per planet
4 Per-planet totals, last week Same per planet
5 Per-planet daily series, last 30 days (planet+date) activeUsers per (planet, day) — for sparklines
6 Top pages cosmos-wide, this week (hostName+pagePath) views, users, engagement; planet resolved client-side via resolvePlanet

Queries 1–5 are event-scoped-dim friendly. Query 6 deliberately omits the planet dim to avoid session-level composition issues — we resolve planet from hostName + pagePath post-fetch. This is why we need hostName (resolves / ambiguity across 8 subdomains).

NZT week boundaries

nztWeekRanges() in _cosmos-shared.js uses Intl.DateTimeFormat(en-CA, {timeZone:'Pacific/Auckland'}) to find what day-of-week NZT thinks today is, then computes the last complete Mon–Sun week (the one that ended on the most recent Sunday) and the week before that. Explicit ISO date strings are passed to GA4 — never relative 7daysAgo (which uses property timezone, not NZT, and shifts during DST).

Signal generation (the "Signals to Review" section)

Rule-based, post-query. Each signal has confidence + action_mode + suggestion (never command):

Type Rule
growth users_week >= 50 && users_prev_week >= 20 && wow > 25%
drop users_prev_week >= 50 && wow < -15%
quiet users_week < 10 && action_mode != 'commons' && != 'hub'
star_page top 3 by avg_engagement_sec where views >= 50, users >= 30, eng >= 30s

Suppression rules: - Curriculum (actionMode='commons', suppressGrowthPrompts:true) never gets growth/drop/quiet - Cosmos atlas (actionMode='hub') never gets quiet (it's not supposed to be a destination) - Bounce signal was DESIGNED but deferred — bounce metric composition with event-scoped dim is risky per rubber-duck; verify with a probe query before shipping

Signals capped at 8 per response to keep the dashboard scannable.


Cosmos-bar pill (public surface, simpler logic)

Lifecycle:

connectedCallback
  → fetchAtlas → render → wire → start polling (if not started)
                          setInterval(45_000) ──┐
                              fetchLiveCount → updatePill
                              (AbortController per fetch, partial DOM update)

disconnectedCallback → stopLiveCounter → clearInterval + abort in-flight

Hide rules (counter UX): - total < 2: hide entirely (avoids "lonely 1 visitor" signal) - 3 consecutive fetch failures: hide entirely (no flickering errors) - After page nav: fresh component instance → fresh state

Why cache: 'no-store' was removed (SME critique): it defeated the CF edge cache's 45s TTL → every browser hit the worker direct → 10–100× extra worker traffic. Default fetch cache mode now respects server Cache-Control: max-age=45. CF edge caches the response for 45s and serves it to all clients of that edge POP. Big efficiency win.


Plain AI constitution change (12 May 2026)

The Plain AI Curriculum constitution permanent-NO list previously banned "Individual user tracking (cookies, fingerprinting, analytics events)" wholesale. This blocked GA4 from Plain AI + Curriculum surfaces. Sush narrowed the rule to ban specific behaviours instead of the technology:

Marketing tracking · retargeting · advertising signals · conversion funnels · behavioural personalisation — never. Aggregate analytics for product understanding (GA4 with ad signals denied, custom event dim cosmos_planet) — allowed.

See ~/.copilot/plain-ai-curriculum-philosophy.md evolution log entry dated 12 May 2026 for the diff and rationale. The Wikipedia test passes: Wikipedia runs aggregate page-view analytics; they don't run ads. Same line for us.

Operational implications: - Aggregate analytics ✅ allowed across all 9 surfaces including Plain AI + Curriculum - Targeting an individual reader / showing different content based on history → still forbidden - A/B testing lesson copy → still forbidden (actionMode='editorial' for Plain AI, actionMode='commons' for Curriculum — Cockpit signals respect this) - Curriculum's suppressGrowthPrompts: true flag ensures the dashboard never recommends marketing/funnels for it


How to extend

Add a new planet

Six touch-points (in this order):

  1. Add to cosmos-atlas/src/data/atlas.json — orbit, body, copy. (Cosmos philosophy rules apply — read cosmos-philosophy.md.)
  2. Build the planet repo — Astro or Hugo. Add the gtag snippet to its BaseLayout/baseof, set cosmos_planet: '<new-slug>'.
  3. CSP — if the planet has a CSP, add googletagmanager.com to script-src and google-analytics.com + region1.google-analytics.com to connect-src. (Most planets currently have no CSP. Plain AI is the strict one.)
  4. Add the slug to _cosmos-shared.js: COSMOS_PLANETS, COSMOS_PLANET_KINDS, HOST_TO_PLANETS, PLANET_SEMANTICS. Pick the right actionMode. If the planet has a non-default bounce interpretation, document it in bounceInterpretation.
  5. Test in GA4 DebugView — load the planet with ?_dbg=1 and confirm hits arrive with cosmos_planet=<new-slug> parameter.
  6. Verify on the dashboard — open /cc/ → 🌌 Cosmos tab → the new planet should appear in the leaderboard within 24–72h once GA4 backfills the dim.

Critical: the slug-detection logic is duplicated client-side (per-planet gtag) AND server-side (resolvePlanet in _cosmos-shared.js). Both must match. Consider adding a QA check that compares atlas.json planets against COSMOS_PLANETS in a future iteration.

Add a new outcome event (Phase B work)

The full event catalog is designed in ~/.copilot/session-state/<id>/files/measurement-dictionary.md. Adding one:

  1. Register the event params as custom dimensions in GA4 Admin (event-scoped).
  2. Fire the event from the planetgtag('event', 'cosmos_bridge_click', { from_planet, to_planet, link_location }) on every cross-planet click.
  3. Extend the cosmos-summary worker with a new query that pulls per-planet event counts.
  4. Add a new section to the dashboard to surface the metric.

Top-priority outcome events (planned, not built): - cosmos_bridge_click (cross-cosmos movement — would enable the journey screen) - newsletter_signup (Shift) - guided_checkout_start + guided_purchase (cert funnel; the latter via Stripe webhook → GA4 Measurement Protocol) - kofi_click, youtube_click (cross-channel attribution)


Phase B / C roadmap (not built — designed)

  • Outcome event instrumentation — see above; this is where the dashboard flips from traffic-vanity to decision-grade.
  • Cross-cosmos journey screen — bridge-click matrix, multi-planet session ratio, weak-bridge editorial opportunities. Builds on cosmos_bridge_click.
  • GSC per-planet — each subdomain is its own GSC property; pull into nightly summary. Top queries, low-CTR opportunities, rising terms per planet.
  • LLM Sunday memo — turn the summary JSON into 200 words of prose Sush reads with coffee. Built last (data needs to be trusted + outcomes tracked first).
  • Mobile responsive Cosmos tab — currently desktop-first with horizontal scroll on the leaderboard table. Defer card-style layout until data is proven valuable.

Pre-existing issues (not introduced by this work)

The Cosmos QA suite (cosmos-atlas/scripts/qa-audit.mjs) currently fails 6 checks from commit 870596b (V5 Wave 3 — pink Earth + layout). All are pre-existing and unrelated to this work:

  1. drift toggle off — UI bug (toggle doesn't deactivate on second click)
  2. ambient player state — audio control assertions
  3. guided over brainbar (desktop) — orbit collision
  4. guided vs earth too close (desktop) — orbit collision
  5. guided over brainbar (iphone) — same
  6. guided vs earth too close (iphone) — same

These are flagged for a separate session. My commits in this project touched Base.astro + cosmos-bar/* — none of the blocking-rule files (cosmos.ts, cosmos.css, atlas.json, PlanetIcon.astro, index.astro).


Operational reference (quick lookup)

Resource Value / Path
GA4 property ID 530486519
GA4 measurement ID G-2HWWZGWCD0
GA4 custom dim ID 14859585669 (cosmos_planet, event-scoped)
KV namespace ID db3bd5e5a2aa4c22a453364ce2a25095 (binding: COSMOS_SUMMARY_KV)
KV keys summary:current, summary:lock
CF Pages project for worker aguidetocloud-revamp
Env var (gated endpoints) ADMIN_PASSWORD_HASH (= public hash in layouts/cc/list.html:502)
CF account ID d42846fe2c29daf890ec57877fda5e04
GA4 service account key ~/.copilot/secrets/ga-service-account.json + base64 in CF env
Cosmos QA suite cosmos-atlas/scripts/qa-audit.mjs (BLOCKING for atlas.json/cosmos.ts changes — but NOT for cosmos-bar component changes per rule scope)
Deploy command (cosmos-atlas) npm run deploy (manual — no git auto-deploy on this project)
Deploy command (other planets) git push triggers CF Pages auto-build

Gotchas + lessons (every one cost time to discover)

  1. GA4 Realtime API doesn't support hostName or pagePath — killed v0 plan. Custom event dim is the only solution.
  2. runReport realtime ≠ runReport historical — same API surface, different dimension support. Always verify combinations before assuming they work.
  3. GA4 custom dim propagation has 24–72h lag for historical aggregations. Live data is immediate. Dashboard shows warm_up: true for the first window.
  4. CF Pages env var PATCH danger — silently wipes other secrets if you're not careful. ALWAYS verify all endpoints after PATCH. Pattern: response from CF API confirms keys; then curl 2–3 endpoints that depend on those secrets.
  5. CF Pages KV bindings can break deploys. Don't add a binding without redeploying immediately and verifying the worker still serves.
  6. public/_headers is gitignored in aguidetocloud-revamp. The SOT is static/staticwebapp.config.json, processed by scripts/generate-cf-config.py in the CF build command. Editing public/_headers directly is futile — overwritten on every deploy.
  7. JS === for password compare is a timing attack. Use timingSafeStrEqual from _cosmos-shared.js. Always.
  8. Dual-mode auth (plaintext OR pre-hashed Bearer) is dangerous when the hash is in public HTML. We dropped pre-hashed; plaintext only.
  9. cache: 'no-store' on cross-origin fetches defeats CF edge caching. Trust the server's Cache-Control header; let CF do its job.
  10. liveStarted = true BEFORE the call is a race. If the call throws, the flag sticks and no retry. Always set flags AFTER the operation that can fail.
  11. GA4 concurrency limits are real. Throttle to 3 parallel runReport calls or eat 429s.
  12. indexOf('/learn/')===0 is NOT the same as startsWith('/learn') — the former fails on canonical URL /learn (no trailing slash). Cost: every Curriculum visitor mis-tagged as Plain AI for ~30 minutes between deploy and SME catch.
  13. The cosmos-bar runs on every planet via a single CDN-hosted bundle at https://cosmos.aguidetocloud.com/cosmos-bar.js. cosmos-atlas has NO git-auto-deploy — every cosmos-bar change requires npm run deploy from the cosmos-atlas repo.

Verification checklist (when picking this work up cold)

# 1. All endpoints respond correctly
curl -sf https://www.aguidetocloud.com/api/stats?realtime=cosmos | jq
# Expect: { totalUnique, scope, generated_at }

curl -sf https://www.aguidetocloud.com/api/stats?realtime=cosmos\&intel=1 -w "%{http_code}\n"
# Expect: 401

curl -sf https://www.aguidetocloud.com/api/cosmos-summary -w "%{http_code}\n"
# Expect: 401

# 2. Pill is on every planet (visual check)
# Open each: cmd.aguidetocloud.com, shift.aguidetocloud.com, plainai.aguidetocloud.com,
#            agents.aguidetocloud.com, claw.aguidetocloud.com, cosmos.aguidetocloud.com
# Look for amber-pulse pill between bodies and MCP relay.

# 3. Cosmos tab loads + renders (browser)
# https://www.aguidetocloud.com/cc/ → unlock → click 🌌 Cosmos
# Should show: Pulse / Leaderboard / Signals / Stars (Warm-up banner on day 1).

# 4. GA4 DebugView: every planet's hits include cosmos_planet param with correct slug.

Cross-references

  • Constitution + voice: ~/.copilot/plain-ai-curriculum-philosophy.md (12 May 2026 narrowing entry)
  • Cosmos atlas philosophy: cosmos-philosophy.md
  • Cosmos navigation rail: cosmos-nav-playbook.md
  • Memory system / where docs live: memory-system-architecture.md
  • Parallel-safe git rules: parallel-git-rules.md
  • Deployment playbook: deployment-playbook.md (the 20-step pre-push checklist)
  • Stripe payment playbook: stripe-payment-playbook.md (paid product context — Guided is the only revenue surface)

Built 12 May 2026 NZT. Two rubber-duck passes + one SME code review + 14 commits across 8 repos. Shipped with 100% endpoint verification and zero regressions to existing surfaces.