Skill v1.0.2
currentAutomated scan100/1005 files
version: "1.0.2"
SKILL.md — RAD Platform Docs: Design Principles, Best Practices & Fixes
This document captures the accumulated engineering knowledge for the RAD Platform documentation site (docs.radmodules.dev). It is the canonical reference for contributors and automated agents working in this repository.
1. Project Overview
RAD (Rapid Application Deployment) Platform provides Google Cloud infrastructure as Terraform/OpenTofu modules. This Docusaurus site is its documentation layer, covering:
- Module configuration guides (269 modules: 128 Cloud Run + 141 GKE variants, plus platform/infra pages)
- Hands-on lab guides (273 modules, one-to-one with the module guides)
- Role-based guides (Using RAD, AI Tooling, User/Partner/Agent/Support/Admin/Finance)
- GCP certification prep (ACE, PCA, PCD, PCDE, PCNE, PDE, PSE — 34 per-section exploration guides plus one guide per certification)
- Platform design docs: Platform Capabilities, Engineering Practices, Engineering Excellence
Live site: https://docs.radmodules.dev Tech stack: Docusaurus 3.9.2, React 19, TypeScript 5.6, GitHub Pages (gh-pages branch)
2. Repository Structure
rad.github.io/├── docs/ # Published documentation — five flat sections, no subdirs│ ├── certification/ # 34 per-section exploration guides + 7 per-cert guides│ │ # (ACE, PCA, PCD, PCDE, PCNE, PDE, PSE)│ ├── design/ # 3 platform design docs (capabilities, practices, excellence)│ ├── guides/ # 8 role-based guides (Using RAD, AI Tooling, User/Partner/│ │ # Agent/Support/Admin/Finance)│ ├── labs/ # 273 hands-on lab guides — flat, one file per module│ └── modules/ # 269 module configuration guides — flat, one file per module│ # (plus a few platform/infra pages: Services_GCP,│ # Migration_Center, Container_Migration, VMware_Engine)│ # docs/ is updated directly from the source repos│ # (rad-modules, rad-automation, partner-modules);│ # there is no longer an updates/ staging dir — see §11│├── src/│ ├── components/ # Custom React components│ │ └── YouTubeEmbed/ # Video player with GCS poster support│ └── css/ # Theme overrides│├── apply_all_updates.py # LEGACY (updates/ → docs/) — retired, not run; see §11├── apply_module_updates.py # LEGACY merge logic — retired; kept for reference├── update_videos.py # Bulk YouTube embed insertion (5 patterns) — currently unused, see §7.2├── verify_changes.py # Playwright browser verification├── verify_final.py # Final Playwright verification with video recording├── docusaurus.config.ts # Site configuration└── sidebars.ts # Sidebar navigation (explicit, not auto-discovered)
3. Documentation Architecture
3.1 Flat File-per-Module Pattern
docs/labs/ and docs/modules/ are both flat directories — one file per module, named after the module directly (no per-module subdirectory):
docs/labs/App_CloudRun.mddocs/modules/App_CloudRun.md
An earlier subdir-per-module layout (docs/modules/App_CloudRun/App_CloudRun.md) was used historically but the site was flattened; if you find a reference to the old nested path, it's stale — fix it to the flat form.
3.2 Flat Sections with Name Normalisation
certification/, design/, and guides/ are likewise flat directories. Source files use varied naming conventions; map them to the canonical docs/ filename when syncing (the legacy NAME_MAP in apply_all_updates.py records some historical mappings for reference, though most current sections use each source repo's filename as-is).
3.3 Multi-Source Priority System
Docs originate in three source repositories. When the same page exists in more than one, precedence is (highest wins):
rad-modules— baseline module documentationrad-automation— platform automation overridespartner-modules— partner module content (highest priority)
Edit the canonical source, then sync directly into docs/ (§11). These are sibling checkouts of this repo, not an in-repo updates/ staging directory.
3.4 Sidebar Navigation is Explicit
sidebars.ts defines every entry manually using { type: 'doc', id: '...', label: '...' }. Auto-discovery is not used. This keeps navigation intentional and prevents orphan pages from appearing in the sidebar.
Sidebar entry format:
{ type: 'doc', id: 'modules/App_CloudRun', label: 'App CloudRun' }
Important: the label here is the only thing that names this page in the nav — front matter sidebar_label is not used (§10.1).
4. Front Matter
Every doc must begin with a YAML front matter block:
---title: "App on Cloud Run — Configuration Guide"description: "Configuration reference for deploying App on Cloud Run with the RAD module — variables, architecture, networking, and operations."---
title— used by Docusaurus for<title>and social sharingdescription— meta description for SEO/social sharing; every current page sets one- `sidebar_label` is not used anywhere in the current site (verified: zero occurrences across
docs/). Nav labels are set directly insidebars.ts({ type: 'doc', id: '...', label: '...' }) — thatlabelfield, not front matter, is authoritative. Do not addsidebar_labelto new pages; if you find it on an old page it's a no-op leftover, safe to remove. idandslugare optional and rarely needed; omit unless there is a specific routing reason
When adding a new page from a source repo (which ships no front matter), create a minimal block from the first # Heading, then add the sidebars.ts entry (§10) — pages are not auto-discovered. Use this shape:
---title: "<the page's # heading text>"description: "<one-sentence SEO description>"---
5. Content Patterns
5.1 Module Configuration Guide Structure
---title: "Module Name on Google Cloud Run"description: "Configuration reference for deploying Module Name on Google Cloud Run with the RAD module — variables, architecture, networking, and operations."---# Module Name on Google Cloud Run<img src="https://storage.googleapis.com/rad-public-2b65/modules/Module_Name_CloudRun.png" alt="Module Name on Google Cloud Run" style={{maxWidth: "100%", borderRadius: "8px"}} />[Module description paragraph][ASCII architecture diagram]## GCP Resources Created| Resource | Name Pattern | Description ||---|---|---|...## Input Variables### Category-`variable_name` (type) — description
Decision guidance + pitfalls conventions (established across the Services_GCP / App_CloudRun / App_GKE guides, 2026-06): a strong config guide does more than list variables — it helps the reader decide.
- Per-group "Choosing…" blockquote. Lead each variable group that has real trade-offs with a
> **Choosing <thing>.** …blockquote naming the decision axis (cost vs availability, managed vs self-managed, exposure model, engine choice) — when/why, not just how. Don't add one to trivial groups. - "Configuration Pitfalls & Sensible Defaults" table. Risk-leveled (Critical/High/Medium/Low) table of value/combination mistakes and their consequences, with a preamble noting that many are now caught at plan time.
- 🛡 plan-time badge. In the pitfalls table, mark rows the module rejects at plan time (via
validationblocks orvalidation.tfpreconditions) with 🛡 plan-time, and phrase the consequence as "…is rejected at plan time" rather than a runtime failure. Leave unmarked the genuine runtime/operational/sizing hazards the module cannot decide. State explicitly that a clean plan confirms value/combination rules, not sizing or topology.
5.2 Lab Guide Structure
---title: "Module Name on Cloud Run — Lab Guide"description: "Hands-on lab: deploy Module Name on Cloud Run in your own Google Cloud project — guided setup, verification, operations, observability, and teardown."---# Module Name on Cloud Run — Lab Guide📖 **[Configuration Guide](link)**[Brief intro]---## Table of Contents1.[Overview](#1-overview)...## 1. Overview### What Is Module_Name?### Key Capabilities Demonstrated| Capability | What It Demonstrates ||---|---|## 2. Architecture[ASCII box diagram]## 3. Prerequisites## 4. Lab Setup## Exercise 1 — Title### Objective### Steps1.Navigate to **GCP Console → Cloud Run**2.Click **Service name**## Cleanup## Reference
Foundation-bound "full power" lab conventions (App_CloudRun / App_GKE labs, 2026-06): a module lab should let the reader exercise the breadth without re-documenting every variable.
- Foundation binding note. App-module labs deploy onto a
Services_GCPfoundation — state up front to use the same `tenant_deployment_id` so the module auto-discovers the shared VPC / Cloud SQL / NFS / cluster / registry instead of provisioning inline. - "Choose your lab path" step. Offer Path A — Minimal (defaults, set only
project_id+tenant_deployment_id) and Path B — Full-Feature (a ready-to-paste config that turns on a representative breadth — DB, NFS, GCS, Redis, init jobs, uptime check, IAP). Keep the highest-blast-radius features (Binary Auth, VPC-SC, IAP) in safe modes so a learner can't lock themselves out. - Per-feature verification. Tag verification steps with the flag that enables them (e.g.
[enable_nfs = true]) so Minimal-path users skip cleanly, and verify each enabled capability actually came up (workload health/shape, DB + secret, NFS/GCS/Redis wiring, init-job success, IAP enforcement, uptime check) — not just a single health check. - Plan-time validation note. Mention that invalid values/combinations are rejected at plan time, cross-linking the config guide's pitfalls table.
- Keep the lab's lean operational spine (Deploy → Access → Operate → Observe → Troubleshoot → Tear down); defer exhaustive variable detail to the config guide so the lab stays accurate over time.
5.3 Role-Based Guides Structure (docs/guides/)
---title: "Admin Guide"description: "RAD Platform administrator guide — managing users, roles, organizations, module catalogs, deployments, and platform settings."---# Admin Guide<img src="https://storage.googleapis.com/rad-public-2b65/guides/Admin_Guide.png" alt="Admin Guide" style={{maxWidth: "100%", borderRadius: "8px"}} />[Introduction]## Guide Section-**Topic** — description
The 8 guides (using-rad.md, ai-tooling-gcp.md, user-guide.md, partner-guide.md, agent-guide.md, support-guide.md, admin-guide.md, finance-guide.md) each get an infographic <img> after the H1, sourced from {GCS_BASE}/guides/<Guide_Name>.png (§7).
5.4 Platform Design Docs Structure (docs/design/)
---title: "Platform Capabilities"description: "What the RAD platform provides — module catalog breadth, deployment automation, and operational guarantees."---# Platform Capabilities<img src="https://storage.googleapis.com/rad-public-2b65/guides/Platform_Capabilities.png" alt="Platform Capabilities" style={{maxWidth: "100%", borderRadius: "8px"}} />> **Scope.** [One-sentence canonical ownership statement][Body sections with cross-references to modules/]
The 3 design docs (platform_capabilities.md, engineering_practices.md, engineering_excellence.md) source their poster images from {GCS_BASE}/guides/, not {GCS_BASE}/design/ — they were folded into the guides/ bucket prefix when the images were added.
6. Formatting Rules
6.1 Heading Hierarchy
| Level | Usage | |
|---|---|---|
# | Page title only (one per file, matches the label in sidebars.ts) | |
## | Major sections (numbered 1, 2, 3… in labs) | |
### | Subsections | |
#### | Deep details (use sparingly) |
6.2 Bold and Code Inline
- Bold for clickable UI elements:
**Save**, **Deploy**, **Admin Settings** Code backticksfor technical terms: `variable_name,terraform apply`
6.3 Module Display Names
Module names in headings and table cells omit underscores; use spaces:
# App CloudRun — Configuration Guide ✓# App_CloudRun — Configuration Guide ✗
The sidebars.ts label may retain underscores for file-name matching, but the # heading and table cells must use spaces.
6.4 ASCII Box Diagrams
Architecture diagrams use Unicode box-drawing characters (not ASCII hyphens). Every line in the box must end at the same column as the right │ border. Off-by-one spacing makes borders appear broken.
┌──────────────────────────────────────────────────────────────────────────────┐│ Services_GCP ││ ALWAYS CREATED OPTIONAL (feature flags) ││ ───────────── ────────────────────── ││ • 46 GCP APIs enabled • PostgreSQL (create_postgres) │└──────────────────────────────────────────────────────────────────────────────┘
Fix applied (commits 3047598, b93ac3e): 658 misaligned lines were corrected across 56 lab and module docs, with the same corrections made in the source repos so a re-sync does not reintroduce them.
6.5 Section Dividers
Use a blank-line --- divider between major sections:
[Section content]---## Next Section
7. Infographic Images and Video Embeds
7.1 Infographic <img> header (current, live convention)
314 pages across modules/ (269), guides/ (8), design/ (3), and certification/ (34) carry a single plain <img> tag immediately after the H1, linking a GCS-hosted infographic PNG. `labs/` pages do not have this image (0 of 273, as of 2026-07-18) — there is no separate lab infographic asset in the bucket, and nothing currently links the module's image onto its lab page either. There is no video, no PDF link, and no wrapping component on any of these — just:
# Page Title<img src="https://storage.googleapis.com/rad-public-2b65/{category}/{Slug}.png" alt="Page Title" style={{maxWidth: "100%", borderRadius: "8px"}} />[body content]
- One blank line before the tag, one blank line after, matching the blank line the body already had — do not add a second blank line.
alttext is sourced from the page's own#heading text (not from front mattertitle, which sometimes carries an escaped\uXXXXsequence instead of the real character).- GCS category → bucket prefix mapping (these do not all match the
docs/folder name): docs/modules/*.md→{GCS_BASE}/modules/<Module_Name>.pngdocs/labs/*.md→ no image (not yet populated — see above)docs/guides/*.md→{GCS_BASE}/guides/<Guide_Name>.pngdocs/design/*.md→{GCS_BASE}/guides/<Design_Name>.png(design docs' images live under theguides/bucket prefix, notdesign/)docs/certification/*_Section_*_Exploration_Guide.md→{GCS_BASE}/certification/<cert-lowercase>_section<N>.png- GCS base:
https://storage.googleapis.com/rad-public-2b65 - This is source-owned content, not site-owned. Because these images were inserted directly into
docs/here first and then ported back intorad-modules/rad-automation/partner-modules(§11), the<img>tag now lives in the source repos too — a future sync will not strip it. If you add a new page or a new category, insert the<img>tag in the source repo, not just here.
7.2 <YouTubeEmbed> component (built, not yet wired into any doc page)
src/components/YouTubeEmbed/ is a real, working React component (video player with GCS poster support) but zero pages currently reference it (verified: no YouTubeEmbed, sidebar_label, or .pdf occurrences anywhere in docs/ as of 2026-07-18). The videos themselves are being published to YouTube via rad-automation's pnpm youtube:publish pipeline (unlisted, SEO metadata sourced from each doc's front matter) — embedding them back into these doc pages is a separate, not-yet-done step. If you take that on, update_videos.py's five-pattern logic (§11.3) is the intended mechanism, but treat it as unverified against the current <img>-only page shape and confirm its patterns still match before relying on it.
8. MDX / JSX Build Error Prevention
Docusaurus renders .md files as MDX. Raw HTML and template syntax in prose can trigger build failures.
8.1 Escape Angle Brackets in Prose
Any < or > that is not a valid HTML/JSX tag must be escaped:
Response time was <200ms ✗ (parses as opening tag)Response time was \<200ms ✓Response time was <200ms ✓
Fix applied (commit 6703130): Escaped <200ms in Django_CloudRun.md.
8.2 Escape Bare Template Variables
Shell or Terraform variable syntax like ${SERVICE} is treated as JSX expression interpolation:
Run `${SERVICE}` ✗ (MDX error: ReferenceError)Run `\${SERVICE}` ✓
Fix applied (commit 4772e57): Escaped ${SERVICE} across prose text.
8.3 Avoid Unquoted Identifier-Like Expressions in Prose
Docusaurus SSG (Static Site Generation) runs JSX. Even valid-looking expressions like {DB_NAME} in Markdown text are evaluated as JSX. Use backticks or escape them:
Set DB_NAME to your database name. ✓ (no braces)Set `DB_NAME` to your database name. ✓ (code span)Set {DB_NAME} to your database name. ✗ (SSG ReferenceError)
Fix applied (commit 5b1eeb6): Fixed {DB_NAME} and {DB_USER} in Wordpress_Common.md.
8.4 Escape Angle Brackets in Code Placeholders
Placeholder syntax like <external-ip> in prose (outside code fences) is parsed as an HTML tag:
Use <external-ip> as the address. ✗Use `<external-ip>` as the address. ✓
Fix applied (commit 8e4ef98): Wrapped bare angle-bracket placeholders in backticks in Sample_GKE.md.
9. Cross-Reference Links
Internal links use relative paths from the current file's location. Do not use absolute doc paths:
[See Platform Capabilities](../design/platform_capabilities.md) ✓[See Platform Capabilities](/docs/design/platform_capabilities) ✗
Docusaurus validates all links at build time (onBrokenLinks: 'throw'). A broken link fails the entire build.
Fix applied (commit 06021e7): Corrected broken cross-reference links across capabilities, outcomes, and practices docs.
When linking to a directory that has a single file (e.g., Ghost_Common/), link to the file directly:
[Ghost Common](../Ghost_Common/Ghost_Common.md) ✓[Ghost Common](../Ghost_Common/) ✗ (resolves to directory, not file)
Fix applied (commit b36cf9a): Fixed Ghost_GKE broken link pointing to a directory.
10. Sidebar Navigation Rules
10.1 sidebar_label in Front Matter Is Not Used — sidebars.ts Is the Only Source of Truth
Historically sidebar_label in YAML front matter overrode the label set in sidebars.ts, which caused conflicts. It has since been removed from every page (verified: zero occurrences across docs/ as of 2026-07-18). Rename a nav entry by editing its label in sidebars.ts only — do not add sidebar_label back to front matter.
Fix applied historically (commit 7feefc4): Removed conflicting sidebar_label from front matter where sidebars.ts was the intended source of truth. That cleanup is now complete site-wide.
10.2 Sidebar Section Positioning
Menu order is determined entirely by the order of entries in sidebars.ts. To move a section, move its entry in that file. There is no auto-sorting.
Changes applied:
Foundationmoved to top of Partner Modules (commit b1565f1)Credit Managementmoved from Getting Started to Platform Tutorials (commit 1ac507d)VMware Enginemoved from Partner Modules to Platform Modules (commit 7da9c0d)- Sidebar menu reordered (commit df174d0)
10.3 Module Name Casing in Sidebar Labels
Sidebar labels for GKE/CloudRun modules should use the format Module CloudRun or Module GKE (with a space, not an underscore). Display names in sidebars.ts are independent of the file path which retains underscores.
11. Update / Sync Workflow
Documentation originates in the source repositories — rad-modules, rad-automation, and partner-modules (checked out as siblings of this repo) — each under docs/<section>/<file>.md. The site is now updated directly from those source repos.
The `updates/` staging directory is no longer used. Earlier, source docs were copied intoupdates/<project>/docs/...and merged intodocs/byapply_all_updates.py/apply_module_updates.py. That staging step has been retired — edit the doc in its source repo and sync it straight intodocs/. Theapply_*.pyscripts remain in the repo for historical reference only and should not be run.
11.1 Canonical source per doc
A given page has one canonical source repo. Where the same page exists in more than one, precedence is unchanged: rad-modules (baseline) → rad-automation → partner-modules (highest priority). Edit the doc in its source repo first so the two stay in sync, then sync it into the site.
11.2 Syncing a source doc into the site
Source docs are now almost plain Markdown: as of 2026-07-17 the infographic <img> header (§7.1) was ported into the source repos too, so it is source-owned, not site-owned, for the 314 pages that carry one — a normal sync no longer strips it. Only the front matter block remains site-owned (source repos ship no YAML front matter at all). When updating docs/<section>/<file>.md from its source:
- Keep the existing front matter block (
--- … ---) verbatim. - Replace everything from the first
#heading onward with the source body (this now includes the<img>infographic tag, if the source page has one — don't strip it back out). - Re-apply the formatting and MDX-safety rules (§6, §8) — escape
<,${…}, and{…}in prose, and verify ASCII box alignment — since source bodies are not authored against Docusaurus/MDX.
This is the same preservation the retired apply_update() performed automatically; it now happens as part of the sync. A quick way to apply it for a single file:
# From the rad.github.io repo root, with the source repo as a sibling.# Preserves the site front matter (first --- … --- block) and replaces the body.python3 - <<'PY'src = "../partner-modules/docs/modules/Services_GCP.md" # source-owned bodydst = "docs/modules/Services_GCP.md" # site-owned front matterimport iolines = io.open(dst, encoding="utf-8").read().splitlines()close = next(i for i in range(1, len(lines)) if lines[i].strip() == "---")fm = "\n".join(lines[:close+1])body = io.open(src, encoding="utf-8").read().rstrip("\n")io.open(dst, "w", encoding="utf-8").write(fm + "\n\n" + body + "\n")PY
The snippet above preserves only the front matter and replaces everything after it — that's correct now, since the<img>header is part of the source body it's replacing with. Only reach forupdate_videos.py(below) if you're doing the separate, not-yet-done work of embedding YouTube videos (§7.2) — it doesn't apply to the current infographic-only pages.
11.3 update_videos.py
Bulk-inserts <YouTubeEmbed> components and PDF links, from a hardcoded list of (file_path, video_id, [img_name, pdf_name]) tuples. Unused since the switch to the plain-`<img>` infographic convention (§7.1) — no page currently has a <YouTubeEmbed> for it to update, and its five patterns were written against the old <img>+<video> / <YouTubeEmbed>+PDF page shapes, not the current title+description-only front matter. Treat as a reference implementation for a future video-embedding pass, not a script to run as-is.
12. Naming Conventions
| Artifact | Convention | Example | |
|---|---|---|---|
| Module directory | PascalCase_Platform | App_CloudRun, Django_GKE | |
| Module doc file | Same as directory + .md, flat (no subdir) | App_CloudRun.md | |
Sidebar label (sidebars.ts, not front matter) | Spaces instead of underscores | App CloudRun | |
Heading # | Spaces, no underscores | # App CloudRun — Lab Guide | |
| GCS infographic image | <Module_Name>.png (module image) / <Guide_Name>.png (guide/design image) / <cert>_section<N>.png (cert image) | Activepieces_CloudRun.png, Admin_Guide.png, pcne_section1.png | |
| Image case sensitivity | Match the object's actual casing exactly — GCS is case-sensitive | see above |
Historical fix (commit 3611277): Corrected WordPress_ to Wordpress_ in PDF file name references throughout the site to match actual GCS object names. (PDF links themselves are no longer used anywhere in the site — see §7.2 — this fix predates their removal and is kept only as a reminder that GCS casing bugs are a recurring failure mode.)
13. Git Workflow
Per CLAUDE.md: commit directly to main with descriptive messages referencing what changed and why, then push straight to main. main has GitHub branch-protection rules configured, but this repo's actual practice is to push directly to it — GitHub reports "Bypassed rule violations" (with the specific rule listed, e.g. "Changes must be made through a pull request") on each such push rather than rejecting it. Do not detour through a claude/... feature branch + PR unless the user explicitly asks for one.
14. Automated Verification
Two Playwright scripts verify the site after changes:
| Script | Purpose | |
|---|---|---|
verify_changes.py | Headless browser checks: page titles, sidebar visibility, screenshots | |
verify_final.py | Same but records a video to ~/verification/video_final/ |
Run against a local dev server (yarn start) before pushing significant structural changes.
15. Quick Reference: Common Fixes
| Issue | Root Cause | Fix | |
|---|---|---|---|
| MDX build error: ReferenceError | {VAR} or ${VAR} in prose treated as JSX | Escape: \${VAR} or use backticks | |
| MDX build error: unexpected tag | <200ms or <external-ip> in prose | Escape: \< or wrap in backticks | |
| Broken link at build time | Relative path points to directory or missing file | Fix path; link to file, not directory | |
| ASCII box looks broken | Lines end at wrong column | Count characters; pad/trim to match right │ column | |
| Sidebar shows wrong label | Wrong label in sidebars.ts (front matter sidebar_label is unused — §10.1) | Fix the label in sidebars.ts directly | |
| Infographic image 404 | Case mismatch with GCS object name, or wrong bucket prefix (design docs use guides/, not design/ — §7.1) | Match GCS object name and prefix exactly | |
| Infographic image missing on a lab page | Expected — labs/ pages don't carry this image yet (§7.1) | Not a bug; a future addition if the user asks for it | |
| Wrong section in nav | Module in wrong sidebar category | Move the entry in sidebars.ts | |
| Module name with underscores in heading | Heading not normalised to spaces per §6.3 | Replace _ with space in # heading and table cells | |
New <img> insertion missing after a docs sync | Image tag was added directly in docs/ instead of the source repo | Port the insertion into the canonical source repo too (§7.1, §11.2) so it survives the next sync |