Skip to content

Enhance internationalization and accessibility features#1

Open
mudouasenha wants to merge 89 commits into
mainfrom
rebranding
Open

Enhance internationalization and accessibility features#1
mudouasenha wants to merge 89 commits into
mainfrom
rebranding

Conversation

@mudouasenha
Copy link
Copy Markdown
Owner

@mudouasenha mudouasenha commented Apr 11, 2026

Implement root language detection and redirection, persist user language preferences, and align i18n initialization with routing. Introduce reusable section components and improve accessibility testing infrastructure. Update documentation for phase execution and release readiness.

Summary by CodeRabbit

  • New Features

    • Tokenized design system, Storybook stories, and refreshed hero with clearer CTAs and in-page section anchors.
    • Sticky animated header with mobile drawer and a URL‑synchronized language switcher that remembers choice.
  • Improvements

    • Reduced‑motion support, responsive/mobile-first refinements, and updated typography.
    • Safer bilingual content handling with validation/adapters and locale parity warnings.
  • Tests

    • Added integration and accessibility test suites (Vitest + Playwright/axe) and verification gates.
  • Documentation

    • Extensive planning, milestones, release checklist, and evidence artifacts added.

- add detectPreferredLanguage utility with localStorage and browser fallback
- redirect root route to detected language while keeping wildcard /en fallback
- remove LanguageDetector usage and static lng override from i18n init
- add explicit accessible loading fallback in LangRouter during language sync
- store selected language in localStorage before route navigation
- keep existing URL segment replacement and i18n language sync behavior
- run npm install to create deterministic dependency lockfile
- verify baseline gate with lint and build via verify:baseline
- add Build Baseline Recovery section with deterministic reinstall commands
- require verify:baseline run before marking phase work done
- preserve legacy constant content under src/legacy/constants-legacy.ts
- replace src/constants/index.ts with a minimal deprecated placeholder
- delete console.log output in Projects component render path
- preserve existing projects mapping and UI rendering behavior
- migrate Skills and Technologies imports to motion/react
- remove remaining framer-motion usage from active src files
- map semantic token colors/radii in Tailwind config

- align Vite alias with TypeScript path alias

- make base outline token class Tailwind-compatible
- generate shadcn sheet component under src/components/ui

- generate shadcn navigation-menu component for desktop nav reuse
- attempted preset application once with existing components.json

- no retry performed per deterministic one-attempt policy
- document single b1Z5ezr60 attempt outcome in 02-CONTEXT

- apply bolder cyan/teal semantic token values for fallback baseline
- migrate desktop nav to shadcn navigation-menu hierarchy

- add mobile drawer navigation and keep language switcher utility-level
- add anchored section shell structure for mixed navigation hierarchy

- rebuild hero for quick credibility scan with explicit CTA cluster

- fix navbar social icon imports to keep build green
- executed hardcoded-color grep audit for App, Navbar, and Hero

- confirmed zero banned palette utilities in migrated core files
- align app shell, navbar, and hero to motion/react transition language

- standardize easing and entrance timing for migrated core sections
- disable scroll-reactive navbar state changes when reduced motion is preferred

- keep navigation clarity while removing unnecessary animated state shifts
- approved manual checks on /en and /pt routes

- confirmed reduced-motion path preserves clarity and CTA discoverability
- add high-prominence mobile sheet CTA linking to #contact with visible Let\x27s talk copy
- tune App/Hero mobile spacing and CTA grouping while preserving section anchors/order
- add canonical phase 4 release checklist with command/evidence contract
- mark validation frontmatter as nyquist compliant and wave-0 complete
- add README release-readiness section with phase verification command suite
- replace stale testing posture with actual Vitest/Playwright/axe structure and commands
- document phase-4 polish surfaces in architecture and keep concerns list active-only
- add SC-1, SC-2, and SC-3 under v1 requirements
- map SC requirements to Phase 4 in traceability table
- replace checklist TODO evidence entries with concrete ./evidence paths
- add evidence inventory README including logs, screenshots, and threshold artifact
- replace stale console.log claim in cross-cutting concerns
- document i18n parity warnings as remaining runtime logging path
- run lint/build/integration/a11y/verify-phase3 and store command outputs in evidence logs
- mark checklist build gates and performance threshold row with pass/fail outcomes
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
portfolio Ready Ready Preview, Comment Apr 27, 2026 0:42am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6cf49b5-ccdb-49fb-b153-ea40b8fd7e49

📥 Commits

Reviewing files that changed from the base of the PR and between f9ff7ff and a6d52bb.

📒 Files selected for processing (3)
  • AGENTS.md
  • DESIGN.md
  • README.md
✅ Files skipped from review due to trivial changes (1)
  • DESIGN.md

📝 Walkthrough

Walkthrough

Completed v1.0 modernization: added extensive planning and release artifacts, introduced a Shadcn/Tailwind design system and UI primitives, implemented Zod-backed i18n schemas and non‑throwing adapters, made routing URL‑authoritative with language detection and loading state, refactored many UI sections, and added Vitest + Playwright test infrastructure.

Changes

Cohort / File(s) Summary
Planning & Milestones
.planning/MILESTONES.md, .planning/PROJECT.md, .planning/STATE.md, .planning/REQUIREMENTS.md (removed), .planning/ROADMAP.md, .planning/milestones/*, .planning/phases/*
Added ~40 planning/plan/summary/verification artifacts; archived v1.0, removed standalone REQUIREMENTS.md, updated state/roadmap and many phase-level plans, summaries, validations, and verification reports.
Design System & UI Primitives
components.json, src/components/ui/*, src/components/sections/*, src/lib/utils.ts, tailwind.config.js, src/index.css
Initialized shadcn/radix config, tokenized theme and Tailwind tokens, added UI primitives (Button, NavigationMenu, Sheet), section primitives (SectionShell/Header/Card), and cn() helper; global CSS and theme variables added.
i18n Schemas, Adapters & Parity
src/features/i18n/contentSchemas.ts, src/features/i18n/contentAdapters.ts, src/features/i18n/localeParity.ts, src/features/i18n/detectPreferredLanguage.ts, src/i18n.tsx
Added Zod schemas, non‑throwing adapters (adaptSkills/Projects/Experiences/Certifications/Contact), a locale parity validator, and detectPreferredLanguage(); removed browser-languagedetector wiring.
Routing & Language Loading
src/MainRoutes.tsx, src/LangRouter.tsx, src/App.tsx, src/main.tsx
Root redirect now uses detectPreferredLanguage(), LangRouter shows a Skeleton + aria-live while loading, App restructured with anchored <section id="..."> elements and motion wrappers, and bones registry is imported at startup.
Component Refactors & Data Adaptation
src/components/{About,Hero,Navbar,LanguageSwitcher,Skills,Technologies,Experience,Projects,Contact,Certifications,Tag}.tsx, src/components/sections/*, src/constants/index.ts, src/legacy/constants-legacy.ts
Migrated components to section primitives, consume adapter results with validation-fallback UI, removed runtime legacy constants (moved to legacy snapshot), standardized motion/reduced-motion handling, updated assets and tokenized classes, and removed debug console output.
Testing & Automation
package.json, vitest.config.ts, playwright.config.ts, tests/setup.ts, tests/integration/*.test.ts?(x), tests/a11y/*.a11y.spec.ts
Added Vitest integration tests (i18n routing, locale parity, content adapters, polish contracts), Playwright + @axe-core a11y specs and config, test setup mocks, and npm scripts for storybook, a11y install, and phase verification.
Storybook & Component Stories
.storybook/*, src/components/*.stories.tsx, README.md, docs/plans/*, .planning/codebase/*
Added Storybook config, many component stories, updated README with design/system/release guidance, and expanded planning/codebase docs (testing, architecture, stack).
Build, Tooling & Config
vite.config.ts, tsconfig.json, tsconfig.app.json, eslint.config.js, boneyard.config.json, components.json
Added path aliases (@), boneyard plugin/config and bones registry, ESLint ignores for storybook/playwright/test-results, Vite plugin changes, and updated scripts/dependencies in package.json.
Release Evidence & Artifacts
.planning/phases/04-final-polish-and-release-readiness/RELEASE-CHECKLIST.md, .planning/phases/04-final-polish-and-release-readiness/evidence/*
Added canonical release checklist, evidence bundle (screenshots/logs), Playwright capture script, and performance/reduced-motion evidence notes.
Miscellaneous
.gitignore, index.html, src/bones/*, src/bones/registry.{js,ts}
Updated .gitignore (storybook/playwright/test-results), switched favicon to SVG, added bones layout JSON and registry modules for runtime bones.

Sequence Diagram(s)

sequenceDiagram
    participant User as "User"
    participant Browser as "Browser"
    participant MainRoutes as "MainRoutes"
    participant LangRouter as "LangRouter"
    participant i18n as "i18n"
    participant Adapters as "Adapters"
    participant UI as "UI"

    User->>Browser: Navigate to "/"
    Browser->>MainRoutes: request "/"
    MainRoutes->>Adapters: call detectPreferredLanguage()
    Note right of MainRoutes: detectPreferredLanguage reads\nlocalStorage or navigator
    MainRoutes->>Browser: redirect to "/:lang"
    Browser->>LangRouter: render with :lang
    LangRouter->>i18n: load locale resources
    i18n->>Adapters: bootstrap adapters & schemas
    Adapters->>Adapters: validate payloads (Zod), emit parity warnings
    Adapters-->>i18n: { items, invalidCount, warnings }
    i18n-->>LangRouter: ready
    LangRouter->>UI: render App with section components
    UI->>Adapters: consume adapter results -> render sections or fallback UI
    User->>UI: switch language
    UI->>Browser: persist lang to localStorage + update URL (replace)
    Browser->>LangRouter: re-render with new :lang (repeat loading/validation)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped the repo, tidy and spry,

Tokens planted, schemas high,
Adapters guard each language gate,
Tests march in to check the state,
Bones and sheets now sing — v1.0 is nigh!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rebranding

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (21)
.planning/phases/02-design-system-and-core-ux-migration/02-RESEARCH.md-61-61 (1)

61-61: ⚠️ Potential issue | 🟡 Minor

Fix hyphenation for compound modifier.

At Line 61, use “Vega-style fallback” for correct grammar/readability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/02-design-system-and-core-ux-migration/02-RESEARCH.md at
line 61, Update the compound modifier in the table entry for DSYS-02: replace
the phrase "Vega style fallback" with the hyphenated "Vega-style fallback" so
the cell reading 'Official preset-capable init flow (`--preset [CODE] --template
vite`) plus locked one-attempt fallback protocol.' becomes '... or Vega-style
fallback when preset is unavailable.' Target the table row labeled DSYS-02
(preset `b1Z5ezr60`) and adjust the text accordingly.
src/components/ui/button.tsx-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Fix Slot usage — use Slot instead of Slot.Root.

At line 51, use Slot directly instead of Slot.Root. The radix-ui package correctly exports Slot and the import is valid; only the usage needs correction.

Proposed fix
-  const Comp = asChild ? Slot.Root : "button"
+  const Comp = asChild ? Slot : "button"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/button.tsx` at line 3, The code imports Slot from radix-ui
but incorrectly uses Slot.Root; update the usage to use Slot directly (replace
any occurrences of Slot.Root with Slot) in the Button component (where Slot.Root
is currently used around the content/children) so it matches the imported symbol
and Radix API.
.planning/phases/04-final-polish-and-release-readiness/04-RESEARCH.md-248-260 (1)

248-260: ⚠️ Potential issue | 🟡 Minor

Phase 4 runbook currently points to a Phase 3 verify command.

Lines 248/255/259/260 use npm run verify:phase3 inside the Phase 4 validation contract. Please either switch these to the Phase 4 gate command or explicitly document that Phase 3’s command is intentionally reused for Phase 4 sign-off.

📝 Suggested doc alignment
-| Full suite command | `npm run verify:phase3` |
+| Full suite command | `npm run verify:phase4` |

-| PH4-SC3 | Release docs/checklists match shipped architecture and checks | docs verification + full gate | `npm run verify:phase3` | ✅ (commands), ❌ (Phase 4 checklist doc) |
+| PH4-SC3 | Release docs/checklists match shipped architecture and checks | docs verification + full gate | `npm run verify:phase4` | ✅ (commands), ❌ (Phase 4 checklist doc) |

-- **Per wave merge:** `npm run verify:phase3`
-- **Phase gate:** `npm run verify:phase3` green plus checklist/evidence artifact complete
+- **Per wave merge:** `npm run verify:phase4`
+- **Phase gate:** `npm run verify:phase4` green plus checklist/evidence artifact complete
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-RESEARCH.md around
lines 248 - 260, The Phase 4 runbook incorrectly references `npm run
verify:phase3` for Phase 4 gates and sampling (seen in the table entries and the
"Full suite command" cell); either update those occurrences to the correct Phase
4 gate command (e.g., replace `npm run verify:phase3` with `npm run
verify:phase4` in the "Full suite command" and the PH4 test map rows) or add a
clear note in the Phase 4 section explicitly stating that the Phase 3 verify
command is intentionally reused for Phase 4 sign-off, ensuring the intent is
unambiguous for reviewers and automation users.
.planning/milestones/v1.0-REQUIREMENTS.md-20-20 (1)

20-20: ⚠️ Potential issue | 🟡 Minor

Fix compound adjective formatting at Line 20.

Use “Vega-style fallback” for correct hyphenation and consistency with technical docs style.

✏️ Suggested edit
-- [x] **DSYS-02**: Design contract is enforced by using preset `b1Z5ezr60`, or Vega style fallback when preset is unavailable.
+- [x] **DSYS-02**: Design contract is enforced by using preset `b1Z5ezr60`, or Vega-style fallback when preset is unavailable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/milestones/v1.0-REQUIREMENTS.md at line 20, Update the compound
adjective at the DSYS-02 checklist item so the fallback phrase uses a hyphen:
change "Vega style fallback" to "Vega-style fallback" in the line referencing
preset `b1Z5ezr60` (the DSYS-02 entry) to match technical-docs hyphenation
conventions.
.planning/milestones/v1.0-ROADMAP.md-47-50 (1)

47-50: ⚠️ Potential issue | 🟡 Minor

Phase 2 plan checklist state conflicts with completion status.

Lines 47–50 are unchecked, but the phase is marked complete elsewhere in this file. Please align these checkboxes with the recorded completion state.

✏️ Suggested fix
-- [ ] 02-01: Initialize shadcn and base theme primitives.
-- [ ] 02-02: Validate preset `b1Z5ezr60`; apply Vega fallback if unresolved.
-- [ ] 02-03: Migrate app shell, navigation, and hero.
-- [ ] 02-04: Apply motion conventions and reduced-motion safeguards.
+- [x] 02-01: Initialize shadcn and base theme primitives.
+- [x] 02-02: Validate preset `b1Z5ezr60`; apply Vega fallback if unresolved.
+- [x] 02-03: Migrate app shell, navigation, and hero.
+- [x] 02-04: Apply motion conventions and reduced-motion safeguards.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/milestones/v1.0-ROADMAP.md around lines 47 - 50, The checklist
items for Phase 2 (identifiers 02-01, 02-02, 02-03, 02-04) are left unchecked
while Phase 2 is marked complete elsewhere; update the checkbox states to
reflect the recorded completion by toggling each of the four items to checked
([x]) or, if any truly remain incomplete, update the phase completion marker
instead so they match; ensure you modify the lines containing "02-01: Initialize
shadcn and base theme primitives.", "02-02: Validate preset `b1Z5ezr60`; apply
Vega fallback if unresolved.", "02-03: Migrate app shell, navigation, and
hero.", and "02-04: Apply motion conventions and reduced-motion safeguards."
accordingly.
.planning/milestones/v1.0-REQUIREMENTS.md-100-103 (1)

100-103: ⚠️ Potential issue | 🟡 Minor

Coverage totals are inconsistent with the traceability table.

Lines 100–103 report 19 mapped requirements, but the table includes SC-1, SC-2, and SC-3 as additional mapped entries. Please either adjust counts or exclude SC rows from the mapped total definition.

✏️ One possible correction
 **Coverage:**
-- v1 requirements: 19 total
-- Mapped to phases: 19
-- Unmapped: 0
+- v1 requirements: 19 total
+- Additional phase completion criteria (SC-*): 3
+- Mapped entries total: 22
+- Unmapped: 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/milestones/v1.0-REQUIREMENTS.md around lines 100 - 103, Update the
"Coverage:" summary to match the traceability table: either increment "v1
requirements" and "Mapped to phases" from 19 to include the SC-1, SC-2, SC-3
entries (e.g., 22) or explicitly exclude the SC rows by adding a note that
SC-1/SC-2/SC-3 are not counted in mapped totals; ensure the totals and any
descriptive text in the "Coverage:" block reflect the chosen approach so the
numbers are consistent with the table and SC- rows are not double-counted.
src/components/About.tsx-39-39 (1)

39-39: ⚠️ Potential issue | 🟡 Minor

Use descriptive, localized alt text for the profile image.

Line 39 uses alt="about", which is too generic for assistive tech and not localized. Use an i18n-backed descriptive alt string.

🔧 Proposed fix
-                            <img className="w-full max-w-sm rounded-xl object-cover sm:max-w-md" src={aboutImg} alt="about" />
+                            <img
+                                className="w-full max-w-sm rounded-xl object-cover sm:max-w-md"
+                                src={aboutImg}
+                                alt={t("about.imageAlt", "Portrait photo of Matheus Gomes")}
+                            />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/About.tsx` at line 39, Replace the generic alt text on the
profile image in the About component (the <img> with className "w-full max-w-sm
rounded-xl object-cover sm:max-w-md") with a localized, descriptive string using
your i18n system (e.g., call your translation function like
t('about.profileImageAlt') or similar); update the component to import/use the
relevant translation hook or function (e.g., useTranslation/t) and ensure the
key describes the image (e.g., "Profile photo of [Name]" or a contextual
description) so assistive tech receives a meaningful, localized alt attribute.
src/components/LanguageSwitcher.tsx-32-32 (1)

32-32: ⚠️ Potential issue | 🟡 Minor

Preserve hash fragments during language switch.

Line 32 drops location.hash, so switching language from anchored URLs (e.g. /#contact) loses in-page context.

🔧 Proposed fix
-        navigate(segments.join("/") + location.search, { replace: true });
+        navigate(`${segments.join("/")}${location.search}${location.hash}`, { replace: true });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LanguageSwitcher.tsx` at line 32, The navigate call in
LanguageSwitcher.tsx currently drops the URL hash (it uses
navigate(segments.join("/") + location.search, { replace: true })); update it to
preserve location.hash by appending it when present (e.g., build the target as
segments.join("/") + location.search + (location.hash || "") or a conditional
append) before calling navigate so anchored URLs like /#contact keep their
fragment after a language switch.
.planning/phases/04-final-polish-and-release-readiness/04-VERIFICATION.md-27-27 (1)

27-27: ⚠️ Potential issue | 🟡 Minor

Phase command naming appears stale in the Phase 4 report.

Line 27 lists verify:phase3 inside Phase 4 verification evidence. If Phase 4 has its own verifier command, this can mislead reproducibility steps.

🔧 Proposed fix
-| 6 | Verification commands and QA evidence links are explicit and reproducible. | ✓ VERIFIED | `lint/build/test:integration/test:a11y/verify:phase3` logs are attached under `./evidence/*.log`; threshold artifact is `Status: PASS`. |
+| 6 | Verification commands and QA evidence links are explicit and reproducible. | ✓ VERIFIED | `lint/build/test:integration/test:a11y/verify:phase4` logs are attached under `./evidence/*.log`; threshold artifact is `Status: PASS`. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-VERIFICATION.md at
line 27, The Phase 4 verification entry incorrectly references the Phase 3
verifier command (`verify:phase3`); update the verification command to the
correct Phase 4 verifier (e.g., `verify:phase4`) in the table row so
reproducibility steps point to the right command, and ensure the attached
evidence logs under `./evidence/*.log` and the threshold artifact `Status: PASS`
remain unchanged; locate and edit the string `verify:phase3` in the Phase 4
verification text to the Phase 4 verifier name used by your CI/release pipeline.
README.md-61-84 (1)

61-84: ⚠️ Potential issue | 🟡 Minor

Document rtk before making it part of the required workflow.

These sections switch from plain npm run ... to rtk npm run ..., but the README never explains what rtk is or how to install it. A clean setup following this doc won't be able to execute the release and a11y steps.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 61 - 84, The README uses the `rtk` wrapper in
commands (e.g., "rtk npm run lint", "rtk npm run test:a11y", "rtk npm run
a11y:install-deps") but never documents what `rtk` is or how to
install/configure it; update the Release Readiness and Accessibility Runtime
Dependencies sections to add a short note describing `rtk` (what it is, why we
use it), provide installation instructions or a link to its install docs, and
include any required environment setup or PATH changes so a user can run the
`rtk npm run ...` commands successfully.
src/components/Contact.tsx-67-83 (1)

67-83: ⚠️ Potential issue | 🟡 Minor

Localize these accessible names.

The visible CTA text is translated, but the hardcoded English aria-labels will override it for assistive tech. In pt, this makes the section announce mixed-language actions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Contact.tsx` around lines 67 - 83, The aria-label attributes
in the Contact component are hardcoded English strings and should be localized;
update the anchor elements (the LinkedIn anchor and the GitHub anchor in
src/components/Contact.tsx) to use the translation function (t) instead of fixed
English text (e.g., replace "Contact on LinkedIn (opens in a new tab)" and
"GitHub profile (opens in a new tab)" with localized keys like
t("contact.linkedinAria") and t("contact.githubAria")), ensuring the translation
keys include the "(opens in a new tab)" phrasing or an equivalent accessible
description for each platform.
src/components/Certifications.tsx-30-34 (1)

30-34: ⚠️ Potential issue | 🟡 Minor

Guard the badge image when no asset mapping exists.

If a valid certification ID is missing from imagesMap, this code sets image to "" and still renders <img src="">, which degrades the card and can trigger a request back to the current page.

💡 Suggested fix
     const certifications = adaptedCertifications.map((cert) => {
         const c = new Certification(cert);
-        c.image = imagesMap[c.id] || "";
+        c.image = imagesMap[c.id];
         return c;
     });
@@
-                                <img
-                                    src={certification.image}
-                                    alt={certification.name}
-                                    className="mb-4 h-32 w-auto rounded-lg border border-border p-2"
-                                />
+                                {certification.image ? (
+                                    <img
+                                        src={certification.image}
+                                        alt={certification.name}
+                                        className="mb-4 h-32 w-auto rounded-lg border border-border p-2"
+                                    />
+                                ) : null}

Also applies to: 62-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Certifications.tsx` around lines 30 - 34, The code assigns
c.image = imagesMap[c.id] || "" which yields an empty src and can trigger
unwanted requests; change this to only set c.image when imagesMap has a valid
value (e.g., if (imagesMap[c.id]) c.image = imagesMap[c.id]; leave undefined
otherwise) and update the Certifications component's render (the JSX that
currently renders the <img> for each Certification between the lines around the
certifications mapping and the render block at the section mentioned) to
conditionally render the <img> only when certification.image is truthy (or use a
proper fallback image), referencing the adaptedCertifications -> certifications
mapping, the imagesMap, and the Certification class to locate the code to
change.
src/components/Navbar.tsx-153-159 (1)

153-159: ⚠️ Potential issue | 🟡 Minor

Move the header control labels into i18n.

The social buttons and menu trigger are still announcing hardcoded English labels, so the localized navbar becomes mixed-language for screen-reader users.

Also applies to: 178-178, 210-216

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Navbar.tsx` around lines 153 - 159, The aria-labels for the
social buttons and menu trigger are hardcoded in English; update the Navbar
component to use your i18n translation function (e.g., useTranslation()/t) and
replace hardcoded strings used in Button anchors and the menu trigger
(references: Button, item.label, aria-label and the menu trigger component) with
translation keys (e.g., nav.openInNewTab, nav.menuToggle) and localized
templates so screen-reader labels like "{item.label} (opens in a new tab)" are
produced from i18n; add the new translation keys to your locale files and ensure
the Navbar imports/uses the translation hook at the top so all aria-labels and
any visible control labels are localized.
src/components/Experience.tsx-45-46 (1)

45-46: ⚠️ Potential issue | 🟡 Minor

Use a collision-resistant key for each experience card.

role + company is not unique if the same title/company appears more than once, which can lead to incorrect reconciliation when this list expands or reorders. Include a stable disambiguator such as the period or an adapter-generated id.

Suggested fix
-                            key={`${experience.role}-${experience.company}`}
+                            key={`${experience.role}-${experience.company}-${experience.date}`}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Experience.tsx` around lines 45 - 46, The key on the
motion.div in the Experience component uses
`${experience.role}-${experience.company}`, which can collide; update the key to
include a stable disambiguator such as `experience.period` or an
adapter-generated `experience.id` so each card is collision-resistant (e.g., use
`key={`${experience.role}-${experience.company}-${experience.period}`}` or
`key={experience.id}`). Locate the motion.div in the Experience component and
replace its key expression accordingly.
.planning/phases/03-section-completion-and-quality-hardening/03-05-PLAN.md-51-54 (1)

51-54: ⚠️ Potential issue | 🟡 Minor

Keep execution_context machine-agnostic.

These /home/matheus/... references make the plan non-portable for anyone outside the author’s workstation, which undermines the autonomous: true contract. Please switch this block to repo-relative paths or stable workflow identifiers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-05-PLAN.md
around lines 51 - 54, The execution_context block currently contains absolute,
machine-specific paths (e.g.,
"@/home/matheus/.codex/get-shit-done/workflows/execute-plan.md" and
"@/home/matheus/.codex/get-shit-done/templates/summary.md"); replace these with
repo-relative paths or stable workflow identifiers so the plan is portable and
autonomous — update the execution_context entry to point to relative locations
inside the repo (or canonical workflow/template IDs used by your CI/workflow
system) and ensure the symbols execution_context, execute-plan.md and summary.md
are referenced accordingly.
docs/plans/2026-04-02-ai-execution-focused-portfolio-refresh.md-13-13 (1)

13-13: ⚠️ Potential issue | 🟡 Minor

Fix heading hierarchy to satisfy markdownlint MD001.

These task headers should be ## (not ###) at this level to avoid heading-increment violations.

Also applies to: 67-67, 98-98, 140-140, 181-181

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/plans/2026-04-02-ai-execution-focused-portfolio-refresh.md` at line 13,
Update the section headers that are currently using "### Task ..." to use "##
Task ..." so the task headings (e.g., "Task 1: Add regression test for new
section structure and proof strip" and the other task headers referenced in the
comment) are one level higher and satisfy markdownlint MD001; locate the task
header lines (all "Task N: ..." headings mentioned) and replace the leading
"###" with "##" consistently.
.planning/phases/03-section-completion-and-quality-hardening/03-RESEARCH.md-55-65 (1)

55-65: ⚠️ Potential issue | 🟡 Minor

Fix malformed table termination to avoid markdownlint failures.

Line 65 is being parsed as part of the table block. Add a blank line before </phase_requirements> (and keep leading/trailing pipes) so MD055/MD056/MD058 stops failing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-RESEARCH.md
around lines 55 - 65, The markdown table isn't terminated so the closing tag
`</phase_requirements>` is being parsed as part of the table; fix this by
inserting a single blank line immediately before the `</phase_requirements>` tag
(preserve the existing leading/trailing pipe characters in the table rows) so
the table block ends cleanly and MD055/MD056/MD058 no longer trigger.
src/locales/pt/translation.json-72-72 (1)

72-72: ⚠️ Potential issue | 🟡 Minor

Fix user-facing typos in localized content.

Line 72 has “Artificial Inteligence” and Line 126 has “Typescript”. Both appear in UI-facing strings.

Suggested correction
-      "name": "Artificial Inteligence for Devs",
+      "name": "Artificial Intelligence for Devs",
...
-        "Typescript",
+        "TypeScript",

Also applies to: 126-126

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/locales/pt/translation.json` at line 72, Replace the misspelled UI
strings in the Portuguese locale: correct the "name" value "Artificial
Inteligence for Devs" to "Artificial Intelligence for Devs" (fix the Inteligence
-> Intelligence) and find and correct the other UI string that reads
"Typescript" to "TypeScript" (capital S). Update the translated JSON entries so
the user-facing keys (e.g., the "name" key and the key at the other occurrence)
contain the corrected spellings.
.planning/phases/03-section-completion-and-quality-hardening/03-RESEARCH.md-113-127 (1)

113-127: ⚠️ Potential issue | 🟡 Minor

Add a language label to the fenced code block.

The code fence starting at Line 113 is unlabeled (MD040). Please annotate it (for example, ```text) to keep docs lint clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-RESEARCH.md
around lines 113 - 127, The unlabeled fenced code block that begins with "src/"
needs a language label to satisfy MD040; edit the Markdown in the block showing
the project tree (the lines starting with "src/" and "tests/") and change the
opening fence from ``` to a labeled fence such as ```text (or ```bash) so the
block is annotated while preserving the exact tree content.
src/index.css-1-1 (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Resolve current stylelint blockers in this file.

The static checks flag import notation, font-family quoting, and declaration spacing (Lines 1, 10, 94-96, 102). These should be fixed to keep lint/build gates deterministic.

Suggested fixes
-@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap");
+@import "https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Manrope:wght@400;500;600;700;800&display=swap";

:root {
-    font-family: "Manrope", sans-serif;
+    font-family: Manrope, sans-serif;
}

Also applies to: 10-10, 94-96, 102-102

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.css` at line 1, Fix stylelint issues by normalizing the `@import` and
font-family declarations and spacing: change the `@import` to use
url('https://fonts.googleapis.com/...') with single quotes and a trailing
semicolon; replace double-quoted font-family values with either unquoted family
names for single-word families or single-quoted multi-word names (e.g.,
Cormorant Garamond, Manrope) and ensure commas are followed by a single space;
and normalize declaration spacing so each property uses a single space after the
colon and no extra spaces before the semicolon in the font-family rules
referenced around lines 10, 94-96 and 102.
src/features/i18n/contentAdapters.ts-22-26 (1)

22-26: ⚠️ Potential issue | 🟡 Minor

Module-level side effect logs warnings on import.

console.warn executes immediately when this module is imported, which:

  • Makes testing harder (warnings fire during test setup)
  • Could spam logs during SSR/build processes
  • Reduces control over when parity checks run

Consider deferring logging to an explicit initialization function or lazy evaluation.

Proposed refactor
 export const structuredLocaleParity = validateStructuredLocaleParity(enLocale, ptLocale);
 
-structuredLocaleParity.unknownKeyWarnings.forEach((message) => {
-  console.warn(message.startsWith(PARITY_WARNING_PREFIX) ? message : `${PARITY_WARNING_PREFIX} ${message}`);
-});
+export function logParityWarnings(): void {
+  structuredLocaleParity.unknownKeyWarnings.forEach((message) => {
+    console.warn(message.startsWith(PARITY_WARNING_PREFIX) ? message : `${PARITY_WARNING_PREFIX} ${message}`);
+  });
+}

Then call logParityWarnings() explicitly during app initialization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/i18n/contentAdapters.ts` around lines 22 - 26, The module
currently logs parity warnings at import time by iterating
structuredLocaleParity.unknownKeyWarnings with console.warn; move that
side-effect into an explicit function (e.g., export function
logParityWarnings()) that reads structuredLocaleParity (created via
validateStructuredLocaleParity(enLocale, ptLocale)) and performs the prefixed
console.warn logic using PARITY_WARNING_PREFIX, then remove the top-level
iteration so imports are side-effect free and callers can invoke
logParityWarnings() during app initialization or tests when appropriate.
🧹 Nitpick comments (18)
vite.config.ts (1)

1-11: Use absolute filesystem paths for resolve.alias for cross-platform reliability.

Vite's official documentation states that relative alias values like '/src' will not be resolved into filesystem paths. Use absolute filesystem paths instead:

Proposed diff
 import { defineConfig } from 'vite'
 import react from '@vitejs/plugin-react-swc'
 import { boneyardPlugin } from 'boneyard-js/vite'
+import { fileURLToPath, URL } from 'node:url'
@@
   resolve: {
     alias: {
-      '@': '/src',
+      '@': fileURLToPath(new URL('./src', import.meta.url)),
     },
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vite.config.ts` around lines 1 - 11, Replace the relative alias value '/src'
with an absolute filesystem path: import a resolver (e.g., import { resolve }
from 'path' or use new URL('./src', import.meta.url)) and change the
resolve.alias entry for '@' to resolve(__dirname, 'src') or the equivalent
URL-based absolute path so Vite gets a proper filesystem path; update the
resolve.alias object where '@' is currently set to '/src'.
src/legacy/constants-legacy.ts (1)

1-5: Enforce “no runtime import” with tooling, not comments alone.

Line 3 states this must not be imported by runtime code, but placing it under src/ still leaves accidental import risk. Consider moving it under docs/planning assets or adding an explicit restricted-import rule for this module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/legacy/constants-legacy.ts` around lines 1 - 5, The file exporting
LEGACY_CONSTANTS_NOT_IN_USE is currently under source and can be accidentally
imported at runtime; either move the content out of compiled sources into a
docs/planning asset (e.g., a .md or docs folder) so it is not part of the build,
or add a lint rule (ESLint no-restricted-imports or equivalent) that explicitly
forbids importing the module that exports LEGACY_CONSTANTS_NOT_IN_USE and
include a clear rule message; update any README/comments to point to the new
docs location if you move it.
tests/a11y/homepage.a11y.spec.ts (1)

14-18: Use a stable, intent-specific locator instead of .rounded-full.

This selector is very generic and can accidentally bind to unrelated elements, making the wait condition flaky. Prefer a dedicated test id or a semantic locator for the specific badge.

♻️ Suggested direction
-  await page.waitForFunction(() => {
-    const badge = document.querySelector(".rounded-full");
-    if (!badge) return true;
-    return Number.parseFloat(getComputedStyle(badge).opacity || "1") >= 0.99;
-  });
+  const badge = page.getByTestId("hero-status-badge");
+  await expect(badge).toBeVisible();
+  await expect.poll(async () => {
+    return await badge.evaluate((el) =>
+      Number.parseFloat(getComputedStyle(el).opacity || "1"),
+    );
+  }).toBeGreaterThanOrEqual(0.99);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/a11y/homepage.a11y.spec.ts` around lines 14 - 18, The wait uses a
generic CSS selector ".rounded-full" in page.waitForFunction which can match
unrelated elements; update the selector to a stable, intent-specific locator
(e.g., a dedicated data attribute or semantic attribute) and adjust the wait
predicate to query that locator (replace document.querySelector(".rounded-full")
with something like document.querySelector('[data-testid="your-badge"]') or an
aria/role-based selector) so page.waitForFunction and the badge opacity check
target the exact badge element consistently; keep the opacity check and return
logic unchanged but reference the new locator string where ".rounded-full" is
used.
tests/integration/final-polish-task1.test.ts (1)

13-15: Class-contract assertions are overly brittle to formatting/order changes.

Current toContain checks can fail on harmless class reordering. Consider asserting token presence independently (or with order-insensitive regex) to keep the contract strict but stable.

♻️ Suggested refactor
-    expect(aboutSource).toContain("ring-1 ring-primary/20");
-    expect(aboutSource).toContain("rounded-2xl border border-border bg-card");
+    expect(aboutSource).toMatch(/\bring-1\b/);
+    expect(aboutSource).toMatch(/\bring-primary\/20\b/);
+    expect(aboutSource).toMatch(/\brounded-2xl\b/);
+    expect(aboutSource).toMatch(/\bborder-border\b/);
+    expect(aboutSource).toMatch(/\bbg-card\b/);

-    expect(tagSource).toContain("bg-accent");
-    expect(tagSource).toContain("text-accent-foreground");
+    expect(tagSource).toMatch(/\bbg-accent\b/);
+    expect(tagSource).toMatch(/\btext-accent-foreground\b/);

Also applies to: 20-21

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/final-polish-task1.test.ts` around lines 13 - 15, The
assertions in final-polish-task1.test.ts that inspect aboutSource (the
expectations using toContain for "ring-1 ring-primary/20" and "rounded-2xl
border border-border bg-card") are brittle to class order/formatting; change
these to assert the presence of each CSS token independently (e.g., check that
aboutSource includes "ring-1", "ring-primary/20", "rounded-2xl", "border",
"border-border", "bg-card") or use an order-insensitive regex/word-boundary
checks so the tests validate tokens rather than exact substring order — update
the expectations that reference aboutSource (and the similar assertions at lines
20-21) accordingly.
tests/a11y/responsive-smoke.spec.ts (1)

26-26: Make the mobile-menu selector locale-aware.

Line 26 hardcodes "Open navigation menu". If accessible names are localized, the PT scenarios can fail despite correct behavior. Prefer a locale-specific regex (or a stable test id reserved for tests).

🔧 Example refactor
-    const menuButton = page.getByRole("button", { name: "Open navigation menu" });
+    const menuButtonName =
+      scenario.locale === "pt"
+        ? /open navigation menu|abrir menu de navegação/i
+        : /open navigation menu/i;
+    const menuButton = page.getByRole("button", { name: menuButtonName });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/a11y/responsive-smoke.spec.ts` at line 26, The selector for the mobile
menu is hardcoded to the English accessible name in the line that assigns
menuButton using page.getByRole("button", { name: "Open navigation menu" });
update this to be locale-aware by replacing the literal name with either a
locale-specific regex (e.g., a RegExp that matches expected translations) or,
preferably, switch to a stable test-only identifier (e.g., a data-testid or
aria-describedby used for tests) and use page.getByRole("button", { name:
/your-regex-here/i }) or page.getByTestId("mobile-menu") accordingly so tests
work across locales; target the menuButton assignment and any related references
to ensure consistency.
tests/integration/final-polish-task2.test.ts (1)

22-24: Strengthen these assertions to reduce false positives.

Current checks can pass with partial token usage (OR regex) or import-only hook presence. Consider asserting both tokens explicitly where required and matching hook invocation (useReducedMotion() instead of raw substring.

Suggested tightening
-    expect(appSource).toMatch(/--motion-duration-medium|--motion-ease-standard/);
-    expect(heroSource).toMatch(/--motion-duration-medium|--motion-ease-standard/);
-    expect(navbarSource).toMatch(/--motion-duration-medium|--motion-ease-standard/);
+    expect(appSource).toContain("--motion-duration-medium");
+    expect(appSource).toContain("--motion-ease-standard");
+    expect(heroSource).toContain("--motion-duration-medium");
+    expect(heroSource).toContain("--motion-ease-standard");
+    expect(navbarSource).toContain("--motion-duration-medium");
+    expect(navbarSource).toContain("--motion-ease-standard");

-    expect(heroSource).toContain("useReducedMotion");
-    expect(navbarSource).toContain("useReducedMotion");
+    expect(heroSource).toMatch(/\buseReducedMotion\s*\(/);
+    expect(navbarSource).toMatch(/\buseReducedMotion\s*\(/);

Also applies to: 31-32

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/final-polish-task2.test.ts` around lines 22 - 24, The
assertions on appSource, heroSource, and navbarSource are too loose (using an OR
regex) and can pass with partial tokens or mere imports; update each expectation
to assert both CSS tokens are present (require both --motion-duration-medium and
--motion-ease-standard) and additionally assert the reduced-motion hook is
actually invoked by matching useReducedMotion( instead of just a substring;
apply the same tightening to the other similar assertions noted (lines 31-32) so
all checks require both tokens and the hook invocation.
.planning/phases/03-section-completion-and-quality-hardening/03-03-SUMMARY.md (1)

54-56: Optional readability polish for accomplishment bullets.

Line 54, Line 55, and Line 56 all begin with “Added”; consider varying phrasing to make the section scan faster.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.planning/phases/03-section-completion-and-quality-hardening/03-03-SUMMARY.md
around lines 54 - 56, The three accomplishment bullets beginning with "Added"
(the lines describing "Added a reusable Vitest integration runtime and
`test:integration` command.", "Added route/language continuity coverage for `/`,
invalid lang fallback, and language switching.", and "Added locale parity and
content adapter integration tests for required keys and malformed payload
safety.") are repetitive; reword them to vary the leading verbs for readability
(for example, use "Introduce", "Cover", "Include" or "Implement"), keep the same
technical details and backticks, and ensure consistent tense and parallel
structure so the three bullets scan faster.
.planning/phases/02-design-system-and-core-ux-migration/02-01-PLAN.md (1)

61-63: Use repo-relative execution references for portability.

Lines 61–63 embed machine-specific absolute paths (/home/matheus/...). Prefer repo-relative docs/paths so the plan is reproducible across contributors and CI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/02-design-system-and-core-ux-migration/02-01-PLAN.md around
lines 61 - 63, The plan file embeds machine-specific absolute paths (e.g.
"@/home/matheus/.codex/get-shit-done/workflows/execute-plan.md" and
"@/home/matheus/.codex/get-shit-done/templates/summary.md" inside the
execution_context block); replace these with repo-relative references (for
example "./workflows/execute-plan.md" or "docs/templates/summary.md" or simply
"workflows/execute-plan.md" depending on repo layout) so the execution_context
entries are portable for other contributors and CI; update the lines containing
the absolute "@/home/matheus/..." entries to the chosen repo-relative paths and
verify links resolve in the repository.
.planning/phases/03-section-completion-and-quality-hardening/03-VALIDATION.md (1)

62-62: Prefer deterministic bootstrap command over raw install chain.

Line 62 hardcodes ad-hoc install commands; this can drift from committed dependency/lockfile state. Consider replacing it with a single repo-owned bootstrap script (or npm ci + scripted install step) to keep validation reproducible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
@.planning/phases/03-section-completion-and-quality-hardening/03-VALIDATION.md
at line 62, Replace the ad-hoc install chain on the checklist with a
deterministic repo-owned bootstrap step: create or use an NPM script named
"bootstrap" in package.json that runs npm ci and any reproducible install steps
(or a dedicated script file), then update the checklist entry in
03-VALIDATION.md to reference running npm run bootstrap (or npm ci && npm run
bootstrap) instead of the raw npm install commands; this ensures validation uses
the committed lockfile and a single, reproducible command (refer to the
checklist line and the "bootstrap" script name when making changes).
playwright.config.ts (1)

26-26: Use !process.env.CI for reuseExistingServer to prevent stale-server false positives in CI.

Setting reuseExistingServer: true unconditionally reuses servers in both local and CI environments. This is problematic in CI where a stale server can cause false test failures. According to Playwright's official documentation, the recommended pattern is !process.env.CI, which reuses servers locally (avoiding unnecessary restarts during development) while forcing fresh server creation in CI (ensuring reliable, isolated test execution).

Suggested change
   webServer: {
     command: "npm run dev -- --host 127.0.0.1 --port 4173",
     url: "http://127.0.0.1:4173",
-    reuseExistingServer: true,
+    reuseExistingServer: !process.env.CI,
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright.config.ts` at line 26, The Playwright config currently sets
reuseExistingServer: true unconditionally; change it to use the CI env flag so
local runs reuse servers but CI always starts a fresh one by replacing the value
with !process.env.CI (update the reuseExistingServer entry in the Playwright
config where the symbol reuseExistingServer is defined).
src/App.tsx (1)

58-62: Consider memoizing CSS variable reads.

getMotionDurationMedium() and getMotionEaseStandard() are called on every render despite reading values that rarely change. If CSS custom properties are static after initial load, wrapping these in useMemo avoids redundant getComputedStyle calls.

♻️ Optional memoization
 function App() {
     const reduceMotion = useReducedMotion();
-    const motionDurationMedium = getMotionDurationMedium();
-    const motionEaseStandard = getMotionEaseStandard();
+    const motionDurationMedium = useMemo(() => getMotionDurationMedium(), []);
+    const motionEaseStandard = useMemo(() => getMotionEaseStandard(), []);

This requires importing useMemo from React. If theme changes should trigger re-reads, add a theme context value to the dependency array instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 58 - 62, The calls to getMotionDurationMedium() and
getMotionEaseStandard() in App cause repeated getComputedStyle reads on every
render; wrap both calls in React's useMemo to memoize their results (import
useMemo) so they only run once (use [] as dependency) or include a theme/context
value in the dependency array if you want re-reads when theme changes; update
the references in App to use the memoized values while leaving
useReducedMotion() unchanged.
tests/integration/ai-portfolio-structure.test.ts (1)

12-59: These assertions are too tightly coupled to source layout.

Reading raw files and checking literal JSX/copy here will fail on harmless refactors that preserve behavior. Prefer rendering the app/components and asserting the resulting landmarks, links, and translated headings instead of source tokens and exact prose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/ai-portfolio-structure.test.ts` around lines 12 - 59, The
tests are too coupled to raw source tokens (readSource/appSource) and literal
JSX strings; instead render the actual components and assert on DOM landmarks,
links, ordering and localized text. Replace readSource usages with rendering App
or specific components via testing-library/react (render(<App />) or
render(<Navbar />)/render(<Hero />)), then: assert navigation exists with
screen.getByRole('navigation') and query anchors with href "#about", "#skills"
etc; assert sections exist and order by selecting elements by id
(document.querySelectorAll('section') or container.querySelectorAll) and
comparing their index positions for ids "about","skills","experience", etc;
assert hero content using screen.getByText for the localized keys
(hero.proofStrip, hero.subtitle or the translated strings) instead of matching
raw file strings; and for locale checks, verify rendered translations (render a
component wrapped with i18n provider) rather than asserting literal
enLocale/ptLocale source tokens. Use testing-library cleanup utilities and keep
assertions on behavior/DOM not source layout.
src/components/Hero.tsx (1)

72-75: Consider adding type guards to locale array access.

While current locale files are valid, the rendering at lines 137-153 uses array items directly as both React keys and content (key={item} and {item}). If a locale file accidentally includes objects or non-strings, this would either produce duplicate [object Object] keys or render incorrectly. The current Array.isArray() check is too permissive.

Consider filtering to ensure only strings are used:

Suggested improvement
-    const highlights = Array.isArray(heroHighlights) ? heroHighlights : [];
-    const proofStrip = Array.isArray(heroProofStrip) ? proofStrip : [];
+    const highlights = Array.isArray(heroHighlights)
+        ? heroHighlights.filter((item): item is string => typeof item === "string")
+        : [];
+    const proofStrip = Array.isArray(heroProofStrip)
+        ? heroProofStrip.filter((item): item is string => typeof item === "string")
+        : [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Hero.tsx` around lines 72 - 75, The locale arrays
heroHighlights and heroProofStrip are only checked with Array.isArray, which
allows non-string entries that later get used as React keys and rendered
content; update how highlights and proofStrip are derived by filtering the
arrays to include only string values (e.g., map/Array.filter with typeof item
=== "string") so components using highlights and proofStrip (and any map
rendering using key={item} / {item}) receive only strings; alternatively, when
mapping, guard each item with a type check and fall back to a stable index-based
key or skip non-strings to avoid duplicate or "[object Object]" keys and
incorrect rendering.
.planning/phases/03-section-completion-and-quality-hardening/03-CONTEXT.md (1)

14-23: Consider normalizing heading structure inside custom blocks for lint stability.

Current heading parsing appears to trigger MD001 in this section; flattening to consistent markdown heading levels (or plain bullets) will keep docs lint clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-CONTEXT.md
around lines 14 - 23, The markdown triggers MD001 due to inconsistent heading
levels inside the "Section Migration Contract" block; update the headings inside
that custom block (e.g., the "Section Migration Contract" title and the
sub-items for "Technologies" and "Skills") so they use a consistent heading
level (or replace nested headings with plain bullets) to flatten the structure
and avoid mixed heading depths; ensure "Technologies" and "Skills" remain
distinct entries but formatted uniformly to satisfy the linter.
.planning/phases/04-final-polish-and-release-readiness/04-VALIDATION.md (1)

23-24: Phase 04 validation doc still labels the full gate as verify:phase3.

This is likely stale wording and can mislead execution during release readiness. Prefer phase-neutral naming in this doc, or point to the actual Phase 04 gate command.

Also applies to: 32-33, 69-71

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-VALIDATION.md
around lines 23 - 24, Update the Phase 04 validation doc to remove the stale
`npm run verify:phase3` reference and replace it with the correct Phase 04 gate
command or a phase-neutral pointer; search for occurrences of the exact string
`npm run verify:phase3` (notably in the table and at the other locations
mentioned) and change them to `npm run verify:phase4` or to a neutral phrase
like "Full suite command: see release gating docs" so the doc no longer points
to a previous phase's command.
.planning/phases/04-final-polish-and-release-readiness/04-04-PLAN.md (1)

100-100: Make heading verification regex resilient to markdown heading syntax.

Line 100 currently matches only raw Signals/Threshold/Evidence lines and may false-fail if the file uses #/## headings.

Suggested update
- ... rtk rg -n "^Signals$|^Threshold$|^Evidence$|^Status: (PASS|FAIL)$" ...
+ ... rtk rg -n "^(#+\\s*)?Signals$|^(#+\\s*)?Threshold$|^(#+\\s*)?Evidence$|^Status: (PASS|FAIL)$" ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-04-PLAN.md at line
100, The current grep pattern rtk rg -n
"^Signals$|^Threshold$|^Evidence$|^Status: (PASS|FAIL)$" is brittle against
markdown headers; update it to allow optional leading '#' characters and
whitespace so headings like "# Signals" or "## Evidence" match. Replace the
pattern with one that uses an optional heading prefix and groups, e.g. matching
"^(#+\s*)?(Signals|Threshold|Evidence)\s*$" and a similar form for status like
"^(#+\s*)?Status:\s*(PASS|FAIL)\s*$" in the automated command so the
performance-threshold.md heading checks pass regardless of markdown header
syntax.
.planning/phases/04-final-polish-and-release-readiness/04-01-PLAN.md (1)

56-59: Local absolute paths reduce portability.

The execution_context references absolute paths (/home/matheus/.codex/...) which won't work for other contributors. Consider using repository-relative paths or environment variables if this workflow needs to be portable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-01-PLAN.md around
lines 56 - 59, The execution_context block currently contains absolute
user-specific paths which break portability; update the execution_context
entries to use repository-relative paths or environment variables (e.g., replace
"/home/matheus/.codex/..." with relative paths like "./.codex/..." or use
${HOME} or a configurable variable) so the workflow can run for other
contributors; locate the execution_context key in this file and update both
referenced paths accordingly and, if needed, add a note or variable placeholder
for contributors to set their own base path.
src/features/i18n/contentAdapters.ts (1)

83-83: Parity warnings are duplicated in every adapter result.

Each call to adaptArray or adaptContact copies all structuredLocaleParity.unknownKeyWarnings into its result. If multiple adapters are called (skills, projects, experiences, etc.), the same parity warnings appear in each result, potentially causing duplicate display or logging.

Consider separating parity warnings from per-adapter unknown-key warnings, or deduplicating at the consumer level.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/i18n/contentAdapters.ts` at line 83, The adapters (functions
like adaptArray and adaptContact) each copy
structuredLocaleParity.unknownKeyWarnings into a local unknownKeyWarnings array
causing duplicate parity warnings across results; change the adapters so they do
not unconditionally copy the full structuredLocaleParity.unknownKeyWarnings into
each adapter result — instead either (a) remove that copy and return only
adapter-specific warnings from adaptArray/adaptContact, aggregating
structuredLocaleParity.unknownKeyWarnings once at a higher level, or (b) keep
the per-adapter warnings but deduplicate by merging
structuredLocaleParity.unknownKeyWarnings with adapter-specific warnings and
removing duplicates before returning; target the assignments that create const
unknownKeyWarnings: string[] = [...structuredLocaleParity.unknownKeyWarnings]
and adjust them accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9236346-1fad-4613-b100-a0d94433d90b

📥 Commits

Reviewing files that changed from the base of the PR and between 69b9f1e and 00bf3ad.

⛔ Files ignored due to path filters (14)
  • .planning/phases/04-final-polish-and-release-readiness/evidence/a11y.log is excluded by !**/*.log
  • .planning/phases/04-final-polish-and-release-readiness/evidence/about-image-framing.png is excluded by !**/*.png
  • .planning/phases/04-final-polish-and-release-readiness/evidence/build.log is excluded by !**/*.log
  • .planning/phases/04-final-polish-and-release-readiness/evidence/hero-desktop.png is excluded by !**/*.png
  • .planning/phases/04-final-polish-and-release-readiness/evidence/integration.log is excluded by !**/*.log
  • .planning/phases/04-final-polish-and-release-readiness/evidence/lint.log is excluded by !**/*.log
  • .planning/phases/04-final-polish-and-release-readiness/evidence/mobile-nav-390.png is excluded by !**/*.png
  • .planning/phases/04-final-polish-and-release-readiness/evidence/mobile-nav-sheet.png is excluded by !**/*.png
  • .planning/phases/04-final-polish-and-release-readiness/evidence/verify-phase3.log is excluded by !**/*.log
  • package-lock.json is excluded by !**/package-lock.json
  • public/favicon.svg is excluded by !**/*.svg
  • src/assets/MatheusGomesProfileMain.jpg is excluded by !**/*.jpg
  • src/assets/certifications/branas_badge.png is excluded by !**/*.png
  • src/assets/mg-mark.svg is excluded by !**/*.svg
📒 Files selected for processing (119)
  • .gitignore
  • .planning/MILESTONES.md
  • .planning/PROJECT.md
  • .planning/REQUIREMENTS.md
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/codebase/ARCHITECTURE.md
  • .planning/codebase/CONCERNS.md
  • .planning/codebase/TESTING.md
  • .planning/milestones/v1.0-REQUIREMENTS.md
  • .planning/milestones/v1.0-ROADMAP.md
  • .planning/phases/01-baseline-stabilization/01-01-SUMMARY.md
  • .planning/phases/01-baseline-stabilization/01-02-SUMMARY.md
  • .planning/phases/01-baseline-stabilization/01-03-SUMMARY.md
  • .planning/phases/01-baseline-stabilization/01-VERIFICATION.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-01-PLAN.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-02-PLAN.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-03-PLAN.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-04-PLAN.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-CONTEXT.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-RESEARCH.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-VALIDATION.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-VERIFICATION.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-design-system-and-core-ux-migration-01-SUMMARY.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-design-system-and-core-ux-migration-02-SUMMARY.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-design-system-and-core-ux-migration-03-SUMMARY.md
  • .planning/phases/02-design-system-and-core-ux-migration/02-design-system-and-core-ux-migration-04-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-01-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-01-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-02-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-02-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-03-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-03-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-04-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-04-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-05-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-05-SUMMARY.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-06-PLAN.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-CONTEXT.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-RESEARCH.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-VALIDATION.md
  • .planning/phases/03-section-completion-and-quality-hardening/03-VERIFICATION.md
  • .planning/phases/04-final-polish-and-release-readiness/04-01-PLAN.md
  • .planning/phases/04-final-polish-and-release-readiness/04-01-SUMMARY.md
  • .planning/phases/04-final-polish-and-release-readiness/04-02-PLAN.md
  • .planning/phases/04-final-polish-and-release-readiness/04-02-SUMMARY.md
  • .planning/phases/04-final-polish-and-release-readiness/04-03-PLAN.md
  • .planning/phases/04-final-polish-and-release-readiness/04-03-SUMMARY.md
  • .planning/phases/04-final-polish-and-release-readiness/04-04-PLAN.md
  • .planning/phases/04-final-polish-and-release-readiness/04-04-SUMMARY.md
  • .planning/phases/04-final-polish-and-release-readiness/04-05-PLAN.md
  • .planning/phases/04-final-polish-and-release-readiness/04-05-SUMMARY.md
  • .planning/phases/04-final-polish-and-release-readiness/04-CONTEXT.md
  • .planning/phases/04-final-polish-and-release-readiness/04-RESEARCH.md
  • .planning/phases/04-final-polish-and-release-readiness/04-VALIDATION.md
  • .planning/phases/04-final-polish-and-release-readiness/04-VERIFICATION.md
  • .planning/phases/04-final-polish-and-release-readiness/RELEASE-CHECKLIST.md
  • .planning/phases/04-final-polish-and-release-readiness/evidence/.tmp/capture-phase04-evidence.mjs
  • .planning/phases/04-final-polish-and-release-readiness/evidence/README.md
  • .planning/phases/04-final-polish-and-release-readiness/evidence/performance-threshold.md
  • .planning/phases/04-final-polish-and-release-readiness/evidence/reduced-motion-note.md
  • README.md
  • boneyard.config.json
  • components.json
  • docs/plans/2026-04-02-ai-execution-focused-portfolio-refresh.md
  • eslint.config.js
  • index.html
  • package.json
  • playwright.config.ts
  • src/App.tsx
  • src/LangRouter.tsx
  • src/MainRoutes.tsx
  • src/bones/hero-section.bones.json
  • src/bones/registry.js
  • src/bones/registry.ts
  • src/components/About.tsx
  • src/components/Certifications.tsx
  • src/components/Contact.tsx
  • src/components/Experience.tsx
  • src/components/Hero.tsx
  • src/components/LanguageSwitcher.tsx
  • src/components/Navbar.tsx
  • src/components/Projects.tsx
  • src/components/Skills.tsx
  • src/components/Tag.tsx
  • src/components/Technologies.tsx
  • src/components/sections/SectionCard.tsx
  • src/components/sections/SectionHeader.tsx
  • src/components/sections/SectionShell.tsx
  • src/components/ui/button.tsx
  • src/components/ui/navigation-menu.tsx
  • src/components/ui/sheet.tsx
  • src/constants/index.ts
  • src/features/i18n/contentAdapters.ts
  • src/features/i18n/contentSchemas.ts
  • src/features/i18n/detectPreferredLanguage.ts
  • src/features/i18n/localeParity.ts
  • src/i18n.tsx
  • src/index.css
  • src/legacy/constants-legacy.ts
  • src/lib/utils.ts
  • src/locales/en/translation.json
  • src/locales/pt/translation.json
  • src/main.tsx
  • tailwind.config.js
  • tests/a11y/homepage.a11y.spec.ts
  • tests/a11y/mobile-nav.a11y.spec.ts
  • tests/a11y/responsive-smoke.spec.ts
  • tests/integration/ai-portfolio-structure.test.ts
  • tests/integration/content-adapters.test.ts
  • tests/integration/final-polish-task1.test.ts
  • tests/integration/final-polish-task2.test.ts
  • tests/integration/i18n-routing.test.tsx
  • tests/integration/locale-parity.test.ts
  • tests/setup.ts
  • tsconfig.app.json
  • tsconfig.json
  • vite.config.ts
  • vitest.config.ts
💤 Files with no reviewable changes (1)
  • .planning/REQUIREMENTS.md

Comment on lines +46 to +49
<execution_context>
@/home/matheus/.codex/get-shit-done/workflows/execute-plan.md
@/home/matheus/.codex/get-shit-done/templates/summary.md
</execution_context>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid machine-specific execution paths in committed plans.

These references only resolve on one workstation, so the plan becomes unusable for anyone else running it. Prefer repo-relative paths or a logical workflow identifier that can be resolved consistently across environments.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/02-design-system-and-core-ux-migration/02-02-PLAN.md around
lines 46 - 49, The execution_context block contains machine-specific absolute
paths; update the execution_context entries to use repo-relative paths or
logical workflow identifiers (e.g., workflows/execute-plan.md and
templates/summary.md or a named workflow ID) so the plan is portable; locate the
<execution_context> section in the .planning file and replace the absolute paths
with repository-relative references or a resolution-friendly identifier for each
workflow/template.

Comment on lines +53 to +56
<execution_context>
@/home/matheus/.codex/get-shit-done/workflows/execute-plan.md
@/home/matheus/.codex/get-shit-done/templates/summary.md
</execution_context>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use portable execution-context references.

These absolute home-directory paths will break for every contributor who is not on the same machine. Commit a portable reference instead of a local filesystem path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/02-design-system-and-core-ux-migration/02-04-PLAN.md around
lines 53 - 56, The execution_context block contains absolute home-directory
paths that are not portable; update the execution_context entry to remove
machine-specific paths and use repository-relative or template tokens instead
(for example, replace the absolute home paths with repo-root tokens or relative
paths such as "@/workflows/execute-plan.md" or "./templates/summary.md"),
ensuring the execution_context references are portable for all contributors;
edit the execution_context lines in the same file to use these
repository-relative references.

- Test 2: Contact section has one primary action button and secondary text links.
- Test 3: Every outbound project/contact link exposes disclosure text and opens in a new tab.
</behavior>
<action>In `Projects.tsx`, keep each project card as a full clickable anchor and add an explicit visible action label `View project (opens in a new tab)` inside each card; keep `target=\"_blank\"` and `rel=\"noopener noreferrer\"`. In `Contact.tsx`, add one primary `Button` CTA labeled `Contact on LinkedIn` pointing to `https://www.linkedin.com/in/matheus-gomes-98823b185` with `target=\"_blank\"` and `rel=\"noopener noreferrer\"`. Add secondary text links for `GitHub` (`https://github.com/mudouasenha`) and `Email` (`mailto:contact.me@linkedin`), each with disclosure copy `(opens in a new tab)` for external links.</action>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use a valid mailto: target in the action contract.

Line 142 currently specifies mailto:contact.me@linkedin, which is not a valid email address and will produce a broken contact action if implemented as written.

Suggested correction
- ... and `Email` (`mailto:contact.me@linkedin`), each with disclosure copy ...
+ ... and `Email` (`mailto:<valid-email-address>`), each with disclosure copy ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-01-PLAN.md
at line 142, The mailto action in the plan is invalid—update the Contact.tsx
email link (the email anchor/secondary text link referenced under the Contact
component) to use a real mailto: address (e.g.,
mailto:your-actual-email@example.com) instead of mailto:contact.me@linkedin;
keep the disclosure copy “(opens in a new tab)” and preserve target="_blank" and
rel="noopener noreferrer" on the email anchor just like the GitHub link and the
primary Button ("Contact on LinkedIn").

Comment on lines +58 to +61
<execution_context>
@/home/matheus/.codex/get-shit-done/workflows/execute-plan.md
@/home/matheus/.codex/get-shit-done/templates/summary.md
</execution_context>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace workstation-specific execution context paths.

Hard-coding /home/matheus/... here makes the plan non-runnable outside that exact environment. Please switch these references to repo-relative paths or stable workflow names.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/03-section-completion-and-quality-hardening/03-06-PLAN.md
around lines 58 - 61, The execution_context block contains workstation-specific
absolute paths (/home/matheus/.codex/get-shit-done/workflows/execute-plan.md and
/home/matheus/.codex/get-shit-done/templates/summary.md); change these to
repo-relative paths or stable workflow identifiers so the plan is portable —
update the execution_context entries to point to ./workflows/execute-plan.md and
./templates/summary.md (or to the canonical workflow names used by your CI)
ensuring the symbols execution_context and the two referenced path strings are
replaced accordingly.

Comment on lines +94 to +96
<verify>
<automated>rtk rg -n "No direct `console\\.log` calls are present in `src/components/Projects\\.tsx`" .planning/codebase/ARCHITECTURE.md && ! rtk rg -n "Direct `console\\.log` present in `src/components/Projects\\.tsx`" .planning/codebase/ARCHITECTURE.md && ! rtk rg -n "console\\.log" src/components/Projects.tsx</automated>
</verify>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Test if the pattern matches the expected ARCHITECTURE.md content
rg -n "No direct.*console\.log.*calls are present in" .planning/codebase/ARCHITECTURE.md 2>/dev/null || echo "Pattern not found or file missing"

Repository: mudouasenha/portfolio

Length of output: 224


🏁 Script executed:

# Test if the unescaped backticks in the pattern would cause command substitution errors
bash -c 'rg -n "No direct `console\\.log` calls are present in" .planning/codebase/ARCHITECTURE.md' 2>&1 | head -20

Repository: mudouasenha/portfolio

Length of output: 109


Fix backtick escaping in the verification command on line 95.

The unescaped backticks within the double-quoted pattern string trigger shell command substitution, causing the verification to fail with "command not found" errors. Escape the backticks as ``` or use single quotes to prevent substitution:

rtk rg -n "No direct \`console\\.log\` calls are present in \`src/components/Projects\\.tsx\`" .planning/codebase/ARCHITECTURE.md
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.planning/phases/04-final-polish-and-release-readiness/04-03-PLAN.md around
lines 94 - 96, The verification command string in the automated check (the rtk
rg -n "No direct `console\.log` calls are present in
`src/components/Projects\.tsx`" ...) contains unescaped backticks which cause
shell command substitution; update that automated pattern to either escape
backticks (e.g., \`) or switch the outer quoting to single quotes so the
backticks are treated literally, ensuring the pattern for "No direct
`console\.log` calls are present in `src/components/Projects\.tsx`" is matched
correctly by the rtk rg invocation.

Comment on lines +56 to +59
{index === 0 ? (
<span className="mt-3 inline-flex rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-primary">
{t("experienceCurrentRole")}
</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t infer the current role from array position.

This badge is attached to the first adapted item, not to explicit data. If the locale payload or adapter order changes, a past role will be labeled as current. Please drive this from a real current/endDate signal instead of index === 0.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Experience.tsx` around lines 56 - 59, The badge is incorrectly
shown based on array position (index === 0) in Experience.tsx; update the
rendering to use explicit data on each experience item (e.g., an
experience.current boolean or an absent/null endDate) instead of index. Locate
the JSX that renders the badge (the span currently guarded by index === 0) and
change the condition to something like experience.current === true or
!experience.endDate (and update any type/interface for the experience prop to
include current?: boolean or endDate?: string|null). Ensure the adapter that
maps incoming locale payloads sets that field so the UI drives the badge from
explicit data rather than array order.

Comment on lines +61 to +66
<motion.a
key={featuredProject.id}
href={featuredProject.url}
target="_blank"
rel="noopener noreferrer"
aria-label={`View project: ${featuredProject.title} (opens in a new tab)`}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Localize the accessible strings too.

These aria-label and alt values are hardcoded in English, so /pt will still announce English link/image text even though the visible UI is translated. Please move them behind i18n keys the same way the visible CTA copy is handled.

Example direction
-                        aria-label={`View project: ${featuredProject.title} (opens in a new tab)`}
+                        aria-label={t("projectsLinkAriaLabel", { title: featuredProject.title })}
...
-                                    alt={`${featuredProject.title} preview`}
+                                    alt={t("projectsImageAlt", { title: featuredProject.title })}

Apply the same pattern to the supporting project cards and add matching locale keys.

Also applies to: 76-80, 104-109, 118-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Projects.tsx` around lines 61 - 66, The aria-label and image
alt strings on the project links/cards are hardcoded in English; update the code
that renders motion.a (for featuredProject) and the supporting project card
components to use i18n keys the same way the visible CTA copy does: add locale
keys for the link label and image alt (e.g., project.linkLabel and
project.imageAlt per project or a formatted key that accepts the project title),
replace the hardcoded aria-label and alt attributes with calls to the i18n
translate function used elsewhere (same pattern as the CTA), and add matching
entries to the locale files for each supported language so the accessible
strings are localized for featuredProject and the supporting cards referenced in
the diff.

Comment thread src/index.css
Comment on lines +14 to +17
.theme {
--font-heading: "Cormorant Garamond", serif;
--font-sans: "Manrope", sans-serif;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n --iglob '*.tsx' --iglob '*.ts' --iglob '*.html' '\btheme\b'
rg -n 'var\(--font-sans\)|var\(--font-heading\)' src/index.css

Repository: mudouasenha/portfolio

Length of output: 144


🏁 Script executed:

cat -n src/index.css | head -120

Repository: mudouasenha/portfolio

Length of output: 5342


🏁 Script executed:

rg -n 'className.*theme|class.*theme|\.theme\s*{' src/

Repository: mudouasenha/portfolio

Length of output: 210


🏁 Script executed:

git ls-files | grep -E '\.(tsx?|jsx?|html)$' | head -20

Repository: mudouasenha/portfolio

Length of output: 530


🏁 Script executed:

rg -n "className=.*theme|class=.*theme" --type tsx --type ts --type html --type jsx

Repository: mudouasenha/portfolio

Length of output: 92


🏁 Script executed:

cat -n src/App.tsx

Repository: mudouasenha/portfolio

Length of output: 4883


🏁 Script executed:

rg -n "\.theme\b" src/ -A 2 -B 2

Repository: mudouasenha/portfolio

Length of output: 272


🏁 Script executed:

rg -i "theme" src/ --type-add 'web:*.tsx;*.ts;*.jsx;*.html' --type web

Repository: mudouasenha/portfolio

Length of output: 47


🏁 Script executed:

cat -n index.html

Repository: mudouasenha/portfolio

Length of output: 563


Move font variables to :root since .theme class is never applied.

The --font-heading and --font-sans variables defined in .theme (lines 14-17) are consumed at lines 97 and 108, but the .theme class is never mounted on any element. This causes typography to fall back to browser defaults (serif and sans-serif) instead of using the intended Cormorant Garamond and Manrope fonts.

Suggested fix
`@layer` base {
- .theme {
-       --font-heading: "Cormorant Garamond", serif;
-       --font-sans: "Manrope", sans-serif;
-   }
  :root {
+       --font-heading: "Cormorant Garamond", serif;
+       --font-sans: "Manrope", sans-serif;
        --background: oklch(0.97 0.012 85);
        --foreground: oklch(0.23 0.02 45);
        ...
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.theme {
--font-heading: "Cormorant Garamond", serif;
--font-sans: "Manrope", sans-serif;
}
`@layer` base {
:root {
--font-heading: "Cormorant Garamond", serif;
--font-sans: "Manrope", sans-serif;
--background: oklch(0.97 0.012 85);
--foreground: oklch(0.23 0.02 45);
...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/index.css` around lines 14 - 17, The custom font CSS variables
(--font-heading and --font-sans) are currently declared under the .theme class
which is never applied, so move those variable declarations to :root (instead of
.theme) so they are globally available where consumed (references to
--font-heading and --font-sans used elsewhere); update or remove the .theme
block accordingly to avoid duplicate definitions.

Comment thread src/LangRouter.tsx
Comment on lines +31 to +46
if (!ready) {
return (
<Skeleton
name="language-route-shell"
loading
animate="pulse"
fallback={
<div aria-live="polite" role="status">
Loading language...
</div>
}
>
<div className="min-h-screen" />
</Skeleton>
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect unresolved changeLanguage promise chains lacking catch handlers
rg -n --type=tsx 'changeLanguage\([^)]*\)\.then\(' -C2

Repository: mudouasenha/portfolio

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Locate and read the LangRouter.tsx file
find . -name "LangRouter.tsx" -type f

Repository: mudouasenha/portfolio

Length of output: 85


🏁 Script executed:

#!/bin/bash
# Read the LangRouter.tsx file to verify the code context
cat -n "src/LangRouter.tsx" | head -100

Repository: mudouasenha/portfolio

Length of output: 1753


🏁 Script executed:

#!/bin/bash
# Search for changeLanguage calls in the codebase (using correct file type)
rg -n 'changeLanguage' --type ts

Repository: mudouasenha/portfolio

Length of output: 307


Add error handling to prevent permanent loading state when language switch fails.

At line 17, the i18n.changeLanguage(lang) promise chain lacks a .catch() handler. If the language change is rejected, ready stays false indefinitely, leaving the skeleton loading state (lines 31–46) stuck on-screen. Add rejection handling and reset ready before starting the async switch.

Proposed fix
   useEffect(() => {
     if (lang && SUPPORTED_LANGS.includes(lang)) {
+      setReady(false);
       if (i18n.language !== lang) {
-        i18n.changeLanguage(lang).then(() => setReady(true));
+        i18n
+          .changeLanguage(lang)
+          .then(() => setReady(true))
+          .catch(() => setReady(true));
       } else {
         setReady(true);
       }
     } else {
       setReady(false);
     }
   }, [lang, i18n]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/LangRouter.tsx` around lines 31 - 46, The ready flag can remain false
forever if i18n.changeLanguage(lang) rejects; update the language-switching flow
in LangRouter.tsx to set ready to false immediately before starting the async
change and attach a .catch handler on i18n.changeLanguage(lang) that resets
ready to true (or a safe fallback state) and logs the error; ensure the success
path still sets ready=true after the promise resolves so the Skeleton (loading
state) is cleared on both success and failure.

Comment thread tsconfig.json
Comment on lines +2 to +7
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current placement of sourceMap in tsconfig.json
jq '{top_level_sourceMap: .sourceMap, compilerOptions_sourceMap: .compilerOptions.sourceMap}' tsconfig.json

Repository: mudouasenha/portfolio

Length of output: 135


Move sourceMap into compilerOptions (current placement violates TypeScript schema).

sourceMap is currently defined at the top level of tsconfig.json, but the TypeScript compiler expects it under compilerOptions. This is invalid configuration and must be corrected.

Proposed fix
 {
   "compilerOptions": {
     "baseUrl": ".",
     "paths": {
       "@/*": ["./src/*"]
-    }
+    },
+    "sourceMap": true
   },
   "files": [],
   "references": [
     { "path": "./tsconfig.app.json" },
     { "path": "./tsconfig.node.json" }
-  ],
-  "sourceMap": true
+  ]
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"sourceMap": true
},
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tsconfig.json` around lines 2 - 7, The tsconfig.json has the "sourceMap"
property at the top level which violates the TypeScript schema; move the
"sourceMap" key (and its boolean value) inside the existing "compilerOptions"
object so it becomes compilerOptions.sourceMap, removing the top-level entry;
ensure the key name is exactly "sourceMap" and set to true/false as intended.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (3)
src/components/Certifications.tsx (1)

12-18: ⚠️ Potential issue | 🟠 Major

Format dates from the app locale and avoid UTC date shifts.

navigator.language can disagree with the selected i18n language, and new Date("YYYY-MM-DD") can render the previous day in local time zones. Parse date-only values as local dates and pass the active i18n language.

🐛 Proposed fix
-const formatDate = (date: string) => {
-    const userLocale = navigator.language || "en-US";
-    return new Intl.DateTimeFormat(userLocale, {
+const formatDate = (date: string, language: string) => {
+    const [year, month, day] = date.split("-").map(Number);
+    const parsedDate =
+        Number.isInteger(year) && Number.isInteger(month) && Number.isInteger(day)
+            ? new Date(year, month - 1, day)
+            : new Date(date);
+    const locale = language.startsWith("pt") ? "pt-BR" : "en-US";
+
+    return new Intl.DateTimeFormat(locale, {
         year: "numeric",
         month: "long",
         day: "numeric",
-    }).format(new Date(date));
+    }).format(parsedDate);
 };
@@
 const Certifications = () => {
-    const { t } = useTranslation();
+    const { t, i18n } = useTranslation();
@@
-                                <strong className="text-foreground">{formatDate(certification.date)}</strong>
+                                <strong className="text-foreground">
+                                    {formatDate(certification.date, i18n.resolvedLanguage ?? i18n.language)}
+                                </strong>

Also applies to: 26-30, 77-80

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Certifications.tsx` around lines 12 - 18, The formatDate
function currently uses navigator.language and new Date(date) which can mismatch
the app i18n locale and cause UTC shifts for "YYYY-MM-DD" strings; change
formatDate to accept or read the active i18n locale (e.g., use i18n.language
instead of navigator.language) and parse date-only strings explicitly as local
dates by detecting a /^\d{4}-\d{2}-\d{2}$/ pattern and constructing the Date
with new Date(year, monthIndex, day) (otherwise fall back to Date constructor),
then pass the i18n locale to Intl.DateTimeFormat; apply the same parsing/locale
fixes to the other format usages referenced around the earlier occurrences
(lines ~26-30 and ~77-80) such that all date-only inputs are parsed as local and
all formatters use the app's active i18n language.
src/components/Projects.tsx (1)

61-80: ⚠️ Potential issue | 🟠 Major

Localize the project link labels and image alt text.

The visible project CTA is translated, but the aria-label and alt text still hard-code English, so Portuguese pages expose mixed-language accessible names.

♿ Proposed direction
-                        aria-label={`View project: ${featuredProject.title} (opens in a new tab)`}
+                        aria-label={t("projectsLinkAriaLabel", { title: featuredProject.title })}
...
-                                    alt={`${featuredProject.title} preview`}
+                                    alt={t("projectsImageAlt", { title: featuredProject.title })}
...
-                            aria-label={`View project: ${project.title} (opens in a new tab)`}
+                            aria-label={t("projectsLinkAriaLabel", { title: project.title })}
...
-                                    alt={`${project.title} preview`}
+                                    alt={t("projectsImageAlt", { title: project.title })}

Add matching en/pt locale keys.

Also applies to: 104-122

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Projects.tsx` around lines 61 - 80, In Projects.tsx update the
hard-coded English accessibility strings to use localized keys: replace the
aria-label on the motion.a (currently `View project: ${featuredProject.title}
(opens in a new tab)`) and the img alt (currently `${featuredProject.title}
preview`) with values pulled from your i18n/locales (add en/pt keys like
"project.viewLabel" and "project.previewAlt" that accept the project title),
then use the same localized keys for the duplicated block around lines 104-122
so both the CTA aria-label and image alt are returned in the active locale
rather than hard-coded English.
src/components/Experience.tsx (1)

67-75: ⚠️ Potential issue | 🟠 Major

Drive the “current role” badge from explicit data, not array position.

index === 0 couples the badge and highlight styling to locale ordering. Add an explicit current/endDate signal to the schema/adapter and render from that instead.

🐛 Proposed direction
-                            <SectionCard className={index === 0 ? "border-primary/20 bg-primary/[0.045] p-5 sm:p-6" : "p-5 sm:p-6"}>
+                            <SectionCard className={experience.current ? "border-primary/20 bg-primary/[0.045] p-5 sm:p-6" : "p-5 sm:p-6"}>
...
-                                        {index === 0 ? (
+                                        {experience.current ? (

This also requires adding the field to the experience schema, locale payloads, and adaptExperiences.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Experience.tsx` around lines 67 - 75, The badge and highlight
are currently driven by index === 0 causing locale-ordering bugs; update the
data flow to use an explicit "current" (or absent endDate) flag in the
experience object instead: add a boolean current (or ensure endDate is
null/undefined) to the experience schema and locale payloads, update
adaptExperiences to set current based on endDate or source data, and change the
rendering in Experience.tsx (where SectionCard, displayPeriod and the
current-role span are used) to test experience.current (or !experience.endDate)
instead of index === 0 and apply the border-primary/bg-primary styles
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.storybook/preview.tsx:
- Around line 50-68: The decorator currently calls i18n.changeLanguage inline
during render (i18n.changeLanguage, Story, context.globals.locale); refactor by
extracting a Wrapper component that uses a useEffect to asynchronously call
i18n.changeLanguage(String(context.globals.locale ?? "en")) and tracks
completion in state (e.g., isReady), then render the themed container and <Story
/> only after isReady is true; replace the inline decorator with this Wrapper so
language switching is performed as a side effect and not during render,
preventing flicker and repeated calls on unrelated re-renders.

In `@package.json`:
- Line 48: Remove the platform-specific Rollup binary dependency by deleting the
"@rollup/rollup-linux-x64-gnu" entry from devDependencies in package.json;
Rollup handles platform-native packages as optional dependencies so removing
this direct dependency prevents install failures on macOS, Windows, ARM, and
Alpine while leaving Rollup's cross-platform behavior intact.

In `@src/components/Contact.tsx`:
- Around line 65-84: The aria-labels for the outbound links are hardcoded in
English; update the anchor elements inside the Contact component (the
Button-asChild LinkedIn anchor and the GitHub <a>) to use localized keys (e.g.,
t("contact.linkedinAriaLabel") and t("contact.githubAriaLabel")) instead of
fixed strings, and add matching entries for contact.linkedinAriaLabel and
contact.githubAriaLabel to both locale files (en and pt) so screen readers get
translated labels that correspond to the visible CTAs (contact.linkedinCta,
contact.githubCta).

In `@src/components/Hero.tsx`:
- Around line 142-144: The CTA opens a new tab but its accessible name lacks a
new-tab cue; add an aria-label using the localized string (e.g.
t("hero.primaryCtaAriaLabel")) to the anchor inside the Button (the <a
href={resumeUrl} ... /> element) so screen readers announce the new-tab
behavior, and add corresponding keys "hero.primaryCtaAriaLabel" to both locale
files with the text including the new-tab cue (e.g. "View résumé (opens in a new
tab)").

In `@src/components/Navbar.tsx`:
- Around line 113-118: Replace hardcoded accessible names in Navbar.tsx with
localized strings and add matching keys in both locale files: use the
translation function (e.g., t or useTranslation) for the logo alt text (replace
current alt="Matheus Gomes logo" with t('navigation.logoAlt')), social link
labels (replace visible/aria-label text for social anchors with
t('navigation.social.<name>')), the menu button label (use
t('navigation.menuButton') and aria-expanded handling on the menu toggle), and
the external resume CTA (use t('navigation.resume') and include the new-tab
context in the localized string, then ensure the resume link has target="_blank"
rel="noopener noreferrer" and an aria-label that includes the external/new-tab
notice via the translated key). Add the corresponding navigation.* keys in both
locale files (e.g., navigation.logoAlt, navigation.menuButton,
navigation.social.github, navigation.resume) so translations for /pt and others
are present.

In `@src/locales/en/translation.json`:
- Around line 94-105: The new nested keys added to the "contact" object (kicker,
summary, title, body, locationLabel, responseLabel, linkedinCta, githubCta)
violate the strict adapter schema for contact (contactSchema.strict()) which
currently permits only address, phoneNo, and email; either move these copy
strings out of the "contact" object into a different top-level translations key
or extend the allowed keys in the adapter/schema to include each new field.
Locate the "contact" JSON object in translation.json and either relocate the new
fields to another translations namespace (e.g., contactCopy.*) or update the
schema/enforcement that references contactSchema.strict() to accept the
additional property names (kicker, summary, title, body, locationLabel,
responseLabel, linkedinCta, githubCta) so the payload passes validation.

In `@src/locales/pt/translation.json`:
- Around line 77-83: The "name" field for the certificate with id
"ai-for-devs-branas" contains a typo ("Artificial Inteligence"); update the
"name" value to "Artificial Intelligence" to correct spelling while leaving
other fields (id, issued_by, date, description, url) unchanged so the entry
remains consistent.

---

Duplicate comments:
In `@src/components/Certifications.tsx`:
- Around line 12-18: The formatDate function currently uses navigator.language
and new Date(date) which can mismatch the app i18n locale and cause UTC shifts
for "YYYY-MM-DD" strings; change formatDate to accept or read the active i18n
locale (e.g., use i18n.language instead of navigator.language) and parse
date-only strings explicitly as local dates by detecting a /^\d{4}-\d{2}-\d{2}$/
pattern and constructing the Date with new Date(year, monthIndex, day)
(otherwise fall back to Date constructor), then pass the i18n locale to
Intl.DateTimeFormat; apply the same parsing/locale fixes to the other format
usages referenced around the earlier occurrences (lines ~26-30 and ~77-80) such
that all date-only inputs are parsed as local and all formatters use the app's
active i18n language.

In `@src/components/Experience.tsx`:
- Around line 67-75: The badge and highlight are currently driven by index === 0
causing locale-ordering bugs; update the data flow to use an explicit "current"
(or absent endDate) flag in the experience object instead: add a boolean current
(or ensure endDate is null/undefined) to the experience schema and locale
payloads, update adaptExperiences to set current based on endDate or source
data, and change the rendering in Experience.tsx (where SectionCard,
displayPeriod and the current-role span are used) to test experience.current (or
!experience.endDate) instead of index === 0 and apply the
border-primary/bg-primary styles accordingly.

In `@src/components/Projects.tsx`:
- Around line 61-80: In Projects.tsx update the hard-coded English accessibility
strings to use localized keys: replace the aria-label on the motion.a (currently
`View project: ${featuredProject.title} (opens in a new tab)`) and the img alt
(currently `${featuredProject.title} preview`) with values pulled from your
i18n/locales (add en/pt keys like "project.viewLabel" and "project.previewAlt"
that accept the project title), then use the same localized keys for the
duplicated block around lines 104-122 so both the CTA aria-label and image alt
are returned in the active locale rather than hard-coded English.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1f02d8c-7d62-482d-aaa2-14181778fbe6

📥 Commits

Reviewing files that changed from the base of the PR and between 00bf3ad and f9ff7ff.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (36)
  • .gitignore
  • .planning/codebase/STACK.md
  • .planning/codebase/STRUCTURE.md
  • .storybook/main.ts
  • .storybook/preview.tsx
  • README.md
  • eslint.config.js
  • package.json
  • playwright.config.ts
  • src/bones/hero-section.bones.json
  • src/components/About.tsx
  • src/components/Certifications.stories.tsx
  • src/components/Certifications.tsx
  • src/components/Contact.stories.tsx
  • src/components/Contact.tsx
  • src/components/Experience.stories.tsx
  • src/components/Experience.tsx
  • src/components/Hero.stories.tsx
  • src/components/Hero.tsx
  • src/components/LanguageSwitcher.stories.tsx
  • src/components/LanguageSwitcher.tsx
  • src/components/Navbar.stories.tsx
  • src/components/Navbar.tsx
  • src/components/Projects.stories.tsx
  • src/components/Projects.tsx
  • src/components/Skills.stories.tsx
  • src/components/Skills.tsx
  • src/components/Technologies.stories.tsx
  • src/components/Technologies.tsx
  • src/components/sections/SectionCard.stories.tsx
  • src/components/sections/SectionHeader.stories.tsx
  • src/components/sections/SectionShell.stories.tsx
  • src/components/sections/SectionShell.tsx
  • src/components/ui/button.stories.tsx
  • src/locales/en/translation.json
  • src/locales/pt/translation.json
✅ Files skipped from review due to trivial changes (16)
  • .gitignore
  • .storybook/main.ts
  • src/components/Technologies.stories.tsx
  • .planning/codebase/STRUCTURE.md
  • .planning/codebase/STACK.md
  • src/components/LanguageSwitcher.stories.tsx
  • src/components/Certifications.stories.tsx
  • src/components/Skills.stories.tsx
  • eslint.config.js
  • src/bones/hero-section.bones.json
  • src/components/sections/SectionHeader.stories.tsx
  • src/components/Projects.stories.tsx
  • src/components/sections/SectionShell.stories.tsx
  • src/components/Contact.stories.tsx
  • src/components/sections/SectionCard.stories.tsx
  • src/components/Hero.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/components/sections/SectionShell.tsx
  • src/components/LanguageSwitcher.tsx
  • playwright.config.ts
  • src/components/About.tsx
  • README.md

Comment thread .storybook/preview.tsx
Comment on lines +50 to +68
decorators: [
(Story, context) => {
void i18n.changeLanguage(String(context.globals.locale ?? "en"));

return (
<div
className={[
"theme min-h-screen bg-background p-6 text-foreground",
context.globals.theme === "dark" ? "dark" : "",
]
.filter(Boolean)
.join(" ")}
>
<div className="mx-auto w-full max-w-6xl">
<Story />
</div>
</div>
);
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expect: no direct i18n.changeLanguage(...) call inside the decorator render body.
rg -nP -C3 'changeLanguage\s*\(' .storybook src

Repository: mudouasenha/portfolio

Length of output: 1288


Move changeLanguage out of the render path.

Line 52 triggers an async i18n mutation while Storybook is rendering, causing stories to briefly render with the previous locale and repeating the side effect on unrelated re-renders.

Extract a wrapper component that moves language switching into useEffect and renders the story only after the language change settles.

♻️ Proposed refactor
 import type { Preview } from "@storybook/react-vite";
+import { type ReactNode, useEffect, useState } from "react";

 import "../src/index.css";
 import i18n from "../src/i18n";
 
+type StoryFrameProps = {
+  locale: string;
+  theme: string;
+  children: ReactNode;
+};
+
+const StoryFrame = ({ locale, theme, children }: StoryFrameProps) => {
+  const [ready, setReady] = useState(
+    () => i18n.resolvedLanguage === locale || i18n.language === locale,
+  );
+
+  useEffect(() => {
+    let cancelled = false;
+
+    setReady(i18n.resolvedLanguage === locale || i18n.language === locale);
+    void i18n.changeLanguage(locale).finally(() => {
+      if (!cancelled) {
+        setReady(true);
+      }
+    });
+
+    return () => {
+      cancelled = true;
+    };
+  }, [locale]);
+
+  return (
+    <div
+      className={[
+        "theme min-h-screen bg-background p-6 text-foreground",
+        theme === "dark" ? "dark" : "",
+      ]
+        .filter(Boolean)
+        .join(" ")}
+    >
+      <div className="mx-auto w-full max-w-6xl">{ready ? children : null}</div>
+    </div>
+  );
+};
+
 const preview: Preview = {
@@
   decorators: [
     (Story, context) => {
-      void i18n.changeLanguage(String(context.globals.locale ?? "en"));
+      const locale = String(context.globals.locale ?? "en");
+      const theme = String(context.globals.theme ?? "light");
 
       return (
-        <div
-          className={[
-            "theme min-h-screen bg-background p-6 text-foreground",
-            context.globals.theme === "dark" ? "dark" : "",
-          ]
-            .filter(Boolean)
-            .join(" ")}
-        >
-          <div className="mx-auto w-full max-w-6xl">
-            <Story />
-          </div>
-        </div>
+        <StoryFrame locale={locale} theme={theme}>
+          <Story />
+        </StoryFrame>
       );
     },
   ],
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
decorators: [
(Story, context) => {
void i18n.changeLanguage(String(context.globals.locale ?? "en"));
return (
<div
className={[
"theme min-h-screen bg-background p-6 text-foreground",
context.globals.theme === "dark" ? "dark" : "",
]
.filter(Boolean)
.join(" ")}
>
<div className="mx-auto w-full max-w-6xl">
<Story />
</div>
</div>
);
},
import type { Preview } from "@storybook/react-vite";
import { type ReactNode, useEffect, useState } from "react";
import "../src/index.css";
import i18n from "../src/i18n";
type StoryFrameProps = {
locale: string;
theme: string;
children: ReactNode;
};
const StoryFrame = ({ locale, theme, children }: StoryFrameProps) => {
const [ready, setReady] = useState(
() => i18n.resolvedLanguage === locale || i18n.language === locale,
);
useEffect(() => {
let cancelled = false;
setReady(i18n.resolvedLanguage === locale || i18n.language === locale);
void i18n.changeLanguage(locale).finally(() => {
if (!cancelled) {
setReady(true);
}
});
return () => {
cancelled = true;
};
}, [locale]);
return (
<div
className={[
"theme min-h-screen bg-background p-6 text-foreground",
theme === "dark" ? "dark" : "",
]
.filter(Boolean)
.join(" ")}
>
<div className="mx-auto w-full max-w-6xl">{ready ? children : null}</div>
</div>
);
};
const preview: Preview = {
decorators: [
(Story, context) => {
const locale = String(context.globals.locale ?? "en");
const theme = String(context.globals.theme ?? "light");
return (
<StoryFrame locale={locale} theme={theme}>
<Story />
</StoryFrame>
);
},
],
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.storybook/preview.tsx around lines 50 - 68, The decorator currently calls
i18n.changeLanguage inline during render (i18n.changeLanguage, Story,
context.globals.locale); refactor by extracting a Wrapper component that uses a
useEffect to asynchronously call
i18n.changeLanguage(String(context.globals.locale ?? "en")) and tracks
completion in state (e.g., isReady), then render the themed container and <Story
/> only after isReady is true; replace the inline decorator with this Wrapper so
language switching is performed as a side effect and not during render,
preventing flicker and repeated calls on unrelated re-renders.

Comment thread package.json
"@axe-core/playwright": "^4.11.1",
"@eslint/js": "^9.17.0",
"@playwright/test": "^1.58.2",
"@rollup/rollup-linux-x64-gnu": "^4.60.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect: package metadata is constrained to linux/x64/glibc.
npm view `@rollup/rollup-linux-x64-gnu` os cpu libc --json

# Expect: Rollup already declares platform packages as optional dependencies.
npm view rollup optionalDependencies --json | jq 'to_entries[] | select(.key == "@rollup/rollup-linux-x64-gnu")'

Repository: mudouasenha/portfolio

Length of output: 214


Remove the direct dependency on the platform-specific Rollup binary.

@rollup/rollup-linux-x64-gnu is constrained to Linux x64 with glibc; including it as a direct dev dependency will break npm install on macOS, Windows, ARM, and Alpine Linux. Rollup already manages platform-specific native packages as optional dependencies.

🐛 Proposed fix
-    "@rollup/rollup-linux-x64-gnu": "^4.60.1",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@rollup/rollup-linux-x64-gnu": "^4.60.1",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 48, Remove the platform-specific Rollup binary
dependency by deleting the "@rollup/rollup-linux-x64-gnu" entry from
devDependencies in package.json; Rollup handles platform-native packages as
optional dependencies so removing this direct dependency prevents install
failures on macOS, Windows, ARM, and Alpine while leaving Rollup's
cross-platform behavior intact.

Comment on lines +65 to +84
<div className="mt-8 flex flex-col gap-3 sm:flex-row sm:flex-wrap">
<Button asChild size="lg" className="rounded-full px-5">
<a
href="https://www.linkedin.com/in/matheus-gomes-98823b185"
target="_blank"
rel="noopener noreferrer"
aria-label="Contact on LinkedIn (opens in a new tab)"
>
{t("contact.linkedinCta")}
</a>
</Button>
<a
href="https://github.com/mudouasenha"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub profile (opens in a new tab)"
className="inline-flex items-center rounded-full border border-border/80 px-4 py-2 text-sm font-semibold uppercase tracking-[0.12em] text-foreground transition-colors hover:bg-accent/70"
>
{t("contact.githubCta")}
</a>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Localize the outbound contact link accessible names.

The visible CTA text is translated, but both aria-labels are hardcoded in English, so /pt still exposes English screen-reader labels.

♿ Proposed fix
-                                aria-label="Contact on LinkedIn (opens in a new tab)"
+                                aria-label={t("contact.linkedinAriaLabel")}
...
-                            aria-label="GitHub profile (opens in a new tab)"
+                            aria-label={t("contact.githubAriaLabel")}

Add matching contact.linkedinAriaLabel and contact.githubAriaLabel entries in en and pt.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="mt-8 flex flex-col gap-3 sm:flex-row sm:flex-wrap">
<Button asChild size="lg" className="rounded-full px-5">
<a
href="https://www.linkedin.com/in/matheus-gomes-98823b185"
target="_blank"
rel="noopener noreferrer"
aria-label="Contact on LinkedIn (opens in a new tab)"
>
{t("contact.linkedinCta")}
</a>
</Button>
<a
href="https://github.com/mudouasenha"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub profile (opens in a new tab)"
className="inline-flex items-center rounded-full border border-border/80 px-4 py-2 text-sm font-semibold uppercase tracking-[0.12em] text-foreground transition-colors hover:bg-accent/70"
>
{t("contact.githubCta")}
</a>
<div className="mt-8 flex flex-col gap-3 sm:flex-row sm:flex-wrap">
<Button asChild size="lg" className="rounded-full px-5">
<a
href="https://www.linkedin.com/in/matheus-gomes-98823b185"
target="_blank"
rel="noopener noreferrer"
aria-label={t("contact.linkedinAriaLabel")}
>
{t("contact.linkedinCta")}
</a>
</Button>
<a
href="https://github.com/mudouasenha"
target="_blank"
rel="noopener noreferrer"
aria-label={t("contact.githubAriaLabel")}
className="inline-flex items-center rounded-full border border-border/80 px-4 py-2 text-sm font-semibold uppercase tracking-[0.12em] text-foreground transition-colors hover:bg-accent/70"
>
{t("contact.githubCta")}
</a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Contact.tsx` around lines 65 - 84, The aria-labels for the
outbound links are hardcoded in English; update the anchor elements inside the
Contact component (the Button-asChild LinkedIn anchor and the GitHub <a>) to use
localized keys (e.g., t("contact.linkedinAriaLabel") and
t("contact.githubAriaLabel")) instead of fixed strings, and add matching entries
for contact.linkedinAriaLabel and contact.githubAriaLabel to both locale files
(en and pt) so screen readers get translated labels that correspond to the
visible CTAs (contact.linkedinCta, contact.githubCta).

Comment thread src/components/Hero.tsx
Comment on lines +142 to +144
<Button asChild size="lg" className="min-w-40 rounded-full px-4">
<a href={resumeUrl} target="_blank" rel="noopener noreferrer">{t("hero.primaryCta")}</a>
</Button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Announce that the résumé CTA opens a new tab.

This external primary CTA uses target="_blank" but the accessible name is just the CTA text, unlike the project/contact outbound links.

♿ Proposed fix
-                                <a href={resumeUrl} target="_blank" rel="noopener noreferrer">{t("hero.primaryCta")}</a>
+                                <a href={resumeUrl} target="_blank" rel="noopener noreferrer" aria-label={t("hero.primaryCtaAriaLabel")}>
+                                    {t("hero.primaryCta")}
+                                </a>

Add hero.primaryCtaAriaLabel in both locales, including the new-tab cue.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button asChild size="lg" className="min-w-40 rounded-full px-4">
<a href={resumeUrl} target="_blank" rel="noopener noreferrer">{t("hero.primaryCta")}</a>
</Button>
<Button asChild size="lg" className="min-w-40 rounded-full px-4">
<a href={resumeUrl} target="_blank" rel="noopener noreferrer" aria-label={t("hero.primaryCtaAriaLabel")}>
{t("hero.primaryCta")}
</a>
</Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Hero.tsx` around lines 142 - 144, The CTA opens a new tab but
its accessible name lacks a new-tab cue; add an aria-label using the localized
string (e.g. t("hero.primaryCtaAriaLabel")) to the anchor inside the Button (the
<a href={resumeUrl} ... /> element) so screen readers announce the new-tab
behavior, and add corresponding keys "hero.primaryCtaAriaLabel" to both locale
files with the text including the new-tab cue (e.g. "View résumé (opens in a new
tab)").

Comment thread src/components/Navbar.tsx
Comment on lines +113 to +118
<a href="#top" className="flex min-w-0 flex-shrink items-center gap-2.5">
<img
className="h-10 w-10 flex-none object-contain"
src={logo}
alt="Matheus Gomes logo"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Localize accessible names and announce external resume links.

The logo alt text, social link labels, menu button label, and external resume CTA accessible names are still hardcoded or missing new-tab context, which leaks English into /pt and weakens outbound-link accessibility.

♿ Proposed direction
-                        alt="Matheus Gomes logo"
+                        alt={t("navigation.logoAlt")}
...
-                                    aria-label={`${item.label} (opens in a new tab)`}
+                                    aria-label={t("navigation.socialAriaLabel", { label: item.label })}
...
-                        <a href={RESUME_URL} target="_blank" rel="noopener noreferrer">{t("navigation.cta")}</a>
+                        <a href={RESUME_URL} target="_blank" rel="noopener noreferrer" aria-label={t("navigation.ctaAriaLabel")}>
+                            {t("navigation.cta")}
+                        </a>
...
-                            <Button variant="outline" size="icon-sm" className="rounded-full" aria-label="Open navigation menu">
+                            <Button variant="outline" size="icon-sm" className="rounded-full" aria-label={t("navigation.openMenu")}>
...
-                                                    aria-label={`${item.label} (opens in a new tab)`}
+                                                    aria-label={t("navigation.socialAriaLabel", { label: item.label })}

Add the corresponding navigation.* keys to both locale files.

Also applies to: 152-158, 165-177, 197-215

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Navbar.tsx` around lines 113 - 118, Replace hardcoded
accessible names in Navbar.tsx with localized strings and add matching keys in
both locale files: use the translation function (e.g., t or useTranslation) for
the logo alt text (replace current alt="Matheus Gomes logo" with
t('navigation.logoAlt')), social link labels (replace visible/aria-label text
for social anchors with t('navigation.social.<name>')), the menu button label
(use t('navigation.menuButton') and aria-expanded handling on the menu toggle),
and the external resume CTA (use t('navigation.resume') and include the new-tab
context in the localized string, then ensure the resume link has target="_blank"
rel="noopener noreferrer" and an aria-label that includes the external/new-tab
notice via the translated key). Add the corresponding navigation.* keys in both
locale files (e.g., navigation.logoAlt, navigation.menuButton,
navigation.social.github, navigation.resume) so translations for /pt and others
are present.

Comment on lines 94 to +105
"contact": {
"kicker": "Hiring conversations",
"summary": "The strongest fit is backend, platform, or AI-systems work where delivery quality matters.",
"title": "Available for recruiter and hiring-manager conversations.",
"body": "If you are hiring for backend engineering, platform work, or applied AI systems, LinkedIn is the fastest way to reach me. I reply with context, not buzzwords.",
"locationLabel": "Location",
"responseLabel": "Preferred contact",
"linkedinCta": "Message on LinkedIn",
"githubCta": "Review GitHub",
"address": "Florianopolis, SC, Brazil",
"phoneNo": "Phone number: Contact me at LinkedIn",
"email": "Email: contact.me@linkedin"
"phoneNo": "Fastest response on LinkedIn",
"email": "Available on request"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align the expanded contact payload with the strict adapter schema.

These new nested fields make the whole contact object fail contactSchema.strict(), because the adapter currently allows only address, phoneNo, and email. Either move the copy outside contact or extend the schema/allowed keys.

🐛 Proposed schema-side fix if these nested fields are intentional
 export const contactSchema = z
   .object({
+    kicker: nonEmptyString,
+    summary: nonEmptyString,
+    title: nonEmptyString,
+    body: nonEmptyString,
+    locationLabel: nonEmptyString,
+    responseLabel: nonEmptyString,
+    linkedinCta: nonEmptyString,
+    githubCta: nonEmptyString,
     address: nonEmptyString,
     phoneNo: nonEmptyString,
     email: nonEmptyString,
   })
   .strict();
-    unknownKeysForRecord(raw, ['address', 'phoneNo', 'email']).forEach((unknownKey) => {
+    unknownKeysForRecord(raw, [
+      'kicker',
+      'summary',
+      'title',
+      'body',
+      'locationLabel',
+      'responseLabel',
+      'linkedinCta',
+      'githubCta',
+      'address',
+      'phoneNo',
+      'email',
+    ]).forEach((unknownKey) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/locales/en/translation.json` around lines 94 - 105, The new nested keys
added to the "contact" object (kicker, summary, title, body, locationLabel,
responseLabel, linkedinCta, githubCta) violate the strict adapter schema for
contact (contactSchema.strict()) which currently permits only address, phoneNo,
and email; either move these copy strings out of the "contact" object into a
different top-level translations key or extend the allowed keys in the
adapter/schema to include each new field. Locate the "contact" JSON object in
translation.json and either relocate the new fields to another translations
namespace (e.g., contactCopy.*) or update the schema/enforcement that references
contactSchema.strict() to accept the additional property names (kicker, summary,
title, body, locationLabel, responseLabel, linkedinCta, githubCta) so the
payload passes validation.

Comment on lines +77 to +83
{
"id": "ai-for-devs-branas",
"name": "Artificial Inteligence for Devs",
"issued_by": "branas.io",
"date": "2025-12-01",
"description": "Desenvolveu habilidades no uso de agentes de IA, spec-driven design, agentic AI development e AI fluency. Credential ID: e9f3d49f-1da3-4278-acc4-132d67b1fb36.",
"url": "https://ll-app-certificates.s3.sa-east-1.amazonaws.com/e9f3d49f-1da3-4278-acc4-132d67b1fb36.png"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Correct the certification title typo.

Artificial Inteligence should be Artificial Intelligence unless this intentionally mirrors the issuer’s exact title.

✏️ Proposed fix
-      "name": "Artificial Inteligence for Devs",
+      "name": "Artificial Intelligence for Devs",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"id": "ai-for-devs-branas",
"name": "Artificial Inteligence for Devs",
"issued_by": "branas.io",
"date": "2025-12-01",
"description": "Desenvolveu habilidades no uso de agentes de IA, spec-driven design, agentic AI development e AI fluency. Credential ID: e9f3d49f-1da3-4278-acc4-132d67b1fb36.",
"url": "https://ll-app-certificates.s3.sa-east-1.amazonaws.com/e9f3d49f-1da3-4278-acc4-132d67b1fb36.png"
{
"id": "ai-for-devs-branas",
"name": "Artificial Intelligence for Devs",
"issued_by": "branas.io",
"date": "2025-12-01",
"description": "Desenvolveu habilidades no uso de agentes de IA, spec-driven design, agentic AI development e AI fluency. Credential ID: e9f3d49f-1da3-4278-acc4-132d67b1fb36.",
"url": "https://ll-app-certificates.s3.sa-east-1.amazonaws.com/e9f3d49f-1da3-4278-acc4-132d67b1fb36.png"
🧰 Tools
🪛 Betterleaks (1.1.2)

[high] 82-82: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/locales/pt/translation.json` around lines 77 - 83, The "name" field for
the certificate with id "ai-for-devs-branas" contains a typo ("Artificial
Inteligence"); update the "name" value to "Artificial Intelligence" to correct
spelling while leaving other fields (id, issued_by, date, description, url)
unchanged so the entry remains consistent.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant