Next.js Web Persona#839
Conversation
📝 WalkthroughWalkthroughAdds a new nextjs-web-steward persona JSON and modifies the Relayfile page: introduces SiteNav/SiteFooter wiring, passes GitHubStarsBadge as navActions, updates hero styles and RootLayout body classes, and hardens the FadeIn reveal logic. ChangesNext.js Web Steward Persona Setup
Relayfile Page & Components
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
web/app/file/relayfile.module.cssParsing error: Declaration or statement expected. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agentworkforce/workforce/personas/nextjs-web-steward.json:
- Around line 16-32: The listed persona skill sources use mutable branch refs;
update each URL under the ids nextjs-app-router-fundamentals,
nextjs-server-client-components, lighthouse-ci-integrator, and seo-audit by
replacing "tree/main" with a pinned commit SHA (e.g., "tree/<commit-sha>") for
that repository, fetching the stable commit SHA from the remote repo and
substituting it into the "source" string so each skill points to an immutable
commit reference instead of the main branch.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 957ae8ee-f53a-4f4e-a24a-3e3557857639
📒 Files selected for processing (1)
.agentworkforce/workforce/personas/nextjs-web-steward.json
| "source": "https://github.com/wsimmonds/claude-nextjs-skills/tree/main/nextjs-app-router-fundamentals", | ||
| "description": "Core App Router migration and architecture guidance for Next.js 13+ projects." | ||
| }, | ||
| { | ||
| "id": "nextjs-server-client-components", | ||
| "source": "https://github.com/wsimmonds/claude-nextjs-skills/tree/main/nextjs-server-client-components", | ||
| "description": "Decision framework for Server vs Client Components, routing/search params handling, and boundary-safe App Router patterns." | ||
| }, | ||
| { | ||
| "id": "lighthouse-ci-integrator", | ||
| "source": "https://github.com/Dexploarer/hyper-forge/tree/main/.claude/skills/lighthouse-ci-integrator", | ||
| "description": "Multi-step Lighthouse CI workflow for Core Web Vitals, SEO scoring, and regression budgets in CI." | ||
| }, | ||
| { | ||
| "id": "seo-audit", | ||
| "source": "https://github.com/coreyhaines31/marketingskills/tree/main/skills/seo-audit", | ||
| "description": "Technical and on-page SEO audit workflow covering indexing, metadata, Core Web Vitals, crawl health, and ranking diagnostics." |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify mutable refs and basic URL reachability for skill sources.
set -euo pipefail
FILE="$(fd -p 'nextjs-web-steward.json' | head -n1)"
echo "Reviewing file: $FILE"
echo "---- Sources found ----"
jq -r '.skills[].source' "$FILE"
echo "---- Mutable refs (tree/main) ----"
jq -r '.skills[].source' "$FILE" | rg -n '/tree/main/' || true
echo "---- HTTP status checks ----"
while IFS= read -r url; do
code="$(curl -s -o /dev/null -w '%{http_code}' "$url")"
echo "$code $url"
done < <(jq -r '.skills[].source' "$FILE")Repository: AgentWorkforce/relay
Length of output: 182
🏁 Script executed:
find . -name "*nextjs-web-steward*" -type f 2>/dev/nullRepository: AgentWorkforce/relay
Length of output: 125
🏁 Script executed:
cat ./.agentworkforce/workforce/personas/nextjs-web-steward.jsonRepository: AgentWorkforce/relay
Length of output: 5926
Pin external skill sources to immutable commit references
All four skill sources use mutable branch references (/tree/main/), which creates non-deterministic persona behavior and weakens reproducibility guarantees:
- Line 16:
claude-nextjs-skills/tree/main/nextjs-app-router-fundamentals - Line 21:
claude-nextjs-skills/tree/main/nextjs-server-client-components - Line 26:
hyper-forge/tree/main/.claude/skills/lighthouse-ci-integrator - Line 31:
marketingskills/tree/main/skills/seo-audit
Replace each tree/main with a pinned commit SHA (e.g., tree/abc1234d) to ensure skill content remains stable over time.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.agentworkforce/workforce/personas/nextjs-web-steward.json around lines 16 -
32, The listed persona skill sources use mutable branch refs; update each URL
under the ids nextjs-app-router-fundamentals, nextjs-server-client-components,
lighthouse-ci-integrator, and seo-audit by replacing "tree/main" with a pinned
commit SHA (e.g., "tree/<commit-sha>") for that repository, fetching the stable
commit SHA from the remote repo and substituting it into the "source" string so
each skill points to an immutable commit reference instead of the main branch.
| "best": { | ||
| "harness": "codex", | ||
| "model": "openai-codex/gpt-5.3-codex", | ||
| "systemPrompt": "", |
There was a problem hiding this comment.
🔴 Empty systemPrompt in all three tiers will leave persona without behavioral instructions
All three tiers (best, best-value, minimum) set "systemPrompt": "". Every existing persona in packages/personas/personas/ provides a substantive system prompt, and the persona validator (packages/personas/scripts/validate-personas.mjs:94-96) explicitly requires systemPrompt to be a non-empty string via isNonEmptyString() (which checks value.trim().length > 0). When a harness loads this persona, the agent will receive no system-level instructions, making it behave as a generic assistant rather than a Next.js web steward.
The `agentsMdContent` field is not a substitute
The file includes agentsMd and agentsMdContent fields with detailed instructions, possibly intended as the system prompt. However, these fields are not part of the documented persona schema (packages/personas/README.md:73-88), are not referenced anywhere in the codebase (confirmed via search), and no code reads them. The detailed behavioral instructions in agentsMdContent should be moved into each tier's systemPrompt field (tailored per tier as other personas do).
Prompt for agents
All three tiers in the persona (best at line 41, best-value at line 50, minimum at line 59) have empty systemPrompt fields. Every other persona in packages/personas/personas/ provides per-tier system prompts that instruct the agent on its role, style, and constraints.
The file contains an agentsMdContent field with detailed Next.js Web Steward instructions, but this field is not part of the recognized persona schema and is not read by any code in the repo.
To fix: populate each tier's systemPrompt with appropriate behavioral instructions derived from the agentsMdContent. Follow the pattern of existing personas like relay-orchestrator.json, where each tier gets a tailored prompt (best tier gets the most detailed, minimum tier gets the most concise). Then consider removing the agentsMd and agentsMdContent fields unless they serve a purpose in the AgentWorkforce framework that is not yet reflected in this repo's code.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 850fd9deb2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "best": { | ||
| "harness": "codex", | ||
| "model": "openai-codex/gpt-5.3-codex", | ||
| "systemPrompt": "", |
There was a problem hiding this comment.
Provide non-empty systemPrompt for this tier
This tier sets systemPrompt to an empty string, which makes the persona unloadable in Relay: loadPersona treats empty prompts as missing and throws (packages/sdk/src/personas.ts, if (!systemPrompt) ...). Because all three tiers here use "systemPrompt": "", selecting nextjs-web-steward fails at runtime instead of spawning an agent, and agentsMdContent does not compensate for this in the SDK path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
web/components/FadeIn.tsx (2)
43-60: ⚡ Quick winCancel the fallback timer after a real intersection.
Once Line 45 fires, the timeout started at Line 56 is still pending and will schedule a redundant
setVisible(true)for every revealed instance. Clearing it in the observer callback avoids needless updates on pages with manyFadeIns.Suggested change
- const observer = new IntersectionObserver( + let fallbackTimer: number | undefined; + const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { reveal(); + if (fallbackTimer !== undefined) { + window.clearTimeout(fallbackTimer); + } observer.unobserve(el); } }, { threshold: 0.01, rootMargin: '0px 0px -8% 0px' } ); @@ - const fallbackTimer = window.setTimeout(reveal, 1500); + fallbackTimer = window.setTimeout(reveal, 1500);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/components/FadeIn.tsx` around lines 43 - 60, The IntersectionObserver callback reveals the element via reveal() but doesn't clear the fallbackTimer, so the fallback timeout still fires; update the observer callback (the function passed to new IntersectionObserver that calls reveal() and observer.unobserve(el)) to also call window.clearTimeout(fallbackTimer) (or clearTimeout) after reveal() to cancel the pending fallback; ensure fallbackTimer (created with window.setTimeout) remains in scope for the callback so clearing it prevents the redundant setVisible(true) call.
30-41: ⚡ Quick winHonor
prefers-reduced-motionbefore hiding offscreen content.Line 31 enables animation for every hydrated client, and Line 41 then hides below-the-fold content until the observer or timeout fires. Users who request reduced motion will still get the fade/slide behavior here. Please short-circuit to
reveal()when reduced motion is enabled.Suggested change
// Only animate when JS is active; content remains visible if hydration fails. + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + reveal(); + return; + } setAnimate(true);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/components/FadeIn.tsx` around lines 30 - 41, The component currently enables animations unconditionally (calls setAnimate(true)) and then hides offscreen content (setVisible(false)), which ignores users' prefers-reduced-motion setting; update FadeIn so that before calling setAnimate(true) you detect the media query (window.matchMedia('(prefers-reduced-motion: reduce)').matches) and if it returns true immediately call reveal() (the existing reveal function) and return, skipping setAnimate and setVisible logic so reduced-motion users never get the hide/fade/slide behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@web/components/FadeIn.tsx`:
- Around line 43-60: The IntersectionObserver callback reveals the element via
reveal() but doesn't clear the fallbackTimer, so the fallback timeout still
fires; update the observer callback (the function passed to new
IntersectionObserver that calls reveal() and observer.unobserve(el)) to also
call window.clearTimeout(fallbackTimer) (or clearTimeout) after reveal() to
cancel the pending fallback; ensure fallbackTimer (created with
window.setTimeout) remains in scope for the callback so clearing it prevents the
redundant setVisible(true) call.
- Around line 30-41: The component currently enables animations unconditionally
(calls setAnimate(true)) and then hides offscreen content (setVisible(false)),
which ignores users' prefers-reduced-motion setting; update FadeIn so that
before calling setAnimate(true) you detect the media query
(window.matchMedia('(prefers-reduced-motion: reduce)').matches) and if it
returns true immediately call reveal() (the existing reveal function) and
return, skipping setAnimate and setVisible logic so reduced-motion users never
get the hide/fade/slide behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: eec71c8c-7f1e-420e-bd58-3248dd1788af
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
.agentworkforce/workforce/personas/nextjs-web-steward.jsonweb/app/file/RelayfileContent.tsxweb/app/file/page.tsxweb/app/file/relayfile.module.cssweb/app/layout.tsxweb/components/FadeIn.tsx
✅ Files skipped from review due to trivial changes (1)
- web/app/layout.tsx
This pull request introduces a new persona configuration for the Next.js web application by adding the
nextjs-web-stewardpersona. This persona defines responsibilities, required skills, workflows, and quality standards for maintaining and improving the Next.js app, with a focus on aligning work with product goals, SEO, and performance.