Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions STEP_2_PORTFOLIO_ECOSYSTEM_VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Step 2 — Portfolio Ecosystem Validation

Validates distinct Projects / Ecosystem navigation against the shared brand's Step 2 changes, using the existing `file:` workflow (no dependency migration, no npm publication). Based on main with the Step 1 Mermaid hotfix already merged; all Step 1 validator assertions preserved.

## Validator additions (`scripts/validate-build.js`)

- Internal `Projects` link must exist and target `/projects/`
- Ecosystem disclosure trigger (button bound to `zc-project-disclosure`): text starts with "Ecosystem", old "Projects" trigger absent — structural, no global string ban
- Disclosure links server-rendered raised to ≥8
- `/projects/fusion-system-blocks/` must continue to show "Public Beta"
- Existing unique-ID and Step 1 Mermaid checks unchanged

## Results

Strict build ✅ · full validator suite ✅ · browser check ✅ — desktop nav renders `Projects · FIRST Robotics · Teaching · Writing & Research · Ecosystem ▾`, disclosure opens with both groups and current-property highlighting, zero console errors, no duplicate IDs.

Merge after the brand Step 2 PR — CI builds against brand main.
16 changes: 15 additions & 1 deletion scripts/validate-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,21 @@ check('drawer trigger has aria-controls', indexHtml.includes('aria-controls="zc-
const discStart = indexHtml.indexOf('id="zc-project-disclosure"');
const discEnd = indexHtml.indexOf('id="zc-mobile-drawer"');
const discBody = discStart !== -1 && discEnd > discStart ? indexHtml.slice(discStart, discEnd) : '';
check('disclosure links server-rendered', (discBody.match(/href="/g) || []).length >= 3);
check('disclosure links server-rendered', (discBody.match(/href="/g) || []).length >= 8, `found ${(discBody.match(/href="/g) || []).length}`);
Comment on lines 141 to +144

// ── Distinct Projects / Ecosystem navigation (Step 2) ────────────────────
// The internal Projects link and the shared Ecosystem disclosure must both
// exist and be clearly distinct. Structural checks — no global string bans
// ("Projects" legitimately appears across the site).
check('internal Projects link targets /projects/', /<a[^>]*href="\/projects\/"[^>]*>Projects/.test(indexHtml));
const ecoTriggerMatch = indexHtml.match(/<button[^>]*aria-controls="zc-project-disclosure"[^>]*>([\s\S]*?)<\/button>/);
const ecoTriggerText = (ecoTriggerMatch?.[1] || '').replace(/<[^>]+>/g, '').trim();
check('ecosystem disclosure trigger labeled Ecosystem', ecoTriggerText.startsWith('Ecosystem'), `got "${ecoTriggerText}"`);
check('old shared Projects trigger absent', !ecoTriggerText.startsWith('Projects'));
Comment on lines +151 to +154

// Fusion System Blocks page must keep its Public Beta status.
const fsbPage = fs.readFileSync(path.join(build, 'projects', 'fusion-system-blocks', 'index.html'), 'utf8');
check('Fusion System Blocks page shows Public Beta', fsbPage.includes('Public Beta'));
Comment on lines +157 to +158
const ids = [...indexHtml.matchAll(/ id="([^"]+)"/g)].map((m) => m[1]);
check('no duplicate ids', ids.length === new Set(ids).size);

Expand Down
Loading