May 2026 — Zen Shipping Log¶
Audience: Future Copilot CLI sessions picking up where this work left off. Source of truth: This page. The session-journal entries are time-bounded; this page is the durable record. How to use: Read top to bottom on first visit. Skip to "Pending recommendations" at the end if you've read it before.
This page covers the 2-day intensive shipping push (3-4 May 2026) that brought aguidetocloud.com and the guided practice-exam product onto the Zen Design System, fixed a paid-product critical bug, and shipped social-share OG images across both sites.
Why this document exists¶
Sush asked, after ~12 commits in 36 hours:
"Update the learn documentation on all the work you did and the lessons you learnt and all the pending things to do and your recommendations so future sessions can read that and continue the work and can understand the history."
Two-day shipping pushes leave a lot of context that disappears between sessions. This page exists so the next session opens with the full backstory and a clear pickup list, not a Slack-thread reconstruction.
What shipped (chronological)¶
1. Astro nav parity (One Body, Two Organs gap fix)¶
- Commit:
guided/9882ceb - Why: A nav redesign on 2 May (Hugo
f3dda5c) added underline-active style + Mind Maps link + group dividers + ko-fi CTA — but only on the Hugo nav. AstroHeader.astrowas overlooked. Guided pages still showed the old rectangle pill on "Cert Practice Exams" and were missing Mind Maps for ~14 hours. - Fix: Mirrored the entire Hugo nav design into
guided/src/components/layout/Header.astrousing the CSS variable name translation (--text-tertiary→--color-text-secondary, etc.). - Doctrine update: Strengthened the "One Body, Two Organs" rule in
~/.copilot/copilot-instructions.mdwith a parity-file table and CSS variable name mapping. See Zen System Quickref.
2. Layer 1 hub redesign — 5 Hugo hubs + Practice Exams (Astro)¶
- Commits:
aguidetocloud-revamp/65473fa(Hugo desktop),guided/48fe132(Astro Practice Exams desktop),aguidetocloud-revamp/f0eec6f(Hugo mobile),guided/465347c(Astro mobile) - Why: Hub pages jumped from
Title + count + dry one-linerstraight into search + grid — feeling like databases instead of "the overflow". Practice Exams (Astro) was the worst offender: stat-block hero violating Sush's own brag-allergy rule, inline style override, 6 companion sections. - Shipped a shared Zen pattern (
zt-hub-*CSS in both copies ofzt-reading.css): .zt-hub-intro— belief subtitle (replaces dry.zt-blog-header-desc).zt-hub-voice— testimonial pull-quote (mirrors homepage.zh-pull).zt-hub-featured— Where-to-start / Most-useful / Most-watched pinned row.zt-hub-disclosure— small honest footnote (e.g. licensing currency note).zt-blog-row--new— subtle indigo dot indicator (replaces loud "New" word badge)- Per-hub belief copy + voice: see Zen System Quickref → Hub Zen pattern
- Killed 22 KB of dead code in
free-tools/list.html— file dropped from 28 KB / 477 lines to 6 KB / 100 lines (commented-out hardcoded card block left over from the Concept 1 prototype migration). - Removed view counts from per-card video meta entirely (replaced with "Most watched" featured row — top-N selection without surfacing the numbers).
3. Mind Maps hub Zen alignment¶
- Commit:
aguidetocloud-revamp/8d1ff82 - Why: Mind-map session shipped 4 batches of new mind maps and a strong custom
mm-*visual system, but the hub still had a dry feature-description lede and dense filter rows on mobile. - Fix: Belief lede ("Some concepts only click once you see the whole picture at a glance…") +
mm-list-disclosureline ("Free to view, download as a branded PNG, or share. No sign-up.") + horizontal-scroll filter pills on mobile. - Skipped voice — none of the 4 testimonials in the library specifically fits mind maps. Honest move = silence over forced fit.
4. Layer 2 — Practice cert landing page redesign¶
- Commit:
guided/2a513cf - The big one. Single Astro template change shipped to ~80 live cert pages.
- Killed: stat strip,
style="background:#16a34a !important"green button, hard-coded AZ-900-specific Mind Maps section in companion (was wrong on every other cert). - Added: belief paragraph ("\$79 practice exams are gatekeeping. Mine are \$9 because it should be."), @yourdream8 testimonial pull-quote, two-CTA layout (filled-indigo primary "Try 20 questions free" + bordered-indigo secondary "Unlock all — \$9"), bonus benefit line for the included study guide, 6-cell trust grid (3 cols desktop / 2 cols mobile), quiz anchor line celebrating the embedded-quiz superpower.
- Companion sidebar trim: 8 sections → 3 (Quick facts · What's included · Explore more).
5. 🔴 P0 — Cert landing ↔ practice page checkout LOOP¶
- Commit:
guided/7d3cf97 - Caught by Sush, not by SLA tests. Full incident write-up: Production Incident Log (see "3 May — Cert-landing ↔ practice page checkout LOOP" entry)
- Root cause: Two pre-existing bugs interacted.
practice.astrohad<a href="/guided/{cert}/?checkout=1">linking back to cert landingPracticeQuiz.tsxslug-extraction returned'guided'(not the cert code) on the 2-segment cert-landing URL →initiateCheckoutsilently failed → URL was cleaned → user stuck → ping-pong infinite loop- The 3 SLA curl checks all PASSED (questions JSON 200, practice 200, checkout API → Stripe URL). Static-load tests cannot catch click-flow bugs. That was the lesson.
- Fix: Both Unlock buttons are now
<button>+ inline<script is:inline>that callsfetch('/guided/api/checkout')directly and redirects to Stripe. No URL navigation between pages, no React-hydration race, no slug-parsing dependency. One click, one fetch, one redirect. - New robust guardrail:
testCheckoutFlow(page, cert)Playwright function intest-guided-qa.cjsthat: - Asserts
id="cert-unlock-btn"andid="practice-unlock-btn"are present (forces the direct-fetch pattern) - Regex-checks both pages for the loop URL pattern (
href="/guided/{cert}/?checkout=1") - Intercepts
POST /guided/api/checkoutviapage.route(), validates thecertCodepayload +productType: 'cert' - Runs on BOTH the practice page button AND the cert landing button
6. Mobile polish — Study/Exam toggle row¶
- Commit:
guided/e11102b - Why: Study Mode + Exam Mode + Start button row was cramped on 375px mobile.
- Fix: 6-line CSS change — container stacks vertically on mobile, mode pills split 50/50, Start button full-width. Outer card padding
p-6→p-4 sm:p-6.
7. OG images for practice pages — generator + 123 images¶
- Commit:
aguidetocloud-revamp/6957f0f(Hugo: generator + images),guided/24b89d6(Astro: SEO meta wired) - Why: None of the
/guided/{cert}/or/guided/{cert}/practice/pages hadog:imageset. Social shares (Twitter, LinkedIn, WhatsApp, Slack, Teams) showed no preview image. - Built the generator infrastructure:
scripts/og-generator/template-practice.html— 1200×630 dark canvas, huge cert code (Inter 240-280px), code-length-aware sizing (xl/lg/md/sm/xs), subtle indigo radial glow, indigo accent line, "Practice Exam" subtitle, footer withaguidetocloud.com+from $9 · 20 free questionsvalue anchorscripts/og-generator/generate_practice_og.py— Python+Playwright (mirrors the existinggenerate_cert_og.pypattern). Reads cert TOMLs from siblingguided/src/content/certs/*.toml. Filtersstatus=="live". Hash-based incremental.- 123 images generated locally and committed (ranged 38-54 KB each)
- Astro side wiring:
BaseLayout.astro— addedogImage+ogImageAltprops, absolute-URL resolution viaAstro.site, emitsog:image:width/height/alt+twitter:image+og:site_name[slug]/index.astro— passes ogImage + adds Product JSON-LD (\$9 paid practice exam with Offer/availability) + BreadcrumbList JSON-LD[cert]/practice.astro— passes ogImage + BreadcrumbList JSON-LD- Cross-domain note: Hugo and guided are both served from
www.aguidetocloud.com(subpath/guided/), so a guided page can reference/images/og/practice/foo.jpgand the request resolves to Hugo static folder.
8. OG fallback chain — every page now has og:image¶
- Commits:
aguidetocloud-revamp/0175232(Hugo cert-tracker auto-derive),guided/9b20e94(Astro default fallback + per-cert module OG) - Why: After the practice-pages OG work, the audit showed:
- Hugo: every page had og:image via the
baseof.htmlfallback chain (frontmatter → YouTube thumb → about → og-default), but 98 cert-tracker pages were falling back to the generic default even though their custom images already existed at/images/og/certs/{slug}.jpg - Astro: if
ogImageprop wasn't passed,og:imagewas completely absent (no fallback). 2,000+ study-guide module pages and the guided home/help/dashboard had nothing - Fixes:
- Hugo
baseof.html— added oneelse if (eq .Section "cert-tracker") .IsPage .Filebranch that auto-derivesog:image = images/og/certs/{BaseFileName}.jpg. Hits 98 pages instantly. - Astro
BaseLayout.astro—ogImagedefaults to/images/og-default.jpg(Hugo's shared default) instead of being conditional. Now EVERY guided page emits og:image + twitter:image. - Astro
ModuleLayout.astro— auto-derivesogImage = /images/og/practice/{cert.toLowerCase()}.jpgfrom the existingcertprop. Hits ~2,000 study-guide module pages with zero new images. - Bug caught in pre-deploy check:
cert.toLowerCase()— module page initially resolved topractice/AZ-900.jpg(uppercase) which would 404 on Cloudflare. Lesson: always lowercase when deriving image URLs from frontmatter.
Lessons learnt (durable)¶
These are now part of the doctrine. Future sessions read these before touching the same surfaces.
Static-load tests cannot catch click-flow bugs¶
The 30 April outage AND the 3 May checkout loop both passed all three SLA curl checks (questions JSON 200, practice 200, checkout API → Stripe URL). The user-facing CLICK flow was broken in both cases. Always add a Playwright click test for any new CTA on the paid product. Pattern is in test-guided-qa.cjs → testCheckoutFlow().
<a href="?checkout=1"> URL-trigger pattern is fragile¶
Depends on JS hydration timing, slug parsing, and silent-fail catches. Use direct <button> + inline-script-fetch instead. More robust, faster (no page reload), no race condition.
Pathname-based parsing is brittle¶
Always pass IDs as props or data-* attrs when available. The 3 May loop existed because the slug-extraction regex worked on 3-segment URLs but failed silently on 2-segment ones.
Always lowercase derived image URLs¶
Cloudflare is case-sensitive. practice/AZ-900.jpg ≠ practice/az-900.jpg. When building paths from frontmatter that may be uppercase (cert codes, vendor slugs), .toLowerCase().
Hub pages need warmth before content¶
The homepage works because it leads with belief + voice (@lalitds testimonial) before showing anything to click. Hub pages that go straight from H1 → search → grid feel like databases, not overflow. The zt-hub-* shared pattern (intro / voice / featured / disclosure) is the answer. Use it on every new hub.
Brag-allergy enforcement¶
Stat-pill rows in heroes ("N exams · M vendors · X free"), per-card view counts on videos, 6-section "Quick stats" sidebars — all violate the user's own values. Sush flagged this multiple times. Counts as small badges next to title is OK; counts as a hero stat block is not.
Skip the testimonial when nothing fits¶
Forcing a voice that doesn't match the page (e.g. trying to fit @yourdream8's exam-pass quote on the Mind Maps hub) is cargo-cult. Honest move = silence. Used on Licensing, Free Tools, Mind Maps.
Parallel-session safety¶
Multiple incidents this push where a parallel mind-map session was running. Always:
- git status -s before any commit
- git add <explicit paths> — never . or -A
- git diff --staged to confirm only your files
- git pull --rebase immediately before push
The mind-map session and Zen-shipping session ran for ~6 hours with zero file collisions because of these rules.
One Body, Two Organs is non-negotiable¶
Hugo (aguidetocloud-revamp) and guided (Astro) are ONE product served from one domain. Any change to nav, footer, theme toggle, design tokens, OG meta, or shared chrome MUST be applied to BOTH platforms in the SAME session. The 2 May Astro nav drift cost ~14 hours of wrong UI. The "One Body, Two Organs" parity-file table in copilot-instructions.md lists every file pair that must change together.
Premium products use ONE accent confidently¶
The cert landing page had #16a34a !important on the buy button (green). It looked "right" by payment-product convention, but broke Zen brand integrity. Replaced with two indigo CTAs — primary filled, secondary bordered. Brand consistency wins over convention.
Pending recommendations (for next sessions)¶
This is the pickup list. Items are grouped by surface area + priority. Each item has enough context that a fresh session can act without re-investigating.
🔴 Layer 2 sections still pending (one per session — no big bang)¶
The Layer 2 audit identified 5 page categories. Section 1 (Practice cert landing) shipped as commit guided/2a513cf. Remaining:
- Video Single pages — single shared partial
layouts/partials/video-single-content.htmlships to ~70 pages - Remove view + likes from meta line (brag-allergy fix, same pattern as videos hub)
- Add belief intro slot via optional
video_introfrontmatter field - Filter related-videos to same section AND tag overlap (currently date-recency)
-
Effort: ~30 min, one-file edit
-
Study Guide pages —
layouts/cert-tracker/single.html, 32 KB layout, 150 pages - Kill
cert-study-hero3-stat block ({N} modules · ~{X}h study time · {Y} completed) — same brag fix - Replace 6 emoji feature pills (📖 ELI5 / 🔄 Flashcards / etc.) with one belief paragraph
- Add @pablonleon testimonial (perfect fit — "passed many exams using your videos")
-
Effort: ~60 min
-
Blog Post pages —
layouts/blog/single.html, 17 posts (will grow) - Add author bio block at end of post (Sutheesh + 2-line + links)
- Add inline newsletter signup form before back-nav (highest-converting moment)
- Filter "More articles" companion to same
card_tag(currently 5 most recent regardless of topic) -
Effort: ~30 min
-
Licensing Plan pages —
layouts/licensing/single.html, 50 plans - Surface "USD per user/month, retail (no enterprise discount)" caveat near the price (currently buried in collapsed disclaimer)
- Filter "Related Blog Posts" using the
card_tag = Licensingfilter (currently substring match on title) -
Effort: ~20 min, smallest pass
-
Free Tools individual pages — 56 tools
- Each tool is its own functional UI (calculator, builder, generator). Hub Zen pattern doesn't apply directly. Treat as Layer 3 in a separate audit cycle.
🟡 Brain bar UX decision (open question for Sush)¶
Sush flagged 4 May: "Because this brain bar can't act as a search box for the entire site, don't you think instead of a search box appearing when they click on the icon in the nav bar should open the whole page?"
My audit found:
- Brain bar is a Microsoft jargon decoder (~5 entries committed; lives at cmd.aguidetocloud.com)
- Nav button uses $_ terminal-style icon (not magnifying glass — already a calmer signal)
- Current behaviour: click opens a Cmd-K-style modal with placeholder "decode a microsoft term — try mde · pim · e3 · foundry"
- Power users use / or Ctrl+K to open mid-flow
My recommendation (Option C — hybrid):
- Click $_ button → navigates to a dedicated /brainbar/ page (or cmd.aguidetocloud.com)
- Keyboard shortcut / and Ctrl+K still open the modal (preserves speed for power users)
- Polish modal copy: more explicit it's a decoder ("Microsoft jargon decoder. Looking for site search? Use Ctrl+F or browse topic hubs.")
Awaiting Sush's choice between:
1. Cross-domain — click goes to cmd.aguidetocloud.com directly (one source of truth, slight trust hit leaving main site)
2. Same-domain landing — new /brainbar/ page on aguidetocloud.com that explains decoder + has examples + search box, with "Open full brain bar →" link to cmd.aguidetocloud.com
🟢 OG generator wiring into CI¶
The new generate_practice_og.py --stale step was added to .github/workflows/deploy.yml but the Cloudflare Pages CI checks out only the Hugo repo — the script soft-fails if the guided sibling isn't present. For now, regeneration is manual (run locally when new certs are added → commit images).
Two paths to make this robust when the cert library grows:
1. Add a actions/checkout step in deploy.yml that pulls the guided repo (read-only) before the OG generation step
2. Commit a snapshot of cert codes (just slug + code JSON) into the Hugo repo so the script runs standalone
🟢 zt-reading.css drift between Hugo and Astro¶
Flagged during Layer 1 work but not fixed. Hugo static/css/zt-reading.css (~132 KB) and guided public/css/zt-reading.css (~113 KB) have drifted ~17 KB from each other. Same hub additions went to both copies, but the older bodies diverged before May. Worth a careful diff-and-merge session before Layer 2 if any shared zt-* changes are needed.
🟢 Mobile filter-pill collapse to <details>¶
Current solution is horizontal-scroll on .zt-blog-filters, .zt-lic-pills, .mm-list-tabs, .mm-format-chips. A future polish pass could collapse them into a <details> "Filter ▼" toggle on mobile for even cleaner first-touch. HTML change so deferred.
🟢 Sticky bottom-bar on Practice cert landing (mobile V2)¶
The Layer 2 Section 1 redesign deliberately deferred the mobile sticky-bottom-bar pattern (premium pattern for paid products — slim "Try 20 free →" bar that appears when user scrolls past hero, hides when embedded quiz is in viewport). Worth shipping as Practice page V2 once V1 has some traffic data.
🟢 Per-section OG generators (only if traffic warrants)¶
If specific licensing plans, prompts, or other low-priority sections start trending in analytics, generate per-section OG using the same Python+Playwright pattern as generate_practice_og.py. For now, the og-default.jpg fallback is doing exactly what was asked for.
🟢 Calendar items still pending (in copilot-instructions.md)¶
These fire on session start. None acted on in this push:
- 2026-04-28 / 2026-05-05 — AI-200 + SC-500 study guide watch (Microsoft beta launches). URLs already documented.
- 2026-05-01 — Agent 365 GA verification (Planner data, ME7 blog update, E7 in Feature Matrix)
- 2026-07-01 — Licence Picker price update (MS prices change July 2026)
How to use this page¶
When you (future Copilot) open this codebase:
- Read top to bottom on first session. Get the full backstory.
- Cross-reference with Production Incident Log for the deeper why behind specific guardrails (the click-flow rule, the PowerShell parser issues, etc.).
- Cross-reference with Zen System Quickref for the actual CSS/component patterns.
- When picking up a "Pending recommendation", always run the SLA pre-flight first if you're touching any practice-exam surface. Then make the change. Then run
test-guided-qa.cjs(the click-flow guardrail will catch the loop bug class). - When you finish a pending item, strike it from this page in the same commit as the work. Don't leave a stale pickup list.
- When you discover a new lesson worth keeping, add it to the "Lessons learnt" section above. The doctrine grows.