Update SOPs: consolidate index, clean client data, set Imagen as default

- README: rewrite index to reflect actual files (STACK/CONTENT/OPTIMIZATION);
  remove 15 dead links to old numbered SOPs; add subdirectory table; update
  image gen to Google Imagen as default
- STACK: fix wp-divi-pipeline script paths; genericize vibrantyou/domain
  examples; strip pre-existing em dashes throughout
- CONTENT: update image generation default to Google Imagen API with allotted quota
- image-gen-workflow: remove client-specific cobhamtech data; generalize
  brand palette step; update date
- wp-divi-pipeline-to-am-stack: remove vibrantyou.yoga client data block;
  fix Related SOPs links to current files
This commit is contained in:
2026-06-09 18:54:57 +02:00
parent 94f7a1f72a
commit 5eb4426d30
7 changed files with 190 additions and 251 deletions
+1 -1
View File
@@ -82,7 +82,7 @@ Hero images: unique per page, named hero-{page-slug}.webp.
## Image Generation ## Image Generation
Preferred source: local ComfyUI (FLUX.1 Schnell) or Google Imagen API. Default source: Google Imagen API (imagen-4.0-generate-001) with allotted quota per project.
Every generated image passes a vision validation check for people/faces before being saved. Every generated image passes a vision validation check for people/faces before being saved.
+46 -60
View File
@@ -1,95 +1,81 @@
# Arising Media Web Design SOPs # Arising Media Web Design SOPs
Standard operating procedures for building, migrating, and deploying websites Standard operating procedures for building, migrating, and deploying websites the Arising Media way. Reference `stack-selector.json` FIRST to pick the correct stack before touching any other SOP.
the Arising Media way. Reference `stack-selector.json` FIRST to pick the correct
stack before touching any other SOP.
## Stack selector (read this first) ## Stack selector (read this first)
`stack-selector.json` machine-readable + human-readable decision guide. `stack-selector.json`: machine-readable + human-readable decision guide. Covers all three stacks, approved colors, section types, DB guidance, hot-copy commands.
Covers all three stacks, approved colors, section types, DB guidance, hot-copy commands.
## Two primary stacks (2026-05-21 standard) ## Primary SOP files
### Stack A — PHP Router + SQLite (50+ page sites) | File | Covers |
**Reference: `arisingmedia.us`** — 10,000+ pages, single router, SQLite content DB. |------|--------|
Edit one template → all pages in that class update instantly. No find-and-replace. | `STACK.md` | Architecture, project structure, build pipeline, WP migration, Docker/nginx deployment, cPanel deployment, DNS/email, form handling, PHP app stack |
Architecture diagram: `arisingmedia.us/.planning/RENDER_ARCHITECTURE.html` | `CONTENT.md` | Writing standards, tone by sector, healthcare credential rules, copy structure, image standards, image generation, prompt engineering |
| `OPTIMIZATION.md` | Mobile responsive, breakpoints, SEO meta, schema.org, robots.txt, sitemap, testing/verification, performance standards |
## Three stacks (2026-05-21 standard)
### Stack A: PHP Router + SQLite (50+ page sites)
Reference: `arisingmedia.us`. 10,000+ pages, single router, SQLite content DB.
Edit one template and all pages in that class update instantly.
Build standard: `arisingmedia.us/.planning/WEBSITE_BUILD_STANDARD.md` Build standard: `arisingmedia.us/.planning/WEBSITE_BUILD_STANDARD.md`
SOP: `15-php-router-sqlite-standard.md` Stack A uses raw docker commands only. No docker compose.
### Stack B Static HTML (fewer than 50 pages) ### Stack B: Static HTML (fewer than 50 pages)
**Reference: `lahrcarpetcleaning.com`** — universal Docker + cPanel deployment. Reference: `lahrcarpetcleaning.com`. Universal Docker + cPanel deployment.
Every page is an HTML file on disk. Simple, portable, no server-side requirements. Every page is an HTML file on disk. Simple, portable, no server-side requirements.
SOP: `01-project-structure.md`, `03-build-pipeline.md`, `08-deployment-docker.md`
### Stack C PHP App (file processing, auth, payments) ### Stack C: PHP App (file processing, auth, payments)
**Reference: `quickconvert.us`** Reference: `quickconvert.us`
SOP: `14-php-app-stack.md` See STACK.md PHP App Stack section.
## Index ## Subdirectories
1. [00-stack-philosophy.md](00-stack-philosophy.md) — Why static HTML + vanilla JS + Python tooling, what we never use | Directory | Purpose |
2. [01-project-structure.md](01-project-structure.md) — Folder layout: flat HTML (Docker) vs directory-style (cPanel). Lahr reference structure. |-----------|---------|
3. [02-wordpress-to-html-migration.md](02-wordpress-to-html-migration.md) — The migration playbook | `local-image-generation/` | Full local pipeline: ComfyUI setup, FLUX.1 Schnell images, Wan 2.2 video, prompt guide, quality levers |
4. [03-build-pipeline.md](03-build-pipeline.md) — JSON data + HTML template + Python build script | `image-gen-workflow/` | Cloud API workflow (Google Imagen 4). Paid client budgets only. |
5. [04-content-rules.md](04-content-rules.md) — Writing standards (no em-dashes, no fake metrics) | `wp-divi-pipeline-to-am-stack/` | End-to-end pipeline: .wpress archive to Stack A deployment |
6. [05-mobile-responsive.md](05-mobile-responsive.md) — Breakpoints, mobile nav, viewport testing | `tools/` | Shared scripts (verify-protection.sh, etc.) |
7. [06-seo-meta.md](06-seo-meta.md) — Title, meta, canonical, keywords, schema, og:, robots.txt, sitemap. Includes lahr examples. | `build/` | Build utilities |
8. [07-form-handling.md](07-form-handling.md) — Resend API + Python stdlib service pattern
9. [08-deployment-docker.md](08-deployment-docker.md) — Universal deployment: Docker+nginx (Path A) AND cPanel+Apache (Path B). Universal project checklist.
10. [09-domain-email-dns.md](09-domain-email-dns.md) — Resend domain verification, SPF/DKIM/DMARC
11. [10-testing-verification.md](10-testing-verification.md) — Playwright checks before declaring done
12. [11-healthcare-regulated-credentials.md](11-healthcare-regulated-credentials.md) — Licensure copy compliance for healthcare clients (MHC-LP, LMHC, supervision disclosure)
13. [12-image-assets.md](12-image-assets.md) — WebP requirement, sizing targets, local generation (FLUX.1 Schnell), convert-to-webp.py
14. [13-reference-site-visual-audit.md](13-reference-site-visual-audit.md) — Playwright Firefox scroll-capture + frame-strip + pin-transition annotation. Output skeleton + schema mapping for any client-cited reference URL.
15. [14-php-app-stack.md](14-php-app-stack.md) — When to use PHP + Docker instead of static HTML. Server-side processing, encryption, auth patterns.
## Image generation workflow ## Image generation
See [local-image-generation/](local-image-generation/) for the full local pipeline: Default: Google Imagen API (imagen-4.0-generate-001) with allotted quota per project.
- [01-comfyui-setup.md](local-image-generation/01-comfyui-setup.md) — ComfyUI install, venv, GPU notes See `image-gen-workflow/` for the workflow, model selection, and prompt patterns.
- [02-flux-images.md](local-image-generation/02-flux-images.md) — FLUX.1 Schnell image pipeline
- [03-wan-video.md](local-image-generation/03-wan-video.md) — Wan 2.2 image-to-video pipeline
- [04-prompt-guide.md](local-image-generation/04-prompt-guide.md) — Lens, angle, depth-of-field prompt patterns
- [05-quality-levers.md](local-image-generation/05-quality-levers.md) — Prompt, steps, model size: decision matrix for fixing output quality
Default stack (free, no API cost): FLUX.1 Schnell GGUF (images) + Wan 2.2 GGUF (video) via ComfyUI All output: `.webp` only in production.
All output: `.webp` only — see `12-image-assets.md`
Cloud APIs (Google Veo, Imagen 4, Gemini video): only when client has explicit paid media budget.
Local generation is the default for all Arising Media client projects.
## Universal project file checklist ## Universal project file checklist
Every project must include ALL of these before first commit: Every project must include ALL of these before first deploy:
``` ```
Dockerfile Docker/VPS nginx web container Dockerfile Docker/VPS: nginx web container
docker-compose.yml Docker/VPS — service orchestration docker-compose.yml Stack B/C only. Stack A uses raw docker commands.
nginx.conf Docker/VPS routing, security headers, gzip nginx.conf Docker/VPS: routing, security headers, gzip
.htaccess cPanel/Apache clean URLs, deny sensitive files .htaccess cPanel/Apache: clean URLs, deny sensitive files
.cpanel.yml cPanel Git copy files to webroot on deploy .cpanel.yml cPanel Git: copy files to webroot on deploy
.dockerignore keeps secrets and tools out of Docker image .dockerignore keeps secrets and tools out of Docker image
.gitignore keeps .env and secrets out of git .gitignore keeps .env and secrets out of git
robots.txt both paths robots.txt both paths
sitemap.xml both paths sitemap.xml both paths
llms.txt both paths AI crawler documentation (see 06-seo-meta.md) llms.txt both paths. AI crawler documentation (see OPTIMIZATION.md)
404.html both paths 404.html both paths
500.html both paths 500.html both paths
``` ```
## Reference projects ## Reference projects
- `lahrcarpetcleaning.com` **primary reference**: universal deployment, directory-style URLs, WebP images, cPanel Git, mobile nav, FLUX image generation - `lahrcarpetcleaning.com`: primary reference. Universal deployment, directory-style URLs, WebP images, cPanel Git, mobile nav, FLUX image generation.
- `floorithardwoodfloors.com` — first WP-to-static migration with Docker stack - `arisingmedia.us`: Stack A reference. PHP router, SQLite, 10,000+ pages.
- `backforge.nl` — simpler static site, no form API - `quickconvert.us`: Stack C reference. PHP app, Stripe, encryption, auth.
## When to deviate ## When to deviate
These SOPs are the default. Deviate only when: These SOPs are the default. Deviate only when:
- The client explicitly requires a different stack (CMS-managed editing, e-commerce with Shopify/Woo, etc.) - The client explicitly requires a different stack (CMS-managed editing, e-commerce, etc.)
- A required feature literally cannot be done with this stack - A required feature cannot be done with this stack
Document the deviation in the project's own `README.md` so future maintainers Document the deviation in the project's own `README.md`.
know what is and is not standard.
+95 -95
View File
@@ -1,4 +1,4 @@
# STACK Architecture, Deployment, and Build Pipeline # STACK: Architecture, Deployment, and Build Pipeline
Author: Andre Cobham / Arising Media Author: Andre Cobham / Arising Media
Updated: 2026-06-09 Updated: 2026-06-09
@@ -6,19 +6,19 @@ Updated: 2026-06-09
Two primary stacks. Pick based on page count and update frequency. Two primary stacks. Pick based on page count and update frequency.
### Stack A PHP Router + SQLite (50+ pages, standard as of 2026-05-21) ### Stack A: PHP Router + SQLite (50+ pages, standard as of 2026-05-21)
- **PHP Router** `router.php` dispatches every content URL to the correct PHP template. Edit one template = entire page class updates on next request. No find-and-replace. No file edits. - **PHP Router**: `router.php` dispatches every content URL to the correct PHP template. Edit one template = entire page class updates on next request. No find-and-replace. No file edits.
- **SQLite** single-file content DB. `pages.sqlite` holds all page content (title, meta, sections JSON, schema). 10,000 rows = 5MB. Sub-millisecond reads. No server process. - **SQLite**: single-file content DB. `pages.sqlite` holds all page content (title, meta, sections JSON, schema). 10,000 rows = 5MB. Sub-millisecond reads. No server process.
- **Vanilla JavaScript** no frameworks. `fetch`, `IntersectionObserver`, `querySelector` - **Vanilla JavaScript**: no frameworks. `fetch`, `IntersectionObserver`, `querySelector`
- **Plain CSS** `tokens.css` (design tokens) + `main.css` (components). No Sass, no Tailwind - **Plain CSS**: `tokens.css` (design tokens) + `main.css` (components). No Sass, no Tailwind
- **Docker + nginx** nginx routes `/assets/*` directly; all content URLs → PHP-FPM → router.php - **Docker + nginx**: nginx routes `/assets/*` directly; all content URLs → PHP-FPM → router.php
- **Resend** transactional email via `/api/contact.php` - **Resend**: transactional email via `/api/contact.php`
- **Reference:** `arisingmedia.us` 10,000+ pages - **Reference:** `arisingmedia.us`: 10,000+ pages
### Stack B Static HTML (fewer than 50 pages) ### Stack B: Static HTML (fewer than 50 pages)
- **Static HTML** every page is a `.html` file on disk - **Static HTML**: every page is a `.html` file on disk
- Same JS, CSS, Docker, nginx, Resend as Stack A - Same JS, CSS, Docker, nginx, Resend as Stack A
- Python 3 stdlib for build scripts (no pip) - Python 3 stdlib for build scripts (no pip)
- **Reference:** `lahrcarpetcleaning.com` - **Reference:** `lahrcarpetcleaning.com`
@@ -36,11 +36,11 @@ Two primary stacks. Pick based on page count and update frequency.
### Why This Stack ### Why This Stack
1. **Performance** a static HTML page with vanilla JS loads in <100ms with no parse cost from frameworks 1. **Performance**: a static HTML page with vanilla JS loads in <100ms with no parse cost from frameworks
2. **Longevity** no dependency rot. A site we build today still works in 10 years with no maintenance 2. **Longevity**: no dependency rot. A site we build today still works in 10 years with no maintenance
3. **Security** no `npm audit` warnings, no supply-chain attack vectors, no transitive deps to patch 3. **Security**: no `npm audit` warnings, no supply-chain attack vectors, no transitive deps to patch
4. **Auditability** every line on the site is something we wrote and can read in plain text 4. **Auditability**: every line on the site is something we wrote and can read in plain text
5. **Hosting** a static folder + tiny Python container fits in the smallest VM tier any provider sells 5. **Hosting**: a static folder + tiny Python container fits in the smallest VM tier any provider sells
### When to Add a Server-Side Service ### When to Add a Server-Side Service
@@ -121,7 +121,7 @@ Contains ONLY what's needed to run `docker compose up`.
├── api/ # form-submit Python service (if used) ├── api/ # form-submit Python service (if used)
│ ├── server.py │ ├── server.py
│ ├── Dockerfile │ ├── Dockerfile
│ ├── .env # gitignored Resend key, etc. │ ├── .env # gitignored: Resend key, etc.
│ └── .env.example │ └── .env.example
├── Dockerfile # nginx web container ├── Dockerfile # nginx web container
├── nginx.conf ├── nginx.conf
@@ -145,7 +145,7 @@ notes, raw assets). This is the dev/maintenance copy. NOT what gets deployed.
Build scripts, JSON data, and notes go into `.planning/` to keep root clean and Build scripts, JSON data, and notes go into `.planning/` to keep root clean and
prevent accidental web exposure. prevent accidental web exposure.
### URL Structure Two Valid Patterns ### URL Structure: Two Valid Patterns
#### Pattern A: Flat HTML (default for Docker/nginx projects) #### Pattern A: Flat HTML (default for Docker/nginx projects)
@@ -156,7 +156,7 @@ Why flat:
- One file = one page, no `/index.html` confusion - One file = one page, no `/index.html` confusion
- Easier sitemap generation - Easier sitemap generation
- `<a href>` links are unambiguous - `<a href>` links are unambiguous
- Crawl budget benefit Google indexes one URL per page, not two - Crawl budget benefit: Google indexes one URL per page, not two
#### Pattern B: Directory-style (default for cPanel/Apache projects) #### Pattern B: Directory-style (default for cPanel/Apache projects)
@@ -242,9 +242,9 @@ directly. Build scripts are for repetition, not for everything.
Three files per template family: Three files per template family:
1. **`data/{thing}.json`** array of objects, one per page 1. **`data/{thing}.json`**: array of objects, one per page
2. **`{thing}/_template.html`** HTML with `{{placeholder}}` markers 2. **`{thing}/_template.html`**: HTML with `{{placeholder}}` markers
3. **`build_{thing}.py`** stdlib Python, stamps template with data 3. **`build_{thing}.py`**: stdlib Python, stamps template with data
#### Example: locations.json #### Example: locations.json
@@ -327,7 +327,7 @@ if __name__ == "__main__":
### Rules ### Rules
1. **Source of truth is JSON, not HTML.** When content needs to change, edit the 1. **Source of truth is JSON, not HTML.** When content needs to change, edit the
JSON and re-run the build script. Never hand-edit a generated `.html` file JSON and re-run the build script. Never hand-edit a generated `.html` file :
the next build will overwrite your changes. the next build will overwrite your changes.
2. **Generated files land in the same folder as their template.** Do not nest 2. **Generated files land in the same folder as their template.** Do not nest
@@ -345,7 +345,7 @@ if __name__ == "__main__":
5. **Build is idempotent.** Running it twice produces identical files. 5. **Build is idempotent.** Running it twice produces identical files.
### Stamping Rules Escaping ### Stamping Rules: Escaping
When a JSON value gets stamped into an HTML attribute or `<title>`, special When a JSON value gets stamped into an HTML attribute or `<title>`, special
characters can break the page. Use these rules: characters can break the page. Use these rules:
@@ -376,20 +376,20 @@ After build, sync the rendered files to deployment.
The playbook for migrating a WordPress (Divi, Elementor, classic, whatever) site The playbook for migrating a WordPress (Divi, Elementor, classic, whatever) site
to vanilla static HTML. to vanilla static HTML.
### Phase 1 Capture Source ### Phase 1: Capture Source
Before touching anything, capture the current site so nothing is lost. Before touching anything, capture the current site so nothing is lost.
1. **Database dump** `wp db export ${domain}.sql --add-drop-table` 1. **Database dump**: `wp db export ${domain}.sql --add-drop-table`
2. **Wp-content snapshot** tar the entire `wp-content/` (themes, plugins, uploads) 2. **Wp-content snapshot**: tar the entire `wp-content/` (themes, plugins, uploads)
3. **Crawl the live site** use `wget --mirror --convert-links --adjust-extension --page-requisites --no-parent https://{domain}` to capture rendered HTML + all assets 3. **Crawl the live site**: use `wget --mirror --convert-links --adjust-extension --page-requisites --no-parent https://{domain}` to capture rendered HTML + all assets
4. **Inventory pages** list every URL returning 200 (use the sitemap if it has one) 4. **Inventory pages**: list every URL returning 200 (use the sitemap if it has one)
5. **Inventory forms** note every Gravity Form / Contact Form 7 / etc. field-by-field 5. **Inventory forms**: note every Gravity Form / Contact Form 7 / etc. field-by-field
6. **Inventory dynamic features** search, comments, members, anything truly dynamic 6. **Inventory dynamic features**: search, comments, members, anything truly dynamic
Save all of this in the project's `.planning/` folder. Save all of this in the project's `.planning/` folder.
### Phase 2 Decide What to Keep ### Phase 2: Decide What to Keep
Re-design pass. Most WP sites have: Re-design pass. Most WP sites have:
- Bloated copy → cut by 30-50% - Bloated copy → cut by 30-50%
@@ -400,7 +400,7 @@ Re-design pass. Most WP sites have:
Show the client a wireframe of the simplified structure before building anything. Show the client a wireframe of the simplified structure before building anything.
### Phase 3 Information Architecture ### Phase 3: Information Architecture
Standard structure for a small business: Standard structure for a small business:
@@ -419,19 +419,19 @@ Standard structure for a small business:
For each location and each service: one flat `.html` page generated from JSON + For each location and each service: one flat `.html` page generated from JSON +
template. template.
### Phase 4 Build ### Phase 4: Build
1. Set up source folder per `01-project-structure.md` 1. Set up source folder per the Project Structure section in STACK.md
2. Write `assets/css/main.css` (variables, reset, typography, layout) 2. Write `assets/css/main.css` (variables, reset, typography, layout)
3. Write `assets/css/components.css` (header, footer, hero, cards, forms) 3. Write `assets/css/components.css` (header, footer, hero, cards, forms)
4. Write `components/header.html` and `components/footer.html` 4. Write `components/header.html` and `components/footer.html`
5. Write `assets/js/components.js` (fetch + inject header/footer) 5. Write `assets/js/components.js` (fetch + inject header/footer)
6. Write `assets/js/main.js` (scroll animations, anything page-wide) 6. Write `assets/js/main.js` (scroll animations, anything page-wide)
7. Build `index.html` first this is the design system in working form 7. Build `index.html` first: this is the design system in working form
8. Generate location and service detail pages from JSON 8. Generate location and service detail pages from JSON
9. Build remaining pages: about, contact, reviews, blog index 9. Build remaining pages: about, contact, reviews, blog index
### Phase 5 Forms ### Phase 5: Forms
If the WP site had Gravity Forms or similar, build a vanilla replacement: If the WP site had Gravity Forms or similar, build a vanilla replacement:
- HTML form in `contact/index.html` (and inline on service/location pages if needed) - HTML form in `contact/index.html` (and inline on service/location pages if needed)
@@ -439,7 +439,7 @@ If the WP site had Gravity Forms or similar, build a vanilla replacement:
- POST to `/api/estimate` (or similar) handled by Python stdlib service - POST to `/api/estimate` (or similar) handled by Python stdlib service
- Server-side validation, reCAPTCHA verification, send via Resend - Server-side validation, reCAPTCHA verification, send via Resend
### Phase 6 SEO Parity ### Phase 6: SEO Parity
Before launch, every old URL must either: Before launch, every old URL must either:
- Have a matching new URL with the same or better content, OR - Have a matching new URL with the same or better content, OR
@@ -461,7 +461,7 @@ Per-page parity checklist:
- Image alt text preserved or improved - Image alt text preserved or improved
- Schema.org JSON-LD added (`LocalBusiness`, `Service`, `BreadcrumbList`) - Schema.org JSON-LD added (`LocalBusiness`, `Service`, `BreadcrumbList`)
### Phase 7 Switch DNS / Cutover ### Phase 7: Switch DNS / Cutover
1. Deploy the static site to a separate URL first (`new.{domain}`) for client review 1. Deploy the static site to a separate URL first (`new.{domain}`) for client review
2. Once approved, point production DNS to the new container 2. Once approved, point production DNS to the new container
@@ -469,16 +469,16 @@ Per-page parity checklist:
4. Submit new sitemap to Google Search Console 4. Submit new sitemap to Google Search Console
5. Use Search Console URL inspection on 5-10 key pages to confirm indexing 5. Use Search Console URL inspection on 5-10 key pages to confirm indexing
### Phase 8 Post-Launch ### Phase 8: Post-Launch
- Monitor Search Console for crawl errors / 404s, fix in nginx as redirects - Monitor Search Console for crawl errors / 404s, fix in nginx as redirects
- Monitor form submissions first real lead through the new form is the - Monitor form submissions: first real lead through the new form is the
ultimate "it works" check ultimate "it works" check
- Decommission WP only after 30 days of clean operation - Decommission WP only after 30 days of clean operation
### What NOT to Do ### What NOT to Do
- Do not run a "headless WordPress" or "WordPress as API" that defeats the - Do not run a "headless WordPress" or "WordPress as API": that defeats the
whole point. Static means static. whole point. Static means static.
- Do not use a static-site-generator tool (Hugo, 11ty, Jekyll, Astro, Next.js - Do not use a static-site-generator tool (Hugo, 11ty, Jekyll, Astro, Next.js
static export). We hand-write HTML and use small Python build scripts only static export). We hand-write HTML and use small Python build scripts only
@@ -506,7 +506,7 @@ Takes a single `.wpress` archive (All-in-One WP Migration backup) and produces:
The goal is NOT a 1:1 copy. The goal is: The goal is NOT a 1:1 copy. The goal is:
1. Preserve all content, SEO equity, and brand identity 1. Preserve all content, SEO equity, and brand identity
2. ENHANCE the design cleaner, faster, more modern 2. ENHANCE the design: cleaner, faster, more modern
3. Remove all WordPress / Divi bloat (plugin CSS, shortcode residue, 300KB JS bundles) 3. Remove all WordPress / Divi bloat (plugin CSS, shortcode residue, 300KB JS bundles)
4. Produce a site that loads in <2s on mobile and scores 95+ on Lighthouse 4. Produce a site that loads in <2s on mobile and scores 95+ on Lighthouse
@@ -551,14 +551,14 @@ All scripts live in `.am-webdesign-sops/wp-divi-pipeline/scripts/`.
| `extract_design.py` | 4 | Pull Divi theme options → design-system.json | | `extract_design.py` | 4 | Pull Divi theme options → design-system.json |
| `extract_media.py` | 5 | Catalog uploads/, emit media-manifest.json | | `extract_media.py` | 5 | Catalog uploads/, emit media-manifest.json |
| `convert_images.py` | 5 | Batch convert images → WebP | | `convert_images.py` | 5 | Batch convert images → WebP |
| `run_pipeline.sh` | 0-7 | Master script runs all phases in order | | `run_pipeline.sh` | 0-7 | Master script: runs all phases in order |
### Per-Project Working Directory ### Per-Project Working Directory
``` ```
{domain}/ {domain}/
└── .planning/ └── .planning/
├── vibrantyou-yoga-YYYYMMDD-*.wpress ← source archive (never modify) ├── {domain}-YYYYMMDD-*.wpress ← source archive (never modify)
├── wpress-extract/ ← Phase 1 output (gitignored) ├── wpress-extract/ ← Phase 1 output (gitignored)
│ ├── package.json ← archive metadata │ ├── package.json ← archive metadata
│ ├── database.sql ← MySQL dump │ ├── database.sql ← MySQL dump
@@ -593,7 +593,7 @@ The archive ends when a header of all null bytes is encountered, or EOF.
Extraction script: Extraction script:
```bash ```bash
python3 /home/sirdrez/arisingmedia-websites/.am-webdesign-sops/wp-divi-pipeline/scripts/extract_wpress.py \ python3 /home/sirdrez/arisingmedia-websites/.am-webdesign-sops/wp-divi-pipeline-to-am-stack/scripts/extract_wpress.py \
/home/sirdrez/arisingmedia-websites/{domain}/.planning/{file}.wpress \ /home/sirdrez/arisingmedia-websites/{domain}/.planning/{file}.wpress \
/home/sirdrez/arisingmedia-websites/{domain}/.planning/wpress-extract/ /home/sirdrez/arisingmedia-websites/{domain}/.planning/wpress-extract/
``` ```
@@ -610,9 +610,9 @@ python3 /home/sirdrez/arisingmedia-websites/.am-webdesign-sops/wp-divi-pipeline/
``` ```
Outputs three files into `.planning/data/`: Outputs three files into `.planning/data/`:
- `pages.json` all published pages/posts with content and SEO meta - `pages.json`: all published pages/posts with content and SEO meta
- `design-system.json` colors, fonts, Divi settings - `design-system.json`: colors, fonts, Divi settings
- `site-info.json` domain, plugin list, WP version, Divi version - `site-info.json`: domain, plugin list, WP version, Divi version
### Divi 5 Content Extraction ### Divi 5 Content Extraction
@@ -654,7 +654,7 @@ Strip Divi class/attribute noise using `clean_divi_html()` from `divi_to_html.py
from divi_to_html import clean_divi_html, rewrite_internal_links from divi_to_html import clean_divi_html, rewrite_internal_links
cleaned = clean_divi_html(raw_html) cleaned = clean_divi_html(raw_html)
cleaned = rewrite_internal_links(cleaned, staging_hosts=("vibrantyou.yoga",)) cleaned = rewrite_internal_links(cleaned, staging_hosts=("{domain}",))
``` ```
### Design System Extraction ### Design System Extraction
@@ -712,13 +712,13 @@ full 5-step scale around the primary hue:
Map extracted Divi content into AM HTML templates. Map extracted Divi content into AM HTML templates.
Build order: Build order:
1. `src/assets/css/main.css` design tokens, reset, typography, layout grid 1. `src/assets/css/main.css`: design tokens, reset, typography, layout grid
2. `src/assets/css/components.css` header, footer, hero, cards, forms, nav 2. `src/assets/css/components.css`: header, footer, hero, cards, forms, nav
3. `src/components/header.html` navigation 3. `src/components/header.html`: navigation
4. `src/components/footer.html` footer links, contact info 4. `src/components/footer.html`: footer links, contact info
5. `src/assets/js/components.js` fetch + inject header/footer 5. `src/assets/js/components.js`: fetch + inject header/footer
6. `src/assets/js/main.js` scroll animations, intersection observer 6. `src/assets/js/main.js`: scroll animations, intersection observer
7. `src/index.html` home page (this IS the design system in working form) 7. `src/index.html`: home page (this IS the design system in working form)
8. Remaining pages: about, classes, contact, blog 8. Remaining pages: about, classes, contact, blog
9. `src/robots.txt`, `src/sitemap.xml`, `src/404.html`, `src/500.html` 9. `src/robots.txt`, `src/sitemap.xml`, `src/404.html`, `src/500.html`
@@ -790,7 +790,7 @@ Priority order for SEO fields:
2. `post_title` with AM format appended: `{Title} | {Brand Name}` 2. `post_title` with AM format appended: `{Title} | {Brand Name}`
3. Never leave title as the raw WP default 3. Never leave title as the raw WP default
Rank Math title templates use `%` tokens strip them and rebuild: Rank Math title templates use `%` tokens: strip them and rebuild:
```python ```python
import re import re
@@ -837,7 +837,7 @@ grep -r "et_pb_\|wp:divi" $SITE --include="*.html"
### Run Order (Complete Execution Sequence) ### Run Order (Complete Execution Sequence)
```bash ```bash
export DOMAIN="vibrantyou.yoga" export DOMAIN="{domain}"
export PROJECT="/home/sirdrez/arisingmedia-websites/$DOMAIN" export PROJECT="/home/sirdrez/arisingmedia-websites/$DOMAIN"
export SOPS="/home/sirdrez/arisingmedia-websites/.am-webdesign-sops" export SOPS="/home/sirdrez/arisingmedia-websites/.am-webdesign-sops"
export WPRESS=$(ls $PROJECT/.planning/*.wpress | head -1) export WPRESS=$(ls $PROJECT/.planning/*.wpress | head -1)
@@ -854,7 +854,7 @@ python3 $SOPS/wp-divi-pipeline/scripts/analyze_db.py "$PROJECT/.planning/wpress-
# Phase 3: Content extraction (Divi 5 example) # Phase 3: Content extraction (Divi 5 example)
python3 $SOPS/wp-divi-pipeline/scripts/extract_divi5.py "$PROJECT/.planning/data/pages.json" "$PROJECT/.planning/data/content/" python3 $SOPS/wp-divi-pipeline/scripts/extract_divi5.py "$PROJECT/.planning/data/pages.json" "$PROJECT/.planning/data/content/"
# Phase 4: Design system (manual read design-system.json, write main.css) # Phase 4: Design system (manual: read design-system.json, write main.css)
# Phase 5: Media migration # Phase 5: Media migration
find $PROJECT/.planning/wpress-extract/uploads -type f \( -name "*.jpg" -o -name "*.png" \) | \ find $PROJECT/.planning/wpress-extract/uploads -type f \( -name "*.jpg" -o -name "*.png" \) | \
@@ -870,7 +870,7 @@ for img in *.jpg *.png; do
cwebp -q 82 "$img" -o "${img%.*}.webp" && rm "$img" cwebp -q 82 "$img" -o "${img%.*}.webp" && rm "$img"
done done
# Phase 6: Build HTML (manual per 05-content-migration.md) # Phase 6: Build HTML (manual: per 05-content-migration.md)
# Phase 7: SEO audit # Phase 7: SEO audit
cd $PROJECT/src cd $PROJECT/src
@@ -930,17 +930,17 @@ Port assignments are unique per project. Track in
### Dockerfile (nginx web container) ### Dockerfile (nginx web container)
CRITICAL the Dockerfile must explicitly list which folders to copy. Never use CRITICAL: the Dockerfile must explicitly list which folders to copy. Never use
`COPY . /usr/share/nginx/html/` because that copies `.env`, `Dockerfile`, `COPY . /usr/share/nginx/html/` because that copies `.env`, `Dockerfile`,
build scripts, etc. into the web root where they become URL-accessible. build scripts, etc. into the web root where they become URL-accessible.
```dockerfile ```dockerfile
FROM nginx:alpine FROM nginx:alpine
# nginx config server-only, never served as a static file # nginx config: server-only, never served as a static file
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
# Public website only explicit list, no wildcards # Public website only: explicit list, no wildcards
COPY index.html /usr/share/nginx/html/ COPY index.html /usr/share/nginx/html/
COPY assets /usr/share/nginx/html/assets/ COPY assets /usr/share/nginx/html/assets/
COPY components /usr/share/nginx/html/components/ COPY components /usr/share/nginx/html/components/
@@ -975,7 +975,7 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
# Defense in depth deny dotfiles, configs, scripts, source files # Defense in depth: deny dotfiles, configs, scripts, source files
location ~ /\. { location ~ /\. {
deny all; deny all;
return 404; return 404;
@@ -989,7 +989,7 @@ server {
return 404; return 404;
} }
# API proxy strip /api/ prefix, forward to Python service # API proxy: strip /api/ prefix, forward to Python service
location /api/ { location /api/ {
proxy_pass http://api:3001/; proxy_pass http://api:3001/;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -1000,7 +1000,7 @@ server {
proxy_connect_timeout 5s; proxy_connect_timeout 5s;
} }
# Flat HTML routing /locations/buffalo serves /locations/buffalo.html # Flat HTML routing: /locations/buffalo serves /locations/buffalo.html
location / { location / {
try_files $uri $uri/ $uri.html =404; try_files $uri $uri/ $uri.html =404;
} }
@@ -1100,10 +1100,10 @@ scripts, `.git/`, etc.) and fails if any returns 200.
``` ```
Exit codes: Exit codes:
- `0` PASS every sensitive path 404, every required path reachable. - `0` PASS: every sensitive path 404, every required path reachable.
- `0` PASS (with warnings) protection clean but `/robots.txt` or - `0` PASS (with warnings): protection clean but `/robots.txt` or
`/sitemap.xml` missing (content gap, not a leak). `/sitemap.xml` missing (content gap, not a leak).
- `1` FAIL at least one sensitive path returned 200, or `/` is unreachable. - `1` FAIL: at least one sensitive path returned 200, or `/` is unreachable.
Run it manually after every `docker compose up -d --build`. Wire it into CI Run it manually after every `docker compose up -d --build`. Wire it into CI
once the site has a remote pipeline. Treat a FAIL as a deploy rollback. once the site has a remote pipeline. Treat a FAIL as a deploy rollback.
@@ -1132,7 +1132,7 @@ compose project:
docker stop {container-name} docker stop {container-name}
docker rm {container-name} docker rm {container-name}
# Now bring up from the renamed folder clean start # Now bring up from the renamed folder: clean start
docker compose -f /path/to/renamed-folder/docker-compose.yml up -d docker compose -f /path/to/renamed-folder/docker-compose.yml up -d
``` ```
@@ -1152,7 +1152,7 @@ WHM, Bluehost, HostGator, SiteGround, etc.) instead of a VPS running Docker.
### Key Rule: Repo Path ≠ Webroot ### Key Rule: Repo Path ≠ Webroot
cPanel Git requires an EMPTY directory as the repository path. The webroot cPanel Git requires an EMPTY directory as the repository path. The webroot
(`public_html/{domain}/`) is never the repo path cPanel rejects it if it (`public_html/{domain}/`) is never the repo path: cPanel rejects it if it
already contains files. already contains files.
``` ```
@@ -1165,7 +1165,7 @@ Deploy target (webroot): /home/{username}/public_html/{domain}/
1. cPanel → Git Version Control → Create Repository 1. cPanel → Git Version Control → Create Repository
2. Repository Path: `/home/{username}/repositories/{domain}/` (must be empty) 2. Repository Path: `/home/{username}/repositories/{domain}/` (must be empty)
3. Clone URL: your Git remote (GitHub, Bitbucket, etc.) 3. Clone URL: your Git remote (GitHub, Bitbucket, etc.)
4. cPanel clones into the repo path never into the webroot 4. cPanel clones into the repo path: never into the webroot
### .cpanel.yml ### .cpanel.yml
@@ -1220,7 +1220,7 @@ deployment:
1. Push to the connected remote (GitHub) 1. Push to the connected remote (GitHub)
2. cPanel → Git Version Control → Manage → Pull or Deploy 2. cPanel → Git Version Control → Manage → Pull or Deploy
3. cPanel runs the `.cpanel.yml` tasks, copying files to webroot 3. cPanel runs the `.cpanel.yml` tasks, copying files to webroot
4. Apache serves from webroot automatically no nginx, no Docker 4. Apache serves from webroot automatically: no nginx, no Docker
### Apache vs nginx ### Apache vs nginx
@@ -1232,7 +1232,7 @@ Options -Indexes
RewriteEngine On RewriteEngine On
# Directory-style URLs: /services/carpet-cleaning/ → index.html inside that folder # Directory-style URLs: /services/carpet-cleaning/ → index.html inside that folder
# Apache handles this automatically with DirectoryIndex no extra rules needed # Apache handles this automatically with DirectoryIndex: no extra rules needed
# Deny sensitive files # Deny sensitive files
<FilesMatch "\.(py|yml|yaml|md|log|sh|env|conf|dockerfile)$"> <FilesMatch "\.(py|yml|yaml|md|log|sh|env|conf|dockerfile)$">
@@ -1312,7 +1312,7 @@ Lahrcarpetcleaning.com is the reference implementation for both paths.
1. https://resend.com/domains → **Add Domain** 1. https://resend.com/domains → **Add Domain**
2. Enter the domain (the one you'll send FROM, not necessarily the website domain) 2. Enter the domain (the one you'll send FROM, not necessarily the website domain)
3. Resend gives 3-4 DNS records. Add them all in Cloudflare (or whatever DNS host) 3. Resend gives 3-4 DNS records. Add them all in Cloudflare (or whatever DNS host)
4. Wait 5-15 minutes, click **Verify** in Resend all records must show green 4. Wait 5-15 minutes, click **Verify** in Resend: all records must show green
### Records Resend Provides ### Records Resend Provides
@@ -1324,7 +1324,7 @@ Lahrcarpetcleaning.com is the reference implementation for both paths.
(Resend uses Amazon SES under the hood, hence `amazonses.com` in the SPF.) (Resend uses Amazon SES under the hood, hence `amazonses.com` in the SPF.)
### DMARC REQUIRED for Inbox Placement ### DMARC: REQUIRED for Inbox Placement
Without DMARC, Gmail flags otherwise-correctly-configured email as suspicious Without DMARC, Gmail flags otherwise-correctly-configured email as suspicious
and routes it to spam. Resend doesn't auto-create this record. You must add it. and routes it to spam. Resend doesn't auto-create this record. You must add it.
@@ -1334,9 +1334,9 @@ and routes it to spam. Resend doesn't auto-create this record. You must add it.
| TXT | `_dmarc` | `v=DMARC1; p=none; rua=mailto:dev@{domain}` | DNS only | Auto | | TXT | `_dmarc` | `v=DMARC1; p=none; rua=mailto:dev@{domain}` | DNS only | Auto |
Components: Components:
- `v=DMARC1` declares a DMARC policy exists - `v=DMARC1`: declares a DMARC policy exists
- `p=none` monitor mode, doesn't reject anything yet (safe to start) - `p=none`: monitor mode, doesn't reject anything yet (safe to start)
- `rua=mailto:...` DMARC failure reports go to this inbox (review weekly) - `rua=mailto:...`: DMARC failure reports go to this inbox (review weekly)
After 30 days of clean DMARC reports with no false positives, optionally After 30 days of clean DMARC reports with no false positives, optionally
upgrade to `p=quarantine` then `p=reject`. upgrade to `p=quarantine` then `p=reject`.
@@ -1389,7 +1389,7 @@ Run this checklist:
### Cloudflare-Specific Notes ### Cloudflare-Specific Notes
The user-agent quirk Cloudflare in front of Resend's API blocks Python's default The user-agent quirk: Cloudflare in front of Resend's API blocks Python's default
`User-Agent: Python-urllib/3.x`. Always set a custom `User-Agent` in the API request headers. `User-Agent: Python-urllib/3.x`. Always set a custom `User-Agent` in the API request headers.
If the DNS provider is Cloudflare, ensure all Resend records have **proxy status: DNS only** If the DNS provider is Cloudflare, ensure all Resend records have **proxy status: DNS only**
@@ -1404,7 +1404,7 @@ Rotate Resend API keys annually:
4. Confirm a test submission still works 4. Confirm a test submission still works
5. Revoke the old key in Resend dashboard 5. Revoke the old key in Resend dashboard
### Resend HTTP 403 Domain Not Verified ### Resend HTTP 403: Domain Not Verified
A 403 from the Resend API does NOT mean the API key is wrong. The specific A 403 from the Resend API does NOT mean the API key is wrong. The specific
error is: error is:
@@ -1435,7 +1435,7 @@ in Cloudflare, then click verify. Old key remains active until they remove it.
--- ---
## Form Handling Resend ## Form Handling: Resend
Static sites can't send email by themselves. Every project that needs a Static sites can't send email by themselves. Every project that needs a
contact form gets a small Python service running in its own Docker container, contact form gets a small Python service running in its own Docker container,
@@ -1565,11 +1565,11 @@ are deduplicated by Resend automatically. This prevents:
- API key in `.env` file, NOT in source control. `.gitignore` it. - API key in `.env` file, NOT in source control. `.gitignore` it.
- API key NEVER reaches the browser bundle (only the server has it) - API key NEVER reaches the browser bundle (only the server has it)
- `.env` file lives in `api/`, NOT in the nginx web root - `.env` file lives in `api/`, NOT in the nginx web root
- Server-side validation on EVERY field never trust client - Server-side validation on EVERY field: never trust client
- HTML-escape every field rendered into the email body to prevent injection - HTML-escape every field rendered into the email body to prevent injection
- Rate limit per IP (5 / 15 min default) - Rate limit per IP (5 / 15 min default)
- 16 KB body cap reject anything larger - 16 KB body cap: reject anything larger
- 10-second upstream timeout don't hold connections open - 10-second upstream timeout: don't hold connections open
- CORS locked to the production domain only (`Access-Control-Allow-Origin: https://{domain}`) - CORS locked to the production domain only (`Access-Control-Allow-Origin: https://{domain}`)
- reCAPTCHA v3 with score threshold (default 0.5) once secret is configured - reCAPTCHA v3 with score threshold (default 0.5) once secret is configured
@@ -1650,7 +1650,7 @@ Use this pattern when a project requires server-side processing that static HTML
- **PHP 8.3** (php:8.3-fpm-alpine base image) - **PHP 8.3** (php:8.3-fpm-alpine base image)
- **Nginx** (Alpine package, same container via supervisord) - **Nginx** (Alpine package, same container via supervisord)
- **SQLite** (pdo_sqlite extension, no separate DB container needed) - **SQLite** (pdo_sqlite extension, no separate DB container needed)
- **libsodium** (built into PHP 8.x use for all encryption) - **libsodium** (built into PHP 8.x: use for all encryption)
- **ImageMagick** (pecl imagick for image processing) - **ImageMagick** (pecl imagick for image processing)
- **msmtp** (SMTP relay for outbound email) - **msmtp** (SMTP relay for outbound email)
- **supervisord** (manages nginx + php-fpm + crond in one container) - **supervisord** (manages nginx + php-fpm + crond in one container)
@@ -1697,19 +1697,19 @@ project/
### Security Requirements (Non-Negotiable) ### Security Requirements (Non-Negotiable)
**CSRF** every POST form and API endpoint must verify a CSRF token tied to the session. **CSRF**: every POST form and API endpoint must verify a CSRF token tied to the session.
**Rate limiting** two layers: **Rate limiting**: two layers:
1. nginx: `limit_req_zone` on /api/ (10 req/s, burst 20) 1. nginx: `limit_req_zone` on /api/ (10 req/s, burst 20)
2. PHP: per-IP daily counter in SQLite rate_limits table 2. PHP: per-IP daily counter in SQLite rate_limits table
**reCAPTCHA v3** on conversion/upload endpoints. Verify server-side via Google API. Cache result in session (verify once per session, not per request). **reCAPTCHA v3**: on conversion/upload endpoints. Verify server-side via Google API. Cache result in session (verify once per session, not per request).
**At-rest encryption** any user-uploaded file must be encrypted before writing to disk. Use `sodium_crypto_secretstream_xchacha20poly1305_*` for files, `sodium_crypto_secretbox` for strings. Key stored in `.env` as `QC_ENCRYPTION_KEY` (32 bytes hex). **At-rest encryption**: any user-uploaded file must be encrypted before writing to disk. Use `sodium_crypto_secretstream_xchacha20poly1305_*` for files, `sodium_crypto_secretbox` for strings. Key stored in `.env` as `QC_ENCRYPTION_KEY` (32 bytes hex).
**Signed download tokens** never expose file paths. Issue a 64-char hex token stored in SQLite with expiry and single-use enforcement. **Signed download tokens**: never expose file paths. Issue a 64-char hex token stored in SQLite with expiry and single-use enforcement.
**Magic link auth** prefer magic link over password. On register: create account unverified, send verify email, block login until verified. Token: 64-char hex, 1-hour expiry, stored in `magic_tokens` table, consumed on use. **Magic link auth**: prefer magic link over password. On register: create account unverified, send verify email, block login until verified. Token: 64-char hex, 1-hour expiry, stored in `magic_tokens` table, consumed on use.
### Nginx Security Headers ### Nginx Security Headers
@@ -1720,7 +1720,7 @@ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; form-action 'self' https://checkout.stripe.com;" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; form-action 'self' https://checkout.stripe.com;" always;
# Stripe webhook POST only # Stripe webhook: POST only
location = /api/stripe-webhook.php { location = /api/stripe-webhook.php {
limit_except POST { deny all; } limit_except POST { deny all; }
} }
@@ -1741,7 +1741,7 @@ catch (Throwable $e) { /* column already exists */ }
### Stripe Integration ### Stripe Integration
- Checkout: create session server-side, redirect to Stripe-hosted page - Checkout: create session server-side, redirect to Stripe-hosted page
- Webhook: verify `Stripe-Signature` header using HMAC-SHA256 (implement without Stripe SDK use curl) - Webhook: verify `Stripe-Signature` header using HMAC-SHA256 (implement without Stripe SDK: use curl)
- Webhook tolerance: 300 seconds (5 min) on timestamp - Webhook tolerance: 300 seconds (5 min) on timestamp
- Register webhook endpoint at: `https://{domain}/api/stripe-webhook.php` - Register webhook endpoint at: `https://{domain}/api/stripe-webhook.php`
- Events to subscribe: `checkout.session.completed`, `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, `invoice.payment_succeeded`, `invoice.payment_failed` - Events to subscribe: `checkout.session.completed`, `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, `invoice.payment_succeeded`, `invoice.payment_failed`
+18 -27
View File
@@ -1,7 +1,6 @@
# Image Generation Workflow Arising Media # Image Generation Workflow: Arising Media
Last updated: 2026-05-10 Last updated: 2026-06-09
Project reference: cobhamtech.com (first full run)
--- ---
@@ -15,13 +14,13 @@ Standardized process for generating, validating, and deploying AI images across
API: Google Gemini (generativelanguage.googleapis.com) API: Google Gemini (generativelanguage.googleapis.com)
SDK: google-genai (NOT the deprecated google-generativeai package) SDK: google-genai (NOT the deprecated google-generativeai package)
Draft model: gemini-2.5-flash-image (Nano Banana Speed Mode) Draft model: gemini-2.5-flash-image (Nano Banana: Speed Mode)
Final model: imagen-4.0-generate-001 (Imagen 4 Quality Mode) Final model: imagen-4.0-generate-001 (Imagen 4: Quality Mode)
Format: JPEG, 85% quality, max 1600px wide Format: JPEG, 85% quality, max 1600px wide
--- ---
## Phase 1 Site Analysis (before any generation) ## Phase 1: Site Analysis (before any generation)
Before generating images, read: Before generating images, read:
- index.html (home page structure) - index.html (home page structure)
@@ -38,14 +37,14 @@ Document this in: 01-model-selection.md (image plan table)
--- ---
## Phase 2 Prompt Engineering ## Phase 2: Prompt Engineering
### Rules ### Rules
- Always reference the site color palette in the prompt (dark navy, slate blue, gold accents) - Always reference the site color palette in the prompt (dark navy, slate blue, gold accents)
- Specify "no text" and "no logos" for background images - Specify "no text" and "no logos" for background images
- Specify "photorealistic" for all marketing images - Specify "photorealistic" for all marketing images
- NO PEOPLE. NO FACES. Hardware, infrastructure, and environment only across all client sites - NO PEOPLE. NO FACES. Hardware, infrastructure, and environment only across all client sites
- This applies to all slots: hero, about, services, contact, location no exceptions - This applies to all slots: hero, about, services, contact, location: no exceptions
- Reason: faces introduce identity/representation risk and age poorly. Hardware stays neutral and professional. - Reason: faces introduce identity/representation risk and age poorly. Hardware stays neutral and professional.
### Prompt structure ### Prompt structure
@@ -54,13 +53,13 @@ Document this in: 01-model-selection.md (image plan table)
### Example (hero background) ### Example (hero background)
"Professional enterprise server room, long corridor of dark rack servers with blue LED ambient lighting, deep perspective, dark navy background, cinematic shallow depth of field, no people, photorealistic, ultra detailed" "Professional enterprise server room, long corridor of dark rack servers with blue LED ambient lighting, deep perspective, dark navy background, cinematic shallow depth of field, no people, photorealistic, ultra detailed"
### cobhamtech.com brand prompt additions ### Per-project brand prompt additions
Always append to prompts for this client: Append brand-specific color and tone language to every prompt for a given client.
"dark navy and blue ambient lighting, professional, enterprise, no text" Example: "dark navy and blue ambient lighting, professional, enterprise, no text"
--- ---
## Phase 3 Generation Script Pattern ## Phase 3: Generation Script Pattern
```python ```python
from google import genai from google import genai
@@ -84,16 +83,16 @@ with open('output.jpg', 'wb') as f:
Validate: file must be > 10,000 bytes. Anything smaller is an API error or empty response. Validate: file must be > 10,000 bytes. Anything smaller is an API error or empty response.
CRITICAL Vision validation is mandatory before saving any image: CRITICAL: Vision validation is mandatory before saving any image:
The toolbox script (ai-imagen-generate.sh) automatically sends each generated image to The toolbox script (ai-imagen-generate.sh) automatically sends each generated image to
gemini-2.0-flash for visual inspection. It asks: "Does this image contain people, faces, gemini-2.0-flash for visual inspection. It asks: "Does this image contain people, faces,
hands, silhouettes, or body parts?" If YES the image is rejected, prompt is tightened, hands, silhouettes, or body parts?" If YES: the image is rejected, prompt is tightened,
and generation retries up to 3 times. Only images that pass inspection are saved. and generation retries up to 3 times. Only images that pass inspection are saved.
Claude cannot visually inspect images the vision validation step is the enforcement gate. Claude cannot visually inspect images: the vision validation step is the enforcement gate.
--- ---
## Phase 4 Placement Patterns ## Phase 4: Placement Patterns
### Pattern A: CSS background-image with dark overlay (hero sections) ### Pattern A: CSS background-image with dark overlay (hero sections)
@@ -143,15 +142,15 @@ Implementation: add img to existing grid + expand grid columns
--- ---
## Phase 5 CSP and nginx Updates ## Phase 5: CSP and nginx Updates
Any new image source domain requires a CSP update in nginx.conf. Any new image source domain requires a CSP update in nginx.conf.
For Google Maps tiles: add `https://*.googleapis.com https://*.gstatic.com` to `img-src` For Google Maps tiles: add `https://*.googleapis.com https://*.gstatic.com` to `img-src`
For self-hosted images: `img-src 'self' data:` is sufficient no change needed For self-hosted images: `img-src 'self' data:` is sufficient: no change needed
--- ---
## Phase 6 Docker Rebuild and Verify ## Phase 6: Docker Rebuild and Verify
After every image + HTML change: After every image + HTML change:
@@ -192,11 +191,3 @@ Every generation run must produce a log entry in:
Log must include: date, client, model, each image file name, prompt used, file size in bytes, placement pattern used, Docker rebuild result. Log must include: date, client, model, each image file name, prompt used, file size in bytes, placement pattern used, Docker rebuild result.
---
## Cobhamtech.com Run Reference
Container: cobhamtech-site
Port: 8010
Assets path: /home/sirdrez/arisingmedia-websites/cobhamtech.com/assets/images/
Color tokens: --ct-black #0c0f18 / --ct-slate #1c2d42 / --ct-blue #2d5a9e / --ct-gold #c79330
+11 -23
View File
@@ -6,7 +6,7 @@ Source: cutout.pro/model-comparison/imagen-vs-nanobanana + Gemini API model audi
## Available Models (via Google Gemini API) ## Available Models (via Google Gemini API)
### Imagen 4 Quality Mode ### Imagen 4: Quality Mode
Model ID: `imagen-4.0-generate-001` Model ID: `imagen-4.0-generate-001`
Also available: `imagen-4.0-ultra-generate-001` Also available: `imagen-4.0-ultra-generate-001`
@@ -24,7 +24,7 @@ Use for:
--- ---
### Nano Banana (Gemini 2.5 Flash Image) Speed Mode ### Nano Banana (Gemini 2.5 Flash Image): Speed Mode
Model ID: `gemini-2.5-flash-image` Model ID: `gemini-2.5-flash-image`
Strengths: Strengths:
@@ -40,7 +40,7 @@ Use for:
--- ---
### Imagen 4 Fast Budget Mode ### Imagen 4 Fast: Budget Mode
Model ID: `imagen-4.0-fast-generate-001` Model ID: `imagen-4.0-fast-generate-001`
Use for: Use for:
@@ -52,38 +52,26 @@ Use for:
## Recommended Workflow ## Recommended Workflow
Step 1 Draft with Speed Mode (`gemini-2.5-flash-image`) Step 1: Draft with Speed Mode (`gemini-2.5-flash-image`)
Generate 2-4 variations quickly. Confirm composition, subject, and tone. Low cost. Generate 2-4 variations quickly. Confirm composition, subject, and tone. Low cost.
Step 2 Refine with Quality Mode (`imagen-4.0-generate-001`) Step 2: Refine with Quality Mode (`imagen-4.0-generate-001`)
Take the winning prompt from step 1. Generate final version at full quality. Take the winning prompt from step 1. Generate final version at full quality.
This is the image that goes into the site. This is the image that goes into the site.
Step 3 Review against brand palette Step 3: Review against brand palette
Check that image tones align with site color tokens: Check that image tones align with the project's color tokens from `tokens.css`.
- cobhamtech.com: dark navy (#0c0f18), slate (#1c2d42), blue accent (#2d5a9e), gold (#c79330) All hero images need to work behind dark overlays.
- All hero images need to work behind dark overlays
Step 4 Save to project assets Step 4: Save to project assets
Path convention: `assets/images/{page}-{slot}.jpg` Path convention: `assets/images/{page}-{slot}.jpg`
Examples: `hero-bg.jpg`, `about-visual.jpg`, `services-bg.jpg` Examples: `hero-bg.jpg`, `about-visual.jpg`, `services-bg.jpg`
--- ---
## Cobhamtech.com Image Plan
| Slot | File | Page | Prompt Theme |
|------|------|------|--------------|
| Hero background | `hero-bg.jpg` | index.html | Dark server room, blue ambient lighting, depth of field |
| About story | `about-visual.jpg` | about.html | IT professional at clean desk, dual monitors, neutral dark background |
| Services hub | `services-bg.jpg` | services/index.html | Enterprise network infrastructure, abstract, dark |
| Intro visual | `intro-visual.jpg` | index.html | Business and technology handshake, professional setting |
---
## Notes ## Notes
- Never use Nano Banana for final production images on client sites - Never use Nano Banana for final production images on client sites
- Imagen 4 Ultra adds marginal quality gain over standard not worth the cost for web assets - Imagen 4 Ultra adds marginal quality gain over standard: not worth the cost for web assets
- All images should be exported as JPEG at 85% quality, max 1600px wide, for web performance - All images should be exported as JPEG at 85% quality, max 1600px wide, for web performance
- Run generated images through the site CSP ensure `img-src` allows `self` and `data:` only (no external CDN hotlinking) - Run generated images through the site CSP: ensure `img-src` allows `self` and `data:` only (no external CDN hotlinking)
+7 -7
View File
@@ -1,15 +1,15 @@
# Local Image Generation SOPs # Local Image Generation: SOPs
Complete reference for generating site images locally using ComfyUI. Complete reference for generating site images locally using ComfyUI.
No cloud API required. No per-image cost. Runs on the Arising Media workstation. No cloud API required. No per-image cost. Runs on the Arising Media workstation.
## Index ## Index
1. [01-comfyui-setup.md](01-comfyui-setup.md) Installing ComfyUI, venv, GGUF node 1. [01-comfyui-setup.md](01-comfyui-setup.md): Installing ComfyUI, venv, GGUF node
2. [02-flux-images.md](02-flux-images.md) FLUX.1 Schnell image generation pipeline 2. [02-flux-images.md](02-flux-images.md): FLUX.1 Schnell image generation pipeline
3. [03-wan-video.md](03-wan-video.md) Wan 2.2 image-to-video pipeline 3. [03-wan-video.md](03-wan-video.md): Wan 2.2 image-to-video pipeline
4. [04-prompt-guide.md](04-prompt-guide.md) Prompt patterns for interior/carpet photography 4. [04-prompt-guide.md](04-prompt-guide.md): Prompt patterns for interior/carpet photography
5. [05-quality-levers.md](05-quality-levers.md) Prompt, steps, model size: what to adjust and when 5. [05-quality-levers.md](05-quality-levers.md): Prompt, steps, model size: what to adjust and when
## Quick start (images already set up) ## Quick start (images already set up)
@@ -42,5 +42,5 @@ docker compose build --no-cache web && docker compose up -d
## Reference project ## Reference project
`lahrcarpetcleaning.com` first project using this full pipeline. `lahrcarpetcleaning.com`: first project using this full pipeline.
Scripts: `tools/gen-images-flux.py`, `tools/gen-video-wan.py`, `tools/convert-to-webp.py` Scripts: `tools/gen-images-flux.py`, `tools/gen-video-wan.py`, `tools/convert-to-webp.py`
+12 -38
View File
@@ -1,4 +1,4 @@
# WP + Divi to AM Stack A Pipeline SOP Index # WP + Divi to AM Stack A Pipeline: SOP Index
End-to-end playbook for converting any WordPress / Divi site backup (.wpress) End-to-end playbook for converting any WordPress / Divi site backup (.wpress)
into an Arising Media Stack A deployment: PHP router + SQLite + vanilla JS/CSS. into an Arising Media Stack A deployment: PHP router + SQLite + vanilla JS/CSS.
@@ -17,23 +17,23 @@ complete ordered execution checklist.
| File | Phase | Description | | File | Phase | Description |
|------|-------|-------------| |------|-------|-------------|
| `00-overview.md` | | Pipeline overview, philosophy, what to extract vs not replicate | | `00-overview.md` | n/a | Pipeline overview, philosophy, what to extract vs not replicate |
| `01-wpress-extraction.md` | 1 | .wpress binary format, extraction script, verification | | `01-wpress-extraction.md` | 1 | .wpress binary format, extraction script, verification |
| `02-database-analysis.md` | 2 | MySQL dump parsing, page inventory, Divi version detection | | `02-database-analysis.md` | 2 | MySQL dump parsing, page inventory, Divi version detection |
| `03-divi-content-extraction.md` | 3 | Divi 4 shortcodes vs Divi 5 blocks, extraction scripts | | `03-divi-content-extraction.md` | 3 | Divi 4 shortcodes vs Divi 5 blocks, extraction scripts |
| `04-design-system-extraction.md` | 4 | Colors, fonts, spacing tokens.css | | `04-design-system-extraction.md` | 4 | Colors, fonts, spacing to tokens.css |
| `05-content-migration.md` | 5-6 | Section remapping, content staging, seed_databases.py | | `05-content-migration.md` | 5-6 | Section remapping, content staging, seed_databases.py |
| `06-media-assets.md` | 5 | Upload migration, WebP conversion, media manifest | | `06-media-assets.md` | 5 | Upload migration, WebP conversion, media manifest |
| `07-seo-preservation.md` | 7 | Redirect map, Rank Math extraction, schema.org | | `07-seo-preservation.md` | 7 | Redirect map, Rank Math extraction, schema.org |
| `08-run-order.md` | | DEPRECATED — superseded by `10-agent-breadcrumbs.md` | | `08-run-order.md` | n/a | DEPRECATED. Superseded by `10-agent-breadcrumbs.md` |
| `09-stack-a-output.md` | | SQLite schemas, sections_json spec, DiviAM module mapping | | `09-stack-a-output.md` | n/a | SQLite schemas, sections_json spec, Divi to AM module mapping |
| `10-agent-breadcrumbs.md` | 0-11 | Ordered agent execution checklist (.wpress live Docker) | | `10-agent-breadcrumbs.md` | 0-11 | Ordered agent execution checklist (.wpress to live Docker) |
## Scripts in scripts/ ## Scripts in scripts/
| Script | Purpose | | Script | Purpose |
|--------|---------| |--------|---------|
| `migrate.py` | CLI launcher runs phases 0-6, prints breadcrumbs for 7-11 | | `migrate.py` | CLI launcher: runs phases 0-6, prints breadcrumbs for 7-11 |
| `run_pipeline.sh` | Legacy shell wrapper (pre-migrate.py) | | `run_pipeline.sh` | Legacy shell wrapper (pre-migrate.py) |
| `extract_wpress.py` | Unpack .wpress binary archive | | `extract_wpress.py` | Unpack .wpress binary archive |
| `analyze_db.py` | Parse SQL dump → pages.json + design-system.json | | `analyze_db.py` | Parse SQL dump → pages.json + design-system.json |
@@ -43,39 +43,13 @@ complete ordered execution checklist.
## Key facts about .wpress archives ## Key facts about .wpress archives
- Format: Custom sequential binary (NOT zip/tar) 4377-byte headers - Format: Custom sequential binary (NOT zip/tar): 4377-byte headers
- Table prefix in SQL dump: `SERVMASK_PREFIX_` (placeholder, NOT `wp_`) - Table prefix in SQL dump: `SERVMASK_PREFIX_` (placeholder, NOT `wp_`)
- Directory layout: flat `uploads/`, `themes/`, `plugins/` at archive root (no `wp-content/` wrapper) - Directory layout: flat: `uploads/`, `themes/`, `plugins/` at archive root (no `wp-content/` wrapper)
- Divi 5 stores theme settings in `et_divi` option as PHP-serialized array - Divi 5 stores theme settings in `et_divi` option as PHP-serialized array
## vibrantyou.yoga — extracted data reference
Site: Vibrant You Yoga (instructor: Meghan)
Domain: https://vibrantyou.yoga
Divi version: 5.0.3
WP version: 6.9.4
Design system:
- Primary: #1a8a7a Dark: #0f5f53 Secondary: #2ea3f2
- Body: #5a6b68 Headings: #2d2d2d
- Body font: DM Sans 17px / 1.6 lh
- Heading font: DM Serif Display 600 / 36px / 1.2 lh
Pages to migrate (22 published):
- home, about, classes, schedule, instructors, contact, blog, faq
- book (private sessions), online-yoga, donate
- Drop: video-category, video-tag, search-videos, user-videos, player-embed,
categories, tags, my-bookings (all plugin-generated archive pages)
Plugins requiring AM replacements:
- Gravity Forms + Stripe → AM HTML form + Python API + Resend
- Events Manager → static schedule table in /schedule/
- All-in-One Video Gallery → embed YouTube/Vimeo directly or drop
## Related SOPs ## Related SOPs
- `../01-project-structure.md` AM deployment directory layout - `../STACK.md`: AM deployment directory layout, build pipeline, WP migration playbook
- `../02-wordpress-to-html-migration.md` — Original 8-phase WP migration playbook - `../OPTIMIZATION.md`: Full `<head>` requirements, schema.org per page type
- `../03-build-pipeline.md` — JSON + template stamping for repeated pages - `../tools/verify-protection.sh`: Post-deploy security audit
- `../06-seo-meta.md` — Full `<head>` requirements, schema.org per page type
- `../tools/verify-protection.sh` — Post-deploy security audit