feat/energia-ux
needs attentionviewing older commit935ca3d · incrementalPR #301reviewed 2026-07-11 13:00 UTC5H · 7M · 4L · 3I- Purpose
- Define the UX target for the Energía enterprise module — a navigable multi-screen prototype grounded in real customer data so demos are concrete and the code foundation (#299/#300) has an agreed design target.
- Goal
- Ship a self-contained interactive demo of the Energía platform (7 screens: Resumen, Hallazgos, Presupuesto, Pagos CFE, Sitio, Huella de Carbono, Sitios y Atributos) with real data for Grupo Axo and Tiendas Neto, switchable by org, for internal sales use only.
- Sub-goals
- SG-1: Illustrative prototype with psychology-driven design principles (energia-platform-full.html) — landed in prior commit bf33cdf7
- SG-2: Real-data overlay for Axo (266 sites, $45.26M) and Neto (1,847 sites, $309.25M) with org-picker and brief files as source of truth — this commit 935ca3d0
- What
- Added 20 new files under docs/design/energia-prototype/real-data/: brief files (axo-brief.md, neto-brief.md) with production-sourced numbers, assemble2.mjs build script, shell2.html template with org-picker UI, 14 screen fragments (7 per org), and the assembled energia-platform-real.html (3,062 lines, 164 KB).
- Why
- Real numbers make demos concrete and land the ROI story. Illustrative figures were grounded in Axo corpus magnitudes but using actual figures eliminates any 'are these numbers real?' doubt in customer meetings.
- Areas
- docs/design/energia-prototype/real-data+5988−0
- Blast
- 20 files, +5,988 lines, all under docs/design/energia-prototype/real-data/. Zero production code touched. No app builds, no schema changes, no API changes.
Findings · 19
correctness3
assemble2.mjs reads ds.css from wrong path — future re-runs will throw ENOENT
docs/design/energia-prototype/real-data/assemble2.mjs:6
Line 6: `readFileSync(join(D, 'ds.css'), 'utf8')` where D = dirname of assemble2.mjs = `real-data/`. But ds.css lives one level up at `energia-prototype/ds.css`. The committed output is correct (ran once before the path diverged), but any future re-run to update a screen will immediately crash with ENOENT. Fix: `join(D, '..', 'ds.css')` — matches the parent assemble.mjs pattern.
Duplicate SVG linearGradient IDs across both org sets — invalid HTML
docs/design/energia-prototype/real-data/energia-platform-real.html:269
The assemble script strips `id="scr-*"` attributes but misses four SVG linearGradient IDs (`pf`, `bud-real`, `carbonFill`, `site-spend-fill`) that appear in both org sets. The assembled document has duplicate IDs (invalid HTML spec). Currently masked because both orgs use identical gradients (same CSS vars), but will silently break if orgs ever get distinct gradient colors. Fix: rename IDs per org (e.g. `id="pf-axo"`) and patch `fill="url(#...)"` references in the same pass.
No-JS fallback: all .screen elements hidden on load — blank canvas if script fails
docs/design/energia-prototype/real-data/shell2.html:55
No `.screen` element has `class="active"` in source HTML; content is only revealed after `showOrg('axo')` runs on load. The happy path is correct, but a script error leaves a blank page. Low severity for an internal demo, but no-JS fallback is absent.
security7
Real production customer financial + compliance data committed to permanent git history
docs/design/energia-prototype/real-data/axo-brief.md:1
The brief files contain production data for two named enterprise customers: exact 12-month CFE spend ($45.26M Axo / $309.25M Neto), monthly spend series, billing error amounts, pending payment totals ($20.86M for Neto), legal entity names (BASECO SA DE CV, TENNIX SA DE CV, TIENDAS SUPER PRECIO SA DE CV with 1,010 sites + $154.1M), and Neto's RENE regulatory compliance status (34,638 tCO₂e > 25,000 threshold — mandatory COA reporting to SEMARNAT). Git history is permanent: removal requires git-filter-repo + force-push + all-clone invalidation. The repo is private now, but access controls can change.
Assembled HTML (164 KB) embeds all sensitive data in immediately renderable form with no access controls
docs/design/energia-prototype/real-data/energia-platform-real.html:1
energia-platform-real.html contains 3 real RPU numbers (974170601071, 576160500947, 048573920164), all legal entity names in branded context, and all financial aggregates rendered as a professional interactive dossier. It is more dangerous than the source brief files because a person with access to only this file gets full context. No gitignore rule prevents regeneration from re-committing it.
No .gitignore rules for real-data/ — assembled HTML will be re-committed on every assemble run
docs/design/energia-prototype/real-data/assemble2.mjs:28
assemble2.mjs writes to energia-platform-real.html in the same directory. No .gitignore entry exists for the output. Any developer who edits a screen fragment and re-runs the assembler will see the 164KB file as a tracked change ready to commit. Recommended: add `docs/design/energia-prototype/real-data/.gitignore` containing `energia-platform-real.html` to make the output untrackable by default.
Open non-draft PR: GitHub renders the HTML diff inline — all customer data visible to any PR reviewer
docs/design/energia-prototype/real-data/README-real-data.md:7
PR #301 is open (not draft). GitHub's file viewer renders the 3,062-line HTML file as a functional interactive demo when a reviewer clicks on it. If repo access were ever extended to a prospect, partner, or contractor for PR review, the real customer data would be immediately visible. The README warning is a human control; converting to a draft PR until no longer needed is a technical control.
RPU identifiers (real CFE service-point numbers) are not anonymized
docs/design/energia-prototype/real-data/screens-axo/site.html:4
Three real RPUs are embedded: 974170601071 (Axo Corporativo, $1.34M/yr), 576160500947 (Axo CEDIS, $3.93M/yr), 048573920164 (Neto store). RPUs are persistent CFE identifiers that link a legal entity to a specific facility's energy contract. Not PII under LFPDPPP (applies to natural persons), but confidential business identifiers that could be used in due diligence or competitive intelligence.
No XSS or exfiltration risk — HTML is fully static, no external resource loads
docs/design/energia-prototype/real-data/energia-platform-real.html:37
No fetch, XHR, sendBeacon, or external src attributes found. All JS is inline DOM manipulation (textContent, classList). No form submissions. Risk for XSS or data exfiltration from the file itself: none.
Data classification: corporate financial data — LFPDPPP does not directly apply, but confidentiality clauses likely do
docs/design/energia-prototype/real-data/axo-brief.md:1
Data pertains to legal entities, not natural persons. LFPDPPP does not apply. However, disclosure of confidential customer financials likely violates standard B2B SaaS confidentiality clauses. A customer discovering their financial data in a code repository should be treated as a medium-to-high severity incident requiring notification.
conventions3
.branch/scope.md describes feat/mod-gates, not the actual branch work (feat/energia-ux)
.branch/scope.md:1
The branch-provisioning convention expects .branch/scope.md to capture intent for the current branch. The file present on feat/energia-ux still documents feat/mod-gates (entitlement-aware soft-launch gates). Two substantial design commits have landed with no scope.md recording their intent. A minimal scope.md noting the Energía UX design spike, the two orgs demonstrated, and the internal-only constraint would satisfy the convention.
README handling guidance does not address Vercel preview access or permissible sharing channels
docs/design/energia-prototype/real-data/README-real-data.md:7
The README says 'Do not publish the file or the artifact link externally, and do not share this branch outside the team without review.' Given the data includes real RPUs, financial totals, and RENE compliance status, it should also specify: (1) whether Vercel preview deployments of this branch are accessible to unauthenticated viewers; (2) who may receive the demo link (prospects vs. internal only). Closing this gap reduces the risk of accidental external sharing.
PR #301 title predates the real-data commit — reviewers won't know the branch now contains production data
docs/design/energia-prototype/real-data/README-real-data.md:1
PR #301 is titled 'docs(energia): platform design vision — minimal 7-screen prototype'. It was opened for the preceding illustrative commit. Reviewers approving the PR may not notice that real customer data was added in the subsequent commit. Update the PR description to flag this.
tests3
Committed generated artifact can silently drift from source screen fragments — no CI freshness check
docs/design/energia-prototype/real-data/assemble2.mjs:1
energia-platform-real.html is the assembled output but it's committed alongside sources. Editing any screen fragment without re-running the assembler silently diverges the committed file. A lightweight CI step — `node assemble2.mjs && git diff --exit-code docs/design/.../energia-platform-real.html` — would catch drift. Under a second to run. Note: this check would also fail today due to the ds.css path bug.
assemble2.mjs id-rewrite validation is incomplete — inner id= attributes on nested elements are not checked
docs/design/energia-prototype/real-data/assemble2.mjs:15
The regex replaces `id="scr-([a-z]+)"` but does not assert that no other id= attributes remain in fragments. If any screen has inner id= attributes, they'd produce duplicate DOM IDs when both org-sets are inlined. A post-assemble grep assertion (`grep -c 'id="' <html>` per org section) would catch leaks. Current fragments appear clean; low severity.
No test gap for static screen HTML fragments — assemble assertions are the right level
docs/design/energia-prototype/real-data:1
The 14 screen HTML files and shell2.html are pure design artifacts. The assemble2.mjs style/script count checks are the appropriate lightweight self-validation. The only actionable gaps are the broken ds.css path and the missing CI drift check.
improvement3
PR targets main — real customer data will be in permanent main history if merged as-is
docs/design/energia-prototype/real-data/:1
Once merged, axo-brief.md, neto-brief.md, and energia-platform-real.html will be in main's permanent git history with real RPUs, razón social names, and financials. Two structural improvements: (1) add a `.gitignore` in the directory so a future rebased version can't land on main without explicit force-add; (2) consider keeping real-data work on a long-lived non-merging branch (e.g. `demo/real-data`) that is structurally never intended to reach main, making the intent clear rather than relying on reviewer memory.
Org-switch resets to portfolio — breaks demo continuity mid-presentation
docs/design/energia-prototype/real-data/shell2.html:45
showOrg() always calls showScreen('portfolio') after switching. In a live demo, switching orgs mid-presentation drops back to Resumen instead of the equivalent screen. Fix: capture the current screen before switching — `const cur = document.querySelector('.orgset.active .screen.active')?.dataset.scr || 'portfolio'` — and pass it to showScreen after the org toggle. One-line fix.
Filter chip state not reset on org switch — can show stale active chips from prior org
docs/design/energia-prototype/real-data/shell2.html:54
Filter/pill-toggle event listeners attach once at load across both org sets. Active chip state from one org can appear in the other org's equivalent bar after switching. Low risk for a demo (chips start in markup default), but a reset in showOrg() to the first chip in each visible bar would keep state clean.