#24 — Shared Shell + JSON Data Foundation - #24
Merged
Conversation
Splits the registry into eight canonical JSON files under data/portfolio/, one
per domain: meta, profile, socials, projects, recruiter-profiles, build-log,
labs and sinama-evidence.
The content was extracted PROGRAMMATICALLY by evaluating the existing
portfolio-data.js and serializing the live object, not re-typed. Re-typing ~17 KB
of bilingual product truth by hand would have risked exactly the kind of silent
drift this data layer exists to prevent. Punctuation, Turkish characters, arrays,
project proof, recruiter evidence, Labs and the sanitized SINAMA examples all
survive byte-for-byte.
Bilingual values keep the { en, tr } shape they already had, so this changed the
storage format and nothing else.
portfolio-data-model.mjs is the single definition of how those files compose back
into the legacy shape, including key order — the generated artifact is compared
byte-for-byte, so order is part of the contract rather than a formatting
preference. The generator and the QA guard both import it, so the file that ships
and the file that is checked cannot disagree about what correct means.
No `experience` dataset was created. No structured experience data existed in the
registry, and inventing an empty one to match a roadmap word would be fake
completeness. It enters the data layer when there is truthful content to extract.
Verified against baseline 23345d6: 0 semantic differences, key order included.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
portfolio-data.js is now generated from data/portfolio/ and committed. Committed on purpose. GitHub Pages serves repository files directly with no build step, so the legacy production pages must not need CI to boot. The alternative — having the legacy runtime fetch the JSON — was rejected: every consumer (Recruiter Mode, Ajoop, Build Log, Labs, the SINAMA Evidence Explorer) reads window.KAAN_PORTFOLIO synchronously during boot, so fetching would have made the whole legacy runtime asynchronous to solve a problem the generator solves at build time. The runtime contract is unchanged: a classic script assigning a frozen window.KAAN_PORTFOLIO before anything reads it. Shallow freeze preserved to match the previous artifact exactly. No consumer and no page changed. Generation is deterministic — same JSON in, byte-identical file out, verified across repeated runs. The output is emitted with LF; the guard normalizes line endings when comparing, because this repository uses `* text=auto` and a Windows checkout is CRLF, so a raw byte comparison would fail on one platform and pass on the other. Adds data:generate and qa:data. qa:data joins the aggregate qa command first, since every later check reads the registry it guards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the #23 foundation proofs with production-intended components. SiteShell owns the landmark structure — skip link, header, main, footer — plus a `banner` slot. SiteHeader and SiteFooter hold NO route literals: navigation arrives through `navItems` and the brand destination through `brandTo`. That is the single property that lets #25 mount real production routes in these same components without editing them, and qa-react-foundation.js now enforces it. Preview-only chrome moved to components/preview/ and is passed through the banner slot, so removing the preview will touch no shell component. Four primitives, each with more than one caller: Container, Surface, Badge, Action, plus SectionHeading. Action renders whichever element its behavior requires — `to` a router Link, `href` an anchor, `onClick` a button — which is why this system contains no simulated controls. External links get rel="noopener noreferrer" automatically so a caller cannot forget it. React now reads canonical JSON at build time through Vite's @DaTa alias, never by fetching: the pre-render step needs the data while generating HTML, and a runtime fetch would produce empty pre-rendered pages. src/react/data/foundation.js is deleted as the migration plan promised, and shell strings moved to data/i18n/react-shell.json. Identity copy comes from canonical data rather than props, so a page cannot rename the person. Fixed while wiring the preview: recruiter `capabilities` are plain language-neutral strings, not bilingual pairs, and were being rendered through the bilingual helper. The data guard caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the single preview-only stylesheet with six files split by responsibility: tokens, base, typography, layout, components, motion. Semantic tokens throughout. components.css contains no literal color values, which is what makes both themes independently designable rather than one being the other with the lightness inverted. Light is a designed surface system — white cards on a cool paper canvas, real borders, shallow cool shadow — not bleached dark. Two tokens exist because one value could not serve both themes: --accent-text (accent as text, since --accent is tuned for fills and focus rings where contrast rules do not apply) and --text-on-accent (ink on an accent fill). Contrast was measured with alpha compositing against the real backdrop, in both themes, after a real page load rather than a scripted theme toggle — toggling mid-script gave unreliable readings. Three light-theme near-misses were fixed rather than accepted: primary action fill 4.33:1, accent chip 4.43:1, warning chip 4.38:1. Minimums are now 5.10:1 dark and 4.61:1 light. Motion is CSS only, 150–320ms, state feedback exclusively — no entrance animations, no scroll effects, no dependency. Under prefers-reduced-motion the one motion carrying meaning, the interactive surface lift, is removed rather than merely accelerated. The preview is now a labelled specimen of the system — type scale, actions, signals, evidence surfaces, capability chips — rendered from real canonical data. Deliberately not a home page layout; #25 designs the page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
qa-portfolio-data.js is the contract for the new data layer, and blocking in CI. It VERIFIES the committed portfolio-data.js against the canonical JSON and never regenerates it. A stale artifact must fail the build: GitHub Pages serves the committed file directly, so that file has to be correct in the repository, not fixed up in CI where nobody would notice it had drifted. Alongside staleness it enforces the protected truth — Forward Deployed Engineer, the background descriptor, the five canonical social URLs, the bilingual footer positioning — plus bilingual field structure, project id/key agreement, recruiter evidence referring only to projects that exist, unique lab ids, resolvable internal links, and that nothing resembling a local path or credential has entered what is now public data. Five deliberate regressions were injected and each exited non-zero: stale artifact, wrong primary title, non-canonical social URL, recruiter evidence citing a missing project, malformed JSON. All reverted. Writing the guard also caught a real bug: it asserted recruiter `capabilities` were bilingual when they are plain strings, which exposed that the preview was rendering them through the bilingual helper and producing empty entries. The guard was corrected to match the data, and the page was fixed — not the other way round. qa-react-foundation.js drops its foundation.js assumptions and now also proves the shared shell carries no preview assumptions. Its exactly-once social check moved from the whole document to the footer, whose contract it actually is, so a page may legitimately link a canonical destination as well. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
V3_DESIGN_SYSTEM.md records the system as implemented: design objective, visual personality, recruiter-first principles, the dark/light strategy and why two accent tokens exist, the type scale, layout primitives, surfaces, color usage, motion rules, accessibility rules, component principles, forbidden patterns, and how #25 should consume it. PORTFOLIO_ARCHITECTURE.md states the new source-of-truth rule and the workflow it implies — edit JSON, regenerate, commit both — plus why the legacy registry is generated rather than fetched, and why a stale artifact fails instead of being regenerated in CI. REACT_MIGRATION_PLAN.md §8 is rewritten as implemented rather than planned. The locked #23–#33 phase order is unchanged; only the status markers on #23 and #24 were touched. QA_BASELINE.md records measured results: 0 semantic differences against the pre-migration registry with key order included, deterministic generation, the five injected failures, bundle sizes with the #23 comparison as architecture-cost context only, contrast minimums per theme, and the production regression sweep. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
UAJOP
marked this pull request as ready for review
August 23, 2026 21:30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#24 — Shared Shell + JSON Data Foundation
Three things: portfolio truth becomes a canonical JSON layer, the React shell becomes production-intended, and the V3 design system gets its foundation.
No public page is React. Home and About are untouched; that is #25.
Data migration parity
The canonical JSON was extracted programmatically from the live registry — not re-typed — then composed back and compared against baseline
23345d6:Zero differences, key order included. The only intentional change afterwards is the truthful #24 Build Log entry (8 → 9 checkpoints). Nothing was reworded.
experiencewas not created — no structured experience data existed in the registry, and inventing an empty dataset to match a roadmap word would be fake completeness.Legacy adapter
portfolio-data.jsis now generated fromdata/portfolio/and committed. GitHub Pages serves repository files directly, so the legacy pages must not need CI to boot.A runtime
fetchwas rejected deliberately: every consumer — Recruiter Mode, Ajoop, Build Log, Labs, the SINAMA Evidence Explorer — readswindow.KAAN_PORTFOLIOsynchronously during boot, so fetching would have made the entire legacy runtime asynchronous to solve a build-time problem.qa:dataverifies and never regenerates. A stale artifact fails CI, because the committed file is what Pages actually serves.Failure injection
Five deliberate regressions, each reverted. All exited non-zero:
portfolio-data.jsprimaryTitleWriting the guard also caught a real bug: it asserted recruiter
capabilitieswere bilingual when they are plain strings — which exposed that the preview was rendering them through the bilingual helper and producing empty entries. The guard was corrected to match the data and the page was fixed, not the reverse.Shared shell
SiteShell/SiteHeader/SiteFooterhold no route literals. Navigation arrives vianavItems, brand destination viabrandTo. That is the single property letting #25 mount production routes in these same components without editing them — andqa-react-foundation.jsnow enforces it, along with keeping preview chrome out ofcomponents/shell/.Four primitives, each with more than one caller:
Container,Surface,Badge,Action, plusSectionHeading.Actionrenders whichever element its behavior requires (to→ Link,href→ anchor,onClick→ button), which is why the system contains no simulated controls.V3 design system
Six stylesheets split by responsibility.
components.csscontains no literal color values — that is what makes both themes independently designable rather than one being the other inverted.Two tokens exist because one value could not serve both themes:
--accent-text(accent as text, since--accentis tuned for fills and focus rings) and--text-on-accent.Contrast was measured with alpha compositing against the real backdrop, in both themes, after a genuine page load. Three light-theme near-misses were fixed rather than accepted: primary action fill 4.33:1, accent chip 4.43:1, warning chip 4.38:1.
Preview
A labelled specimen of the system — type scale, actions, signals, evidence surfaces, capability chips — rendered from real canonical data (SINAMA, Merge Rush, recruiter focuses). Deliberately not a home page layout.
/react-preview//react-preview/about404.htmlVerified at 1440×900, 1366×768, 768×768, 844×390, 390×844 and 360×800, both themes, EN and TR (long Turkish copy is the overflow worst case): no horizontal overflow anywhere, clean hydration, client nav / back / forward confirmed as real client transitions.
QA
npm run qa:datanpm run qanpm run build:reactnpm run qa:reactnpm run qa:a11y(11 production pages)npm run qa:a11y:react(3 routes)git diff --checkqa:datais blocking and runs first in CI. No existing gate weakened; Lighthouse and the external link scan remain the only report-only checks.Production isolation
git diff --name-only maintouches zero production HTML, CSS or runtime JS. The only production-visible change isportfolio-data.jsbecoming generated while keeping the identical runtime contract, plus the Build Log entry.Verified in-browser across
index,works,games,labs,now,sinama-case-study,merge-rush-case-study: registry boots frozen and synchronous, Recruiter Mode renders registry-backed capabilities, Build Log shows the new entry alongside existing history, Labs renders all four, the SINAMA Evidence Explorer renders fromsinamaEvidence, Command Palette opens, theme and EN/TR behave as before, 5 canonical footer destinations everywhere, 0 broken images.Bundle
316.5 KB raw / 109.0 KB gzip; 1 JS chunk, 1 CSS chunk, 3 HTML files, 1 imported image. No dependency added.
Against #23, as architecture-cost context only: JS gzip +6.5 KB, CSS gzip +2.0 KB, home pre-rendered markup +9.8 KB — the preview gained a design system, a shared shell and real data. Not production cost; no public page is React.
Documentation
V3_DESIGN_SYSTEM.md(new) ·PORTFOLIO_ARCHITECTURE.md(canonical data layer, generated adapter, shared shell) ·REACT_MIGRATION_PLAN.md§8 rewritten as implemented ·README.md,SITE_PREFLIGHT.md,QA_BASELINE.md.Locked #23–#33 roadmap unchanged — only the status markers on #23 and #24.
Deferred
#25 Home + About · #26 Works + Games · #27 Recruiter Mode + Build Log · #28 Ajoop + Command Palette · #29 Case Studies + Dynamic Project Routes · #30 Labs + Mini-game Shells · #31 Legacy Runtime Removal · #32 External Dependency + Bundle Cleanup · #33 Hardening + V3 Final.
🤖 Generated with Claude Code