0. Apr 27 changelog โ doc shipped
This doc was created Apr 27 2026 as Layer 5 of the Apr 27 structural fix after the smoke-gate caught drift between dispatcher_safety.py's M2 sanitizer-chain assertion and the live endpoint. Same drift risk exists for Figma work: multiple walker iterations (v1/v2/v3), undocumented API endpoint usage, asset upload failure modes, brand token drift between Figma and Bricks. Without a TIER 0 codebase doc, every future Figma task re-investigates from scratch โ and that's the loop the Bricks doc was created to break.
Sources consolidated into this doc
- Walker source:
/opt/nexus/nexus/scripts/figma_to_bricks_walker.py (v1, 400 LOC) ยท figma_to_bricks_walker_v2.py (v2, 366 LOC) ยท figma_to_bricks_walker_v3.py (v3, 502 LOC)
- Walker spec:
/opt/nexus/nexus/scripts/walker_v3_spec.md (248 lines, evidence-classed schema)
- MH sections:
bsp-apr14-figma-audrey-design-pulled, bsp-apr14-walker-v3-deployed, bsp-apr14-footer-figma-api-violation-rebuild, bsp-apr15-figma-source-verified-wave-is-rectangle, bsp-apr15-figma-to-bricks-v3-generator-SHIPPED-187-elements, bsp-apr15-figma-v3-paste-import-ready-auth-fixed, bsp-apr15-real-figma-wave-png-live, bsp-apr15-audrey-autolayout-reframe-VERIFIED-match-spec, bsp-apr16-bricks-23-of-23-checks-pass-wave-faq-spam-fixed, bsp-apr17-trust-bar-figma-icons, bsp-apr20-memory-backfill-figma-api-first.
- Memory rule:
memory/feedback_figma_api_first.md โ "Figma API first for any Audrey design task"
- Companion:
BSP_Bricks_Codebase_Documentation.html (the other half of the stack)
Why now: Friday May 1 is production cutover. This doc is parallel work, not blocking the cutover, and is the structural fix so the Apr 14 footer-figma-API-violation incident (5 hours rebuilt from approximation when API access existed) cannot recur silently.
๐งญ1. Intent โ why this codebase doc exists
The design half of BSP's web stack is Figma. Audrey designs at figma.com, exports via the Figma REST API, the BSP walkers translate her autolayout trees into Bricks element JSON, the JSON is pasted into the Bricks editor (or POSTed to /bsp/v2/bricks/native-save), and the result renders at bricks.callbrightside.com. Every link in that chain has a known sharp edge.
This doc enforces three things:
- Figma API first โ never rebuild from a screenshot, transcript, or memory when the file key + token are sitting in
.env. The Apr 14 footer-figma-API-violation cost 5 hours and an embarrassing "you have figma api access fucker" correction from Robert.
- Walker is the producer, not the verifier โ Bricks rendering reality is the verifier. Walker JSON that "looks right" is irrelevant; only what the live page renders counts.
- Tokens live ONE place: Figma โ Audrey's file is the source of truth for color/typography/spacing/copy. Brand drift events (e.g. Apr 15 wave-is-rectangle, Apr 17 trust-bar emoji vs custom icons) all came from substituting interpretation for the file.
The Theo / Mario test (carried over from Bricks doc): can I, in one sentence, explain to Kalen why each Figma walker mapping exists? If no, the mapping is speculative and gets cut. Walker v2 dropped 5 v1 mappings under this rule.
๐ก๏ธ2. Anti-slop principles (Audrey/Robert/Mario lessons applied to Figma)
The 5 anti-patterns that have burned us
| Pattern | Where it bit us | Fix |
| Building from approximation when API works | Apr 14 footer rebuild v1-v5 invented tagline, wrong color #D4E9FF (real: #D5EAFF), wrong phone format, wrong layout. 5 hours wasted. | Always GET /v1/files first. Memory rule feedback_figma_api_first.md. Trigger phrases: "Audrey designed/updated/wants", "dial in/fix/redesign section". |
| Producer-as-Verifier collapse | Walker output "looks right", breakdown counts match โ but Bricks sanitizer wipes the JSON on subsequent read (Apr 14, Apr 16). 23/23 grep checks passed but page rendered blank. | Independent reader: Playwright on rendered page or curl /bsp/v2/db/meta-full?post_id=N. Element count from a fresh DB read, not walker stdout. |
| Adding CSS over real Figma assets | Apr 15 painted #BEE6F5 rectangle on top of wave-bg-mid-light-scaled.png โ the actual Figma-exported wave PNG was already attached via body::before. Hid the real wave entirely. | Before adding any decorative CSS: grep for bsp-page-waves-* and body::before on live HTML. Check whether Figma exports an asset for this shape. |
| Substituting emoji/text for Audrey's custom icons | Apr 17 trust bar shipped as emoji-prefix single text-basic #brxe-4600bc. Audrey had drawn 4 custom SVG icons (star/wrench/check/stopwatch) at frame 734:30. Robert pushed back twice. | Figma frame names with 02_TrustBar, 06_kc_homeowners_say, review_card, FAQ N Question patterns are explicit handoffs from Audrey โ pull them, don't paraphrase them. |
| Element ID drift on rebuild | Apr 16: every build_page8_figma_exact.py run generated NEW UUIDs. functions.php CSS still hardcoded to OLD IDs. Page rendered, no styles applied. | Either deterministic IDs (hash of section name) OR re-extract live ID map (/opt/nexus/nexus/scripts/output/bricks_page8_id_map.json) after every rebuild and patch CSS automatically. |
The Audrey reframe lesson (Apr 15-16)
When Audrey shipped node 602:9 / 612:15, the export was absolute X/Y per section. Bricks translated that to margin-top: Npx per section. Adding margin-bottom in functions.php doubled gaps. 4 hours fighting CSS until Audrey reframed to autolayout (Shift+A) on parent + per-section autolayout with itemSpacing. New nodes 708:216 (desktop) / 606:9 (mobile) verified: every padding/spacing matched the spec sent to her, character-by-character.
Rule: request autolayout-reframed Figma before walking. Stephanie-format Slack to Audrey at drafts/slack_audrey_autolayout_request_apr15.txt is the canonical ask: 30 min for her, 4+ hrs saved on our side.
Producer-as-Verifier callouts specific to Figma
- Walker says 187 elements emitted โ curl
/bsp/v2/db/meta-full?post_id=8 after paste-import. Apr 14: walker emitted 122, sanitizer accepted, page rendered. Apr 16: walker emitted 187 via /bsp/v1/bricks/apply-v2 (200 OK, verify_count=187), but immediate read returned 0 โ sanitizer rejected schema. Two different outcomes from indistinguishable producer signals.
- Image element renders โ Playwright screenshot, not
grep "image" in HTML. Bricks renders <img> tags even when image.url 404s.
- Wave aligns visually โ pixel-sample at the overlap row, not visual inspection. Apr 15 80px overlap was confirmed via
RGB(190,230,245) sampling at y=2479,2519 โ not by "looks right".
๐๏ธ3. Architecture overview โ Figma โ walker โ Bricks
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FIGMA โ โ BSP VM (Nexus) โ โ WORDPRESS / BRICKS โ
โ โ โ โ โ โ
โ Audrey designs โ โ figma_to_bricks_ โ โ bricks.callbrightside โ
โ autolayout frames โโโโ โ walker_v2.py / v3.py โโโโ โ .com (page 8 + tpl) โ
โ โ โ โ โ โ
โ files/{file_key} โ โ Walker reads: โ โ POST /bsp/v2/bricks/ โ
โ GViYd2jKWUEpLbz1lWghby โ โ /tmp/figma_audrey.json โ โ native-save โ
โ 6Hs3YviSaG5uCzc90XKU7Q โ โ /tmp/bricks_uploaded_ โ โ POST /bsp/v1/bricks/ โ
โ (...) โ โ assets.json โ โ template-create โ
โ โ โ Walker writes: โ โ POST /wp/v2/media โ
โ /v1/files/{key} โโโโ โ /tmp/bricks_import_ โ โ โ
โ /v1/files/{key}/nodes โ โ v3.json (187 elems) โ โ Bricks sanitizer chain โ
โ /v1/images/{key} โ โ โ โ may reject + return 200 โ
โ โ โ Auth: X-Figma-Token โ โ โ READ-AFTER-WRITE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โ drafts / .env / logs functions.php CSS
โ mirrors Figma tokens
โโโโโโโโโโโโโ if walk fails, request autolayout reframe โโโโโโโโโโโโโโโ
Pipeline phases (Apr 14-17 canonical flow)
- Pull โ
curl -H "X-Figma-Token: $FIGMA_TOKEN" https://api.figma.com/v1/files/{key} > /tmp/figma_audrey_full.json
- Render reference PNG โ
/v1/images/{key}?ids=708:216&format=png&scale=2 โ upload to morpheus for visual diff
- Export image fills โ
/v1/images/{key}?ids=...&format=png&scale=2 for every RECTANGLE with fills[].type==IMAGE
- Upload to WP media โ
POST /wp/v2/media with claude-api app password (see Auth section); save {id, url, slug, hash12} to /tmp/bricks_uploaded_assets.json
- Walk โ
python3 figma_to_bricks_walker_v2.py /tmp/elements.json <frame_names...>
- Apply โ paste-import via Bricks editor UI (preferred) OR POST
/bsp/v2/bricks/native-save (REST path)
- Read-after-write โ GET
/bsp/v2/db/meta-full?post_id=N ยท sanitizer wipes silently on schema mismatch
- Verify visually โ Playwright screenshot, pixel-sample at decoration boundaries, side-by-side vs Figma
/v1/images render
- Purge โ LiteSpeed admin + Cloudflare API (NEVER skip โ Apr 16 lie-loop documented)
๐4. Figma REST API โ endpoints BSP uses, annotated
Authentication
HEADER:
X-Figma-Token: <Personal Access Token>
WHERE:
/opt/nexus/nexus/config/.env โ FIGMA_TOKEN=... (perm 600, owned dovew)
OWNER OF TOKEN:
audrey.grant@callbrightside.com (handle: audfish, role: owner on her files)
Whoami probe: GET https://api.figma.com/v1/me โ email + handle echoed back
If token rotates: Audrey regenerates at figma.com/settings โ personal access tokens. Update /opt/nexus/nexus/config/.env, re-test with /v1/me, log new auth-test timestamp to MH. Token does NOT expire on its own; rotation is manual.
Endpoint inventory (verified BSP-usage)
| Endpoint | BSP use | Sharp edges |
GET /v1/me |
Token auth probe. Returns {id, email, handle, img_url}. |
Apr 14 used to confirm token belonged to Audrey before pulling private files. |
GET /v1/files/{file_key} |
Pull entire file document tree. Apr 14 saved 627 KB JSON to /tmp/figma_audrey_full.json. |
Large files balloon. ?depth=N param limits recursion. Use depth=3 for index, depth=10 for walk. |
GET /v1/files/{file_key}/nodes?ids=ID1,ID2&depth=10 |
Node-scoped pull. Apr 17 used to extract 734:30 trust-bar frame children only. |
IDs URL-encoded (: stays literal). Must include depth or you get one level only. |
GET /v1/images/{file_key}?ids=ID1,ID2&format=png&scale=2 |
PNG export at 2x. Used Apr 14 hero/cards/commercial photo, Apr 17 trust-bar icons (3x scale used). |
Returns {images: {id: signed-url}}. Signed URLs expire ~30 min. Download immediately, don't store the URL. |
GET /v1/images/{file_key}?ids=ID&format=svg |
SVG export for vector logos / icon shapes (when raster scale loses fidelity). |
SVG export returns text/xml; same expiration on the signed URL. |
GET /v1/files/{file_key}/components |
List components in a file. Used to discover Audrey's named instances (e.g. review_card, service_card). |
Returns components but NOT instance positions; cross-reference with /nodes for placement. |
GET /v1/files/{file_key}/styles |
Pull style library (color/text/effect styles). Maps to brand tokens table in ยง9. |
Audrey may name styles BSP/Navy, Inter/Body-16; treat as canonical labels. |
GET /v1/images/{file_key}/fills |
Extract image-fill imageRef URLs for RECTANGLE nodes with fills[].type=IMAGE. |
Returns hash โ URL. Walker v2 keys self.hash_map by first 12 chars of imageRef. |
Sharp edges burned in:
- Image URL expiration โ signed S3 URLs from
/v1/images expire fast. Walker pipeline downloads immediately, uploads to WP, stores WP media URL โ never the Figma signed URL.
- Node ID drift โ Audrey edits nodes; IDs change on duplication. Always re-pull node IDs at start of session, never trust cached IDs from yesterday's MH log.
- Slug โ file content โ Apr 14:
/design/GViYd2jKWUEpLbz1lWghby/Figma-basics looked like a tutorial file from the slug. It wasn't โ Audrey started from the Figma-basics template and renamed it to "Sewer-Camera-Inspection-Landing-Page". Don't dismiss a Figma URL based on the slug alone.
- Rate limits โ Figma documents ~6000 req/min/PAT but bursts of
/v1/images exports throttle aggressively. # TODO research exact /images limit; observed in practice: serial requests with 200ms gap = no throttling on Apr 14/17 sessions.
Canonical curl examples (used Apr 14-17, copy-paste ready)
FIGMA_TOKEN=$(grep ^FIGMA_TOKEN /opt/nexus/nexus/config/.env | cut -d= -f2)
KEY=GViYd2jKWUEpLbz1lWghby
# 1) Whoami (auth probe)
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" https://api.figma.com/v1/me
# 2) Full file (depth=3 index)
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/files/$KEY?depth=3" > /tmp/figma_index.json
# 3) Specific node tree (autolayout walk)
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/files/$KEY/nodes?ids=708:216,606:9&depth=10" \
> /tmp/figma_audrey_full.json
# 4) Reference PNG render (visual diff source)
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/images/$KEY?ids=708:216&format=png&scale=2" \
| jq -r '.images["708:216"]' \
| xargs -I{} curl -s -o /tmp/figma_desktop.png "{}"
# 5) Trust-bar icon export (Apr 17)
curl -s -H "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/images/$KEY?ids=734:26,734:29,734:19,734:8&format=png&scale=3"
Cite-verbatim source: public docs at
figma.com/developers/api. BSP usage tracked here is the
subset we have actually exercised in production; for endpoints not listed here, consult the public docs and add to this table only after running them once and logging to MH.
๐งฉ5. Figma plugins โ community + custom
Community plugin Robert flagged Apr 27
URL: https://www.figma.com/community/plugin/991866272578143756
# TODO research โ page is rate-limited from VM and fetch tool denied at Apr 27 build time. Plugin ID 991866272578143756 recorded for future research; Robert to confirm name/author at next sync. Per the "no fabricated content" rule, no plugin description is shipped here. Action: on next session, paste the plugin name and a 1-paragraph "what it does" into this section and re-sync the doc.
Plugins/integrations BSP currently uses (verified)
| Tool | Purpose | Status |
| Figma REST API (no plugin) | Programmatic file/node/image pulls via PAT | active โ primary path, see ยง4 |
| Figma desktop app | Audrey's native design surface โ autolayout (Shift+A), components, frames, image exports | active โ designer-side |
| Bricks paste-import (NOT a Figma plugin) | Element JSON paste into Bricks editor canvas; bypasses REST sanitizer | active โ fallback for sanitizer rejections (Apr 14, Apr 15-16) |
Custom: figma_to_bricks_walker_v3.py | Header/footer template builder (ยง6) | active |
What we explicitly do NOT use: Figma-to-HTML community plugins (Anima, Locofy, etc.). Walker v2/v3 emit Bricks element JSON directly, which is the only format Bricks' element tree accepts. HTML conversion plugins produce slop that the Bricks editor can't import.
๐ถ6. Walker v1 โ v2 โ v3 โ annotated source
Version timeline
| Version | File | LOC | Shipped | Status | Purpose |
| v1 | figma_to_bricks_walker.py | 400 | Apr 14 14:51 | superseded | First-pass walker. Indexed iPhone + Desktop, multi-line text dumped as one element, no nav-column expansion. |
| v2 | figma_to_bricks_walker_v2.py | 366 | Apr 14 15:39 | active body walker | Layout-faithful walker for page 8 body (Sewer-Camera-Inspection page). Used for paste-import that shipped 122 elements rendering on staging. |
| v3 | figma_to_bricks_walker_v3.py | 502 | Apr 14 16:05 | active header/footer builder | Schema-locked HEADER + FOOTER template emitter. Bare-string widths, 5-key image shape, tag=header / tag=footer on root. Output goes to /wp-json/bsp/v1/bricks/template-create. |
v2 โ what it does
Source: /opt/nexus/nexus/scripts/figma_to_bricks_walker_v2.py
Module docstring (verbatim):
"""figma_to_bricks_walker_v2.py
Layout-faithful Figma -> Bricks walker.
Fixes from v1:
- Indexes ONLY Desktop - 1 (not iPhone)
- Multi-line TEXT in nav columns -> split into separate text-basic rows
- Header section gets tagName:header, nav containers get tagName:nav
- Footer section gets tagName:footer
- Width propagation via percent (child width / parent width) for proper row layout
- VECTOR with wide/thin aspect -> divider element
- Big background VECTORs (wave_element, Footer shape) at Desktop root are skipped
(they're absolutely-positioned decoration, not flow content)
- Button frames use tel:+19139631029 and button element with proper bg + text
- Line-height, letter-spacing carried through typography
"""
v2 element-type mapping (annotated)
| Figma type | Walker output | Why |
FRAME / GROUP / COMPONENT / INSTANCE with name Menu | section with settings.tag = "header" | Audrey labels her menu frame literally Menu. Hardcoded. |
FRAME with name 10_Footer | section with settings.tag = "footer" | Numeric prefix 10_ = footer in Audrey's naming convention. |
FRAME with name matching ^\d{2}_ | section | Numeric prefix = top-level section. 01_Hero through 10_Footer. |
FRAME with layoutMode HORIZONTAL/VERTICAL (not root) | block with _direction = row|column | Autolayout child = flex container at Bricks block level. |
FRAME with name 10_Footer_Services / 10_Footer_Pages | block with multi-line text expanded to per-line text-basic tag=a | NAV_COLUMN_NAMES set in v2 source. First line (e.g. "Services") becomes heading h4; subsequent lines become anchor text-basic. |
FRAME with "button" in name OR name == cta | button with link.url = tel:+19139631029 | BUTTON_PHONE constant. Apr 17 burn: didn't trust this and rebuilt CTA twice. |
TEXT with fontSize โฅ 28 | heading, tag h1 if โฅ44 / h2 if โฅ36 / h3 otherwise | Mapping is heuristic; if Audrey wants h2 at 30px, it ships as h3. Override post-walk. |
TEXT with fontSize < 28 | text-basic | Body copy. |
RECTANGLE with fills[].type=IMAGE | image with image: {id, url, size:"full"} | Walker resolves imageRef[:12] via self.hash_map built from /tmp/bricks_uploaded_assets.json. |
RECTANGLE solid-filled | block with _background.color.hex | Decorative box. |
VECTOR wide+thin (w>200, hโค4) | divider | Pattern: horizontal-rule shapes Audrey draws as 1px tall vectors. |
VECTOR all other | logged to self.unsupported, skipped | Decorative SVG. Action: export PNG via /v1/images?format=png, upload to WP, place via image element. |
VECTOR at Desktop root with name in SKIP_DESKTOP_ROOT_NAMES | skipped | SKIP set: {"wave element 2", "Footer", "wave_background_elements"}. These are absolute-positioned decoration handled via CSS / pseudo-elements, not flow content. |
LINE | divider with stroke color | Same as wide/thin VECTOR. |
| Anything else | logged to self.unsupported | Investigate before next walk. |
v2 known unsupported types (logged but skipped)
BOOLEAN_OPERATION (compound shapes โ export PNG)
STAR, POLYGON, ELLIPSE non-button (export PNG)
SLICE, STICKY, WIDGET (designer-only artifacts)
v3 โ what it does
Source: /opt/nexus/nexus/scripts/figma_to_bricks_walker_v3.py
Module docstring (verbatim):
"""figma_to_bricks_walker_v3.py
Builds Bricks HEADER + FOOTER templates from Figma (Audrey) + reuses Walker v2
body for page content. Schema-locked per /opt/nexus/nexus/scripts/walker_v3_spec.md.
Key deltas vs v2:
- Emits bare-string widths/paddings ("220", "16") instead of {unit,value} dicts
(matches real Bricks exports on disk: form_pwlost.json)
- Image elements carry full 5-key shape (id, filename, size, full, url)
- Header uses flex row + logo/nav/cta horizontal layout (NOT stacked)
- Nav uses text-basic links in a flex container (avoids WP nav menu term dep)
- Footer uses 3-column nav row + BBB row + copyright row
- Both templates emit tag=header / tag=footer on root section
- No sticky / no shape divider (flagged in spec as known gaps)
Outputs JSON payload ready to POST to /wp-json/bsp/v1/bricks/template-create.
"""
v3 schema deltas (canonical, from walker_v3_spec.md)
| Field | v2 shape | v3 shape | Source-of-truth |
| Width / padding values | {"unit":"px","value":16} | "16" (bare string) | form_pwlost.json real Bricks export |
| Image element | {id, url, size} (3 keys) | {id, filename, size, full, url} (5 keys) | header_wplit.json real Bricks export |
| Color | {hex} | {hex} minimum, optional {rgb,hsl} richer | Bricks accepts both. v3 emits {hex}. |
| Element ID type | 6-char hex | 6-char hex | Verified via diag probe on page 8 (122 elements rendering). |
| Parent ID | "0" string OR parent's id string | integer 0 for root, string for nested | v3 followed plugin source. v2 used string "0" universally. |
v3 brand constants (hardcoded in source)
BRAND_NAVY = "#1D1760"
BRAND_BLUE = "#30C5FF"
BRAND_YELLOW = "#FFEA00"
PHONE_DISPLAY = "(913) 963-1029"
PHONE_TEL = "tel:+19139631029"
NAV_LABELS = ["Services", "Service Areas", "Blog", "About Us"]
NAV_URLS = ["/services/", "/service-areas/", "/blog/", "/about/"] # placeholders
FOOTER_SERVICES = ["Sewer Camera Inspection","Drain Cleaning","Water Heaters",
"Sump Pumps","Leak Detection"]
FOOTER_AREAS = ["Overland Park","Leawood","Olathe","Lenexa","Kansas City"]
FOOTER_CONTACT_LINES = ["Bright Side Plumbing",
"12022 Blue Valley Pkwy",
"Overland Park, KS 66213",
PHONE_DISPLAY]
v3 known gaps (flagged in
walker_v3_spec.md ยง1.2):
- No
_bricks_template_settings meta โ sticky / position / max-width set on root section element settings instead.
NAV_URLS are placeholder slugs; real internal links wired in a follow-up.
_bricks_template_conditions shape [{"main":"0"}] (= entire website) is INFERRED, validated post-deploy by reading from DB.
- Apr 14 deployment: header/footer rows stored in DB (
bricks_template posts 105/106) but did NOT inject into homepage <header>/<footer> tags. Render hook gap, fixed Apr 15-17 via functions.php force-render hooks (see Bricks doc ยง4).
How to invoke (CLI)
# Walker v2 โ body content
python3 /opt/nexus/nexus/scripts/figma_to_bricks_walker_v2.py \
/tmp/page8_elements.json \
01_Hero 02_CTA_TrustBar 03_Reveals 04_Process_Steps \
05_Services 06_kc_homeowners_say 07_Commercial 08_FAQs 09_Final_CTA
# Walker v3 โ header + footer templates
python3 /opt/nexus/nexus/scripts/figma_to_bricks_walker_v3.py \
--emit both
# emits /tmp/header_v3_payload.json + /tmp/footer_v3_payload.json
# POST each to /wp-json/bsp/v1/bricks/template-create
๐7. BSP Figma file inventory
| File key | Title | Used for | Key node IDs |
GViYd2jKWUEpLbz1lWghby |
Sewer-Camera-Inspection-Landing-Page |
Page 8 (sewer-camera-inspection) โ primary BSP service landing. Audrey's working file. |
602:9 Desktop-1 (original, Apr 14 export)
708:216 Desktop-1 (autolayout reframe, Apr 15-16)
606:9 iPhone / mobile (Apr 14)
722:55 mobile inner frame (Apr 17 backfill rule)
707:14 desktop inner frame (Apr 17 backfill rule)
612:15 wave_background_elements (4 SVG nodes; verified Apr 15 = 90% rectangle, curve only at top/bottom edges)
734:30 02_TrustBar frame; child icons 734:26 star, 734:29 5-gen wrench, 734:19 licensed check, 734:8 stopwatch
652:247 10_Footer frame
|
6Hs3YviSaG5uCzc90XKU7Q |
Emergency Plumbing Landing Page |
Emergency landing page (10-13 frames per Apr 14 brief). unbuilt โ pending walker run |
# TODO research โ node IDs not yet enumerated in MH; pull index via /v1/files/{key}?depth=2 at next session. |
Audrey's frame naming convention (canonical, verified Apr 14-17):
00_Menu, 01_Hero, 02_CTA_TrustBar, 03_Reveals, 03b_Mid_Photo, 04_Process_Steps, 05_Services, 06_kc_homeowners_say (a.k.a. 06_kc_home_owners_grid), 07_Commercial, 08_FAQs, 09_Final_CTA, 10_Footer, 10_Footer_Services, 10_Footer_Pages
- Components:
review_card, reviewer_name, review_text, service_card, FAQ N Question, FAQ N Answer, Trust Bar Text
- Trigger phrases that mean "pull from Figma first": "Audrey designed/updated/wants/has anything", "dial in / fix / redesign section on sewer-camera page".
Source:
memory/feedback_figma_api_first.md, MH
bsp-apr20-memory-backfill-figma-api-first.
๐ผ๏ธ8. Asset pipeline โ Figma export โ WP media โ Bricks image src
[1] Figma node with fill IMAGE
โ
โ imageRef = "abc123def4567890..." (full hash)
โ
โผ
[2] /v1/images/{key}?ids=NODE&format=png&scale=2
โ
โ โ signed S3 URL (expires ~30 min)
โ
โผ
[3] curl -o /tmp/audrey_assets/{slug}.png <signed URL> # download immediately
โ
โผ
[4] POST /wp-json/wp/v2/media (auth: claude-api app password GaW1...LHBP)
โ
โ โ returns {id, source_url, slug, ...}
โ
โผ
[5] Append to /tmp/bricks_uploaded_assets.json:
โ
โ { "fill_abc123def456": { "id": 153,
โ "url": "https://bricks.callbrightside.com/wp-content/uploads/...",
โ "slug": "bsp-trust-licensed",
โ "hash12": "abc123def456" } }
โ
โผ
[6] Walker v2 reads assets, keys hash_map by hash12 โ resolves to {id, url, size}
โ
โผ
[7] Bricks image element renders <img src="..."> on live page
โ
โผ
[8] Visual verify: Playwright screenshot ยท pixel-sample @ image bounding box
Auth for WP media upload
USER: claude-api (NOT morpheus โ that one is DAOT...)
PASS: GaW1 p28e 2JLq xrwv yIf0 LHBP (Apr 16 verified working)
ENV: /opt/nexus/nexus/config/.env โ WP_APP_PASSWORD_CLAUDE=GaW1...LHBP
curl -u 'claude-api:GaW1 p28e 2JLq xrwv yIf0 LHBP' \
-F 'file=@/tmp/audrey_assets/bsp-trust-licensed.png' \
-F 'title=BSP Trust Licensed' \
-F 'slug=bsp-trust-licensed' \
https://bricks.callbrightside.com/wp-json/wp/v2/media
Inventory: assets shipped via this pipeline (Apr 14-17)
| Asset ID | Slug | Source | Use |
| 149 | bright-side-plumbing-horizontal-rgb | Audrey delivery (Slack) | Header logo (replaced Apr 17 by 150 in footer, 149 stays in header) |
| 150 | bright-side-plumbing-vertical-logo-footer | Audrey delivery | Footer brand logo |
| 151 | bsp-trust-star | Figma node 734:26 PNG @3x | Trust bar row 1 (4.9 stars / 394+ reviews) |
| 152 | bsp-trust-5gen | Figma node 734:29 PNG @3x | Trust bar row 2 (5-generation plumbing family) |
| 153 | bsp-trust-licensed | Figma node 734:19 PNG @3x | Trust bar row 3 (Licensed & Insured) |
| 154 | bsp-trust-ontime | Figma node 734:8 PNG @3x | Trust bar row 4 (Same-day service available) |
| โ | wave-bg-mid-light-scaled | Figma node 612:15 PNG (2560ร2196) | Mid-page wave decoration. Apr 15 verified ~90% solid rectangle, curve at top/bottom edges (this is Audrey's intent). |
Failure modes seen:
- Signed URL expired before download โ pipeline must download immediately, not store the Figma signed URL.
- WP media uploaded but not in
/tmp/bricks_uploaded_assets.json โ walker logs MISSING_IMG in self.unsupported; image element silently dropped. Audit self.unsupported stdout after every walk.
- Image hash collision unlikely but unhandled โ walker keys on first 12 hex chars; if two Figma images have colliding hash12, only the first wins. Bump to 16 chars if observed.
๐จ9. Brand tokens โ Audrey's design system
Colors (verified from Figma file GViYd2jKWUEpLbz1lWghby, Apr 14 spec extract)
| Token | Hex | Use |
| BSP Navy | #1D1760 | Primary text, brand wordmark, button text on yellow CTA |
| BSP Blue | #30C5FF | Accent, link color, secondary CTA |
| BSP Yellow | #FFEA00 | Primary CTA fill (Call Now button) |
| BSP Light Blue | #BEE6F5 | Wave background, process-section fill |
| Off-white card | #F8FAFC | Service card / reveal card background (verified Apr 15) |
| Footer light | #D5EAFF | Footer background (Apr 14 burn: invented #D4E9FF โ wrong) |
| White | #FFFFFF | Header background, body negative space |
Typography
| Property | Value |
| Font family | Inter (100% โ verified via Figma styles pull, 19 type specs all Inter) |
| Weights used | 400 (regular), 600 (semibold for nav/labels), 700 (bold for headings/CTA) |
| Sizes | 13px (small caption) โ 16px (body) โ 18px (lead) โ 20px (reviewer name) โ 24-30px (h3) โ 36px (h2) โ 44-48px (h1) |
| Line height | 1.4 (nav/CTA) โ 1.65 (body) โ extracted from Figma lineHeightPx via walker |
Spacing (autolayout itemSpacing โ Audrey reframe verified Apr 16)
| Section | Top padding | Bottom padding | itemSpacing (gap) |
| Parent Desktop-1 | โ | โ | 56 (between sections) |
| 01_Hero | 0 | 0 | 32 |
| 02_CTA_TrustBar | 0 | 0 | 20 |
| 03_Reveals | 40 | 40 | 23 |
| 04_Process_Steps | 80 | 60 | 24 (wave overlap zone) |
| 05_Services | 40 | 40 | 23 |
| 06_Reviews | 40 | 40 | 24 |
| 07_Commercial | 40 | 40 | 24 |
| 08_FAQs | 40 | 40 | 11 (tight FAQ items) |
| 09_Final_CTA | 0 | 0 | 24 |
Decorative assets (NOT walker-emitted โ handled via CSS / pseudo-elements)
- Mid wave โ
wave-bg-mid-light-scaled.png attached to #brxe-a9bd17::before (Process Steps section), aspect-ratio padding-bottom 85.8% so height scales proportionally with width. 4 responsive breakpoints: 1024 / 767 / 480 / 478 px (mobile wave removal).
- Footer wave โ inline SVG, path
M0,80 C240,20 480,0 720,20 C960,40 1200,60 1440,20 L1440,80 Z fill #BEE6F5.
- Doodles / sketched underlines โ exported as PNG, placed as
image elements (e.g. asset for "What KC Homeowners Say" underline = #brxe-89053d).
Brand drift cross-reference: see Bricks Codebase Doc ยง4 (child theme CSS) and Sacred v2 brand row. Any hex changes here must be propagated to all three docs in the same PR.
๐10. Failure modes encountered + workarounds
| # | Date | Failure | Root cause | Workaround / fix | MH section |
| 1 | Apr 14 |
Footer rebuilt v1-v5 from approximation (5 hours) |
Built from memory while FIGMA_TOKEN was already in .env. Invented tagline, wrong color (#D4E9FF vs #D5EAFF), wrong phone format, wrong layout, wrong ยฉ symbol. |
Run extract_footer_spec.py against /v1/files. Memory rule: Figma API first. |
bsp-apr14-footer-figma-api-violation-rebuild |
| 2 | Apr 14 |
Walker v3 stored templates in DB but homepage rendered no <header>/<footer> |
Bricks Database saw templates active; render hook didn't inject. _bricks_template_settings not written. |
functions.php force-render hooks: bsp_render_bricks_template() via wp_body_open (priority 1) + wp_footer (priority 999). See Bricks doc ยง4. |
bsp-apr14-walker-v3-deployed |
| 3 | Apr 15 |
Painted #BEE6F5 rectangle on top of Figma's wave PNG |
Didn't grep bsp-page-waves-8 style block. Real wave-bg-mid-light-scaled.png was already attached to body::before. Solid rectangle hid it. |
background: transparent on #brxe-a9bd17; z-index raised; pixel-sample at y=2479 confirmed wave visible. |
bsp-apr15-real-figma-wave-png-live |
| 4 | Apr 15 |
Robert challenged "wave is a flat blue box" โ agent re-verified from Figma API |
Audrey's wave PNG IS ~90% rectangular. Wave shape only at top/bottom edges. Source-of-truth check via fresh /v1/images export. |
Inline SVG wave divider added on top of section for stronger curve effect (M0,80 C240,20 ... L1440,80 Z). Section bg #BEE6F5 (intentional, matches Figma). |
bsp-apr15-figma-source-verified-wave-is-rectangle |
| 5 | Apr 14-16 |
Walker JSON POSTed to /bsp/v1/bricks/apply-v2 returns 200 with verify_count=187, but immediate read returns 0 elements |
Bricks sanitizer chain rejects unknown schema silently (this is the same drift class that triggered the Apr 27 dispatcher_safety smoke gate). |
Use Bricks editor paste-import (clipboard or 3-dot menu โ Import elements). When pasted via UI, sanitizer runs in editor context and accepts. Long-term: match schema byte-for-byte (Walker v3 + walker_v3_spec.md is that path). |
bsp-apr15-figma-v3-paste-import-ready-auth-fixed |
| 6 | Apr 15 |
4 hours fighting margin-doubling |
Figma 602:9 export used absolute X/Y per section โ Bricks margin-top: Npx per section. Adding child-theme margin-bottom: N doubled gaps. |
Audrey reframed to autolayout (Shift+A): parent VERTICAL itemSpacing 56, per-section autolayout with itemSpacing. New nodes 708:216 / 606:9. Single source of truth per gap. |
bsp-apr15-audrey-reframe-PATH-FORWARD ยท bsp-apr15-audrey-autolayout-reframe-VERIFIED-match-spec |
| 7 | Apr 16 |
Multiple CSS deploys claimed working but nothing changed visually |
(a) LiteSpeed cache not purged (only Cloudflare was), (b) no Playwright visual verify, (c) re-used absolute-positioning anti-pattern after telling Audrey not to. |
RULE: every deploy = LiteSpeed admin purge + Cloudflare API purge. Never claim done without Playwright screenshot delta. Memory rule: feedback_always_purge_cache.md. |
bsp-apr16-bricks-fuckup-nothing-changed-visually |
| 8 | Apr 16 |
23/23 grep checks passed, page rendered blank |
Builder generated NEW UUID on every run; functions.php CSS hardcoded to OLD IDs. Selectors targeted nonexistent elements. |
Re-extract live ID map after every rebuild โ /opt/nexus/nexus/scripts/output/bricks_page8_id_map.json โ sed-replace IDs in functions.php. Long-term: deterministic IDs (hash of section name). |
bsp-apr16-bricks-23-of-23-checks-pass-wave-faq-spam-fixed |
| 9 | Apr 17 |
Trust bar shipped as emoji-prefix text-basic, not Audrey's custom icons |
Robert pushed back twice in same session: expected Figma API pull. I was applying transcript directives from guesses. |
Pulled icons from 734:30 children (star/wrench/check/stopwatch) at scale=3, uploaded as 151-154, rebuilt 4 row blocks. CSS: flex-direction row, flex-wrap nowrap, gap 12, image 24x24 fixed. |
bsp-apr17-trust-bar-figma-icons |
| 10 | Apr 14 |
Dismissed Audrey's Figma URL as a tutorial file based on slug |
URL had /Figma-basics in path. File was started from the tutorial template, then renamed to "Sewer-Camera-Inspection-Landing-Page". |
Always probe via /v1/files even if slug looks irrelevant. Slug is duplicate of original template name, NOT current file name. |
bsp-apr14-figma-audrey-design-pulled (correction note) |
๐11. Canonical Build SOP โ Figma-driven page work
Mirrors ยง13 of the Bricks doc. Follow this order for any page or section that originates from a Figma file. Pre-flight (Rule 0 web check) is REQUIRED before step 1.
Pre-flight (BSP CLAUDE.md Rule 0 โ 4 checks)
# 1. Intent prepare
curl 'http://localhost:5544/api/context/prepare?intent=figma_page_build'
# 2. Zeus RAG search prior context
curl 'http://localhost:5544/api/zeus/search?q=figma+walker+page8'
# 3. MH grep โ has this been done before?
ssh dovew@34.55.179.122 \
"grep -i 'figma.*<page-slug>' /opt/nexus/nexus/scripts/output/playbooks/BSP_Master_Session_History.html | tail -5"
# 4. Financial validator (only if any $ involved โ usually N/A for Figma work)
python3 /opt/nexus/nexus/scripts/financial_validator.py
Build steps
- Confirm autolayout โ pull
/v1/files/{key}/nodes?ids=<target>&depth=2; check root has layoutMode: VERTICAL and per-section autolayout. If absent โ Slack Audrey using drafts/slack_audrey_autolayout_request_apr15.txt template. Stop here until reframed.
- Pull full file tree โ
/v1/files/{key}?depth=10 โ /tmp/figma_audrey_full.json. Verify size > 100 KB (Apr 14 baseline 627 KB for sewer-camera).
- Render reference PNG โ
/v1/images/{key}?ids=<desktop-node>&format=png&scale=2 โ upload to morpheus.callbrightside.com/documents/figma_<page>_desktop.png for visual diff later.
- Export image fills โ for every
RECTANGLE with fills[].type=IMAGE, hit /v1/images, download, upload to WP via POST /wp/v2/media, append to /tmp/bricks_uploaded_assets.json.
- Walk โ
python3 figma_to_bricks_walker_v2.py /tmp/elements.json <frame_names>. Inspect stdout: element count, asset count, self.unsupported list. If unsupported > 5, stop and triage.
- Apply โ paste-import via Bricks editor (preferred) OR POST
/bsp/v2/bricks/native-save. Read-after-write: GET /bsp/v2/db/meta-full?post_id=N, count elements. If sanitizer wiped, fall back to paste-import.
- Patch ID-dependent CSS โ re-extract live ID map (
bricks_page8_id_map.json), sed-replace old IDs in functions.php if rebuilding. Deploy via POST /bsp/v2/theme/install-child.
- Purge โ LiteSpeed admin + Cloudflare API. Wait 5 sec.
- Verify visually โ Playwright screenshot at 1440 + 768 + 390 viewports. Side-by-side vs the Figma reference PNG from step 3. Pixel-sample at decoration boundaries (waves, doodles, image overlaps).
- Log to MH โ
nexus_html_logger.py with section ID bsp-aprNN-<page-slug>-figma-shipped, severity success, content includes element count, asset IDs, sha + bytes of functions.php, MH cross-links.
Stop conditions (per BSP CLAUDE.md Rule 6 โ two-failure stop):
- Sanitizer wipes element JSON twice โ STOP and post: walker stdout, REST verify_count, post-write read count, what schema was attempted. Do not scatter alternatives.
- Visual diff still off after 2 deploy iterations โ STOP and ask Audrey to either re-export the section or confirm the live state matches her intent.
๐งต12. Cross-cutting threads
Walker evolution
v1 (Apr 14 14:51, 400 LOC)
โโโ indexed BOTH iPhone and Desktop frames โ confused output
โโโ multi-line TEXT in nav columns shipped as one element
โโโ no nav-column expansion
โโโ superseded 48 min later by v2
v2 (Apr 14 15:39, 366 LOC) โ
active body walker
โโโ Desktop-1 only
โโโ nav column expansion (Services/Pages headers + per-line text-basic)
โโโ tagName: header / nav / footer
โโโ width propagation via percent (child / parent)
โโโ VECTOR wide+thin โ divider element
โโโ SKIP_DESKTOP_ROOT_NAMES = {"wave element 2", "Footer", "wave_background_elements"}
โโโ button frames โ tel:+19139631029 button element
โโโ used in Apr 14 paste-import that shipped 122 elements rendering
v3 (Apr 14 16:05, 502 LOC) โ
active header/footer template builder
โโโ schema-locked per walker_v3_spec.md (248 lines, evidence-classed)
โโโ bare-string widths/paddings ("16" not {unit,value})
โโโ 5-key image shape (id, filename, size, full, url)
โโโ horizontal header layout (logo / nav row / cta) NOT stacked
โโโ 4-col footer (logo+social / Services / Pages / Contact+Hours+BBB)
โโโ tag=header / tag=footer on root section
โโโ BRAND_NAVY/BLUE/YELLOW + PHONE_TEL hardcoded
โโโ outputs to /tmp/header_v3_payload.json + /tmp/footer_v3_payload.json
โโโ POST to /wp-json/bsp/v1/bricks/template-create
Asset upload events (Apr 14-17)
- Apr 14: hero photo, 6 service-card photos, commercial photo, footer logo (asset 149 horizontal-rgb)
- Apr 15:
wave-bg-mid-light-scaled.png (Figma node 612:15 PNG, 2560ร2196)
- Apr 17: 5 new assets โ 150 vertical footer logo, 151-154 trust-bar icons (star/5gen/licensed/ontime) at scale=3 from Figma frame 734:30
Brand drift events (color/spec corrections)
- Apr 14: footer color #D4E9FF (invented) โ #D5EAFF (Figma source)
- Apr 15: wave shape misread as rectangle vs "curve" โ verified via fresh /v1/images, IS 90% rectangle by design
- Apr 17: trust bar emoji-prefix โ 4 custom Figma SVG icons
- Apr 17: reviewer name 15px โ 20px w700 (Robert feedback)
- Apr 17: office hours 7amโ8am Mon-Fri (Audrey delivery, time changed)
๐งน13. Cleanup candidates
| Item | Status | Action |
figma_to_bricks_walker.py (v1, 400 LOC) | superseded | Move to scripts/archive/ after May 1 cutover. v2 is the active body walker. |
/tmp/figma_audrey_v2.json + /tmp/figma_audrey_full.json | stale | Apr 14-17 vintage. Re-pull at session start; never trust cached JSON. |
NAV_URLS placeholders in v3 (/services/, /blog/, etc.) | placeholder | Wire real internal links once page slugs lock in (post-cutover). |
_bricks_template_conditions: [{"main":"0"}] in v3 | inferred | Verify against deployed template-create response in DB; document canonical conditions shape. |
| v2 hardcoded SKIP_DESKTOP_ROOT_NAMES set | load-bearing | Keep โ without it, Desktop root VECTOR shapes leak into flow. |
Plugin URL 991866272578143756 not yet identified | pending | Confirm with Robert next session; populate ยง5. |
walker_v3_spec.md ยง1.2 sticky/position gap | known gap | Decide: encode sticky in element settings (current) vs write _bricks_template_settings. Defer post-cutover. |
๐14. References
Public Figma docs
BSP scripts (cite verbatim before reinventing)
/opt/nexus/nexus/scripts/figma_to_bricks_walker_v2.py โ body walker (366 LOC)
/opt/nexus/nexus/scripts/figma_to_bricks_walker_v3.py โ header/footer template builder (502 LOC)
/opt/nexus/nexus/scripts/walker_v3_spec.md โ schema spec (248 lines, evidence-classed)
/opt/nexus/nexus/scripts/audrey_asset_batch_upload.py โ asset upload helper
/opt/nexus/nexus/scripts/bsp_audrey_photo_workflow.py โ photo intake workflow
/opt/nexus/nexus/config/.env โ FIGMA_TOKEN (perm 600)
MH sections (canonical Figma history)
bsp-apr14-figma-audrey-design-pulled โ first auth + 627 KB pull
bsp-apr14-walker-v3-deployed โ header/footer template ship + render gap flagged
bsp-apr14-footer-figma-api-violation-rebuild โ the "you have figma api access fucker" correction
bsp-apr15-figma-source-verified-wave-is-rectangle โ wave PNG is 90% rectangle, by design
bsp-apr15-figma-to-bricks-v3-generator-SHIPPED-187-elements โ generator built, deployment blocked on auth
bsp-apr15-figma-v3-paste-import-ready-auth-fixed โ auth fix (GaW1 not DAOT) + sanitizer documented
bsp-apr15-real-figma-wave-png-live โ removed fake bg rectangle covering real wave
bsp-apr15-audrey-autolayout-reframe-VERIFIED-match-spec โ autolayout matches spec exactly
bsp-apr16-bricks-23-of-23-checks-pass-wave-faq-spam-fixed โ element ID drift lesson
bsp-apr16-bricks-fuckup-nothing-changed-visually โ LiteSpeed cache miss + producer-as-verifier collapse
bsp-apr17-trust-bar-figma-icons โ 4 custom icons pulled, emoji-prefix text removed
bsp-apr20-memory-backfill-figma-api-first โ canonical "Figma API first" rule
Companion playbooks