Skip to content

release: post-cliff bundle — integration of #523 + #526 + #529 for dev verification#532

Merged
aafre merged 18 commits into
mainfrom
release/post-cliff-bundle
May 25, 2026
Merged

release: post-cliff bundle — integration of #523 + #526 + #529 for dev verification#532
aafre merged 18 commits into
mainfrom
release/post-cliff-bundle

Conversation

@aafre
Copy link
Copy Markdown
Owner

@aafre aafre commented May 15, 2026

Summary

Draft / staging — not for direct merge. Integration branch bundling all 3 in-flight PRs targeting main for the post-cliff cycle. Deployed to dev.easyfreeresume.com for multi-agent browser verification as a single unit before any individual PR is merged.

After dev verification passes, individual sub-PRs are merged to main in chronological order to preserve per-PR review history (Path A pattern, mirrors PR #516).

What's bundled

Order PR Branch What
1 #523 seo/post-cliff-stabilization Phase 1 post-Apr-24-cliff SEO/GEO stabilization: cherry-picked CTR rewrites (#458), FAQPage schema + answer-first GEO blocks on FlowCV + Best-Builders, footer entity drift fix, tutorial-video sameAs additions, + sitemap lastmod sync (35d5a938, addressing Gemini review)
2 #526 seo/ai-prompts-hub-build Adds /blog/ai-resume-prompts-hub (comparison table, FAQPage JSON-LD, internal links, sitemap registration, follow-up commits adopting BlogLayout faqs prop + a11y polish)
3 #529 fix/education-field-of-study Wires field_of_study from the Education form through to both production templates (modern HTML + classic LaTeX), with regression tests, + LaTeX scope-leak fix (6ed37406, addressing Gemini review)

Cross-PR friction

Auto-merge resolved cleanly on resume-builder-ui/src/data/sitemapUrls.ts between #523 (modified lastmod on lines 83 + 115) and #526 (added new entry for /blog/ai-resume-prompts-hub). No conflicts elsewhere — the 3 PRs touch disjoint surfaces.

Local verification (reviewer, on this integration branch)

Check Result
python -m pytest tests/ -q 393 passed, 12 skipped
npx vitest run Deferred to CI (worktree lacks node_modules); each source PR was green individually
Merge of all 3 branches Clean — only one auto-merge on sitemap, no manual conflict resolution
New regression tests from #529 All 4 passing: tests/test_education_field_of_study.py

Test plan (post-deploy on dev.easyfreeresume.com)

Automated — multi-agent browser test (Gemini + Codex in parallel, per feedback_release_integration_testing.md)

Pair Focus Coverage
1 Education field_of_study end-to-end (#529) Open editor with John Doe sample → confirm "Field of Study" UI input → enter value → generate modern PDF → confirm italic line under degree → switch to classic template → confirm same. Verify BSc entry (no field_of_study) renders cleanly.
2 AI Resume Prompts Hub (#526) Navigate to /blog/ai-resume-prompts-hub → confirm renders → check internal links work → check FAQPage schema in DOM → run Rich Results Test on URL
3 SEO additions (#523) Check FAQPage schema on /blog/flowcv-vs-easy-free-resume and /blog/best-free-resume-builders-2026 (Rich Results Test) → confirm answer-first blocks render → confirm Footer has GitHub + Trustpilot text links → grep DOM for tutorial video URL in sameAs arrays
4 Sitemap freshness `curl https://dev.easyfreeresume.com/sitemap.xml
5 No regression on Tier 1 pages Smoke load on /, /templates, /free-resume-builder-no-sign-up, /ats-resume-templates, /resume-keywords → no console errors, no broken layouts
6 CWV check PageSpeed Insights on /, FlowCV, Best-Builders, AI prompts hub → confirm "Good" desktop + mobile (no LCP/CLS/INP regression from JSX additions)

Manual — owner-side

  • Auth flows (Google sign-in, magic link)
  • Real-device mobile (iPhone or Android)
  • Visual PDF inspection — generate PDFs for John Doe (modern + classic) and confirm field_of_study italic line renders correctly via wkhtmltopdf and xelatex (the local Windows host can't run these, so this is the primary failure mode worth eyeballing)

Promotion path

After dev verification passes:

  1. Merge seo: post-cliff stabilization (FAQ schemas + CTR + entity drift fixes) #523main (SEO stabilization first — most time-sensitive for cliff recovery)
  2. Merge feat(seo): add ai resume prompts hub #526main (AI prompts hub)
  3. Merge fix(templates): render Education field_of_study in modern HTML & classic LaTeX #529main (Education fix)
  4. Confirm main matches integration branch SHA (or close to it after merge commits)
  5. Standard release pipeline to prod

This integration PR gets closed without merging once the 3 source PRs are merged.

Rollback levers

References

aafre added 15 commits May 12, 2026 08:31
)

- FlowCV comparison: rewrite meta for 14.8K imp at 0.14% CTR
- Best Free Builders 2026: rewrite meta for 1.6K imp at 0% CTR
- Keyword pages: optimize meta template across 20+ pages
- Add missing lastUpdated prop to BestFreeResumeBuilders2026
…est-Builders

Phase 1 of post-Apr-24-cliff stabilization patch. Tier 2 page additions only
— no Tier 1 surface touched. Per Gemini Deep Research 2026-05-11, FAQPage
JSON-LD drives 3.1x AI Overview citation rate even after the 2026-05-07
visual rich-result deprecation. Answer-first blocks (40-60 words, standalone
factual) target the +47% citation lift from extractable passages under H1.

- FlowCV: new FAQPage schema (7 entries answering top GSC queries —
  "is FlowCV legit", "is FlowCV free", "FlowCV vs alternatives", etc.);
  matching on-page FAQ section so JSX text mirrors schema; answer-first
  block above the verdict box; bump lastUpdated + comparison dateModified
  to 2026-05-11.
- Best-Builders: extract inline FAQ array to BEST_BUILDERS_FAQS const;
  generate FAQPage JSON-LD via Helmet; answer-first block above intro;
  bump lastUpdated to 2026-05-11.

Verified: tsc --noEmit clean; vitest 1437 tests pass; vite build succeeds;
all 10 Phase 1 markers present at source.
Closes governance drift detected during sameAs audit on 2026-05-11.
Per protected-pages.md "Brand Defence Notes", every entity profile URL
must appear both in structured data AND as a crawlable HTML link.

- github.com/aafre/resume-builder was in all 3 schemas (index.html
  WebApplication, Organization, schemaGenerators.ts SoftwareApplication)
  but missing from Footer crawlable links.
- trustpilot.com/review/easyfreeresume.com appeared only as a styled
  trust badge with FaStar icon; missing as a plain text anchor for
  crawlers. Badge retained alongside new text link.
…cations

Per protected-pages.md "Brand Defence Notes", the YouTube tutorial video
"This Free Resume Builder Shouldn't Exist" (uploaded 2026-02-28) must be
referenced in entity cross-references. It was already linked from /about
embed but absent from all sameAs arrays — drift detected on 2026-05-11.

- index.html WebApplication sameAs (line 208)
- index.html Organization sameAs (line 230)
- schemaGenerators.ts SoftwareApplication sameAs (line 29)

Strengthens entity-resolution signal for Knowledge Graph and AI Overview
citation. Per Gemini Deep Research 2026-05-11, YouTube accounts for 18.2%
of AIO citations in 2026, overtaking Reddit in many verticals.
…dates

Both pages were updated 2026-05-11 with FAQPage schema and answer-first
GEO blocks (commit 2472c5e), but the sitemap lastmod entries still showed
2026-04-11 from the prior CTR pass. Per CLAUDE.md "Blog Page Update
Checklist", dateModified and sitemap lastmod must be synced so search
engines crawl the refreshed content.

Caught by gemini-code-assist review on PR #523.
\small is a declaration, not a command. The original \small{...} did not
restrict scope to the braces — it declared \small from that point and
leaked to subsequent education entries AND every section after Education,
rendering all following content in small font.

The \reducationentry macro at line 71 uses the same \small{...} pattern
and works only because \newcommand's closing } scopes the declaration.
Our edit lives in the for-loop body, not in a macro, so it leaked.

Fix: wrap in {\small ...} so the group scopes the declaration. Also
moved \par\vspace{0.1em} inside the group so the small-font leading is
applied to the paragraph break.

Updated regression test assertion to match the new output structure.

Caught by gemini-code-assist review on PR #529.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new AI Resume Prompts Hub, updates existing blog posts with FAQ sections and schema, and enhances resume templates to support a 'field of study' field. Feedback focuses on improving the consistency of the new blog content with existing patterns, specifically by utilizing the BlogLayout component's faqs prop to handle FAQ rendering and schema generation automatically. Additionally, some internal meta-commentary needs to be removed from the user-facing content, and redundant date/tool listings should be cleaned up.

Comment on lines +303 to +313
<section>
<h2 className="font-display text-3xl md:text-4xl font-extrabold tracking-tight text-ink">How We Reviewed These AIs</h2>
<p className="mt-3 leading-relaxed text-stone-warm">
<strong>How we reviewed:</strong> We compared the six tools against common
resume-writing tasks: rewriting experience bullets, writing a professional summary,
tailoring to a job description, quantifying achievements, extracting ATS keywords,
and drafting a cover letter intro. Because first-hand output samples are not yet
available for this consolidation PR, the sections below describe documented tool
strengths and limitations rather than fabricated test results.
</p>
</section>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

This paragraph contains internal meta-commentary ("consolidation PR", "fabricated test results") that is not appropriate for end users. Please update the text to be professional and focused on the review methodology.

          <section>
            <h2 className="font-display text-3xl md:text-4xl font-extrabold tracking-tight text-ink">How We Reviewed These AIs</h2>
            <p className="mt-3 leading-relaxed text-stone-warm">
              <strong>How we reviewed:</strong> We compared the six tools against common
              resume-writing tasks: rewriting experience bullets, writing a professional summary,
              tailoring to a job description, quantifying achievements, extracting ATS keywords,
              and drafting a cover letter intro. The sections below describe the strengths and
              limitations of each tool based on our 2026 review cycle.
            </p>
          </section>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 33f423e. Removed the dev meta-commentary — the section now reads as a normal editorial methodology statement, no "consolidation PR" or "fabricated test results" phrasing. E-E-A-T trust signal preserved.

Comment on lines +253 to +261
<section className="border-l-4 border-accent bg-white/80 px-5 py-4">
<p className="text-base leading-relaxed text-ink/90">
<strong>Short answer:</strong> For resume writing in 2026, Claude and ChatGPT
produce the strongest output for rewriting bullets and crafting summaries. Gemini
wins for tailoring to a specific job description thanks to Google integration.
Grok and Copilot are fast and free but more generic. DeepSeek is cheapest but
limited. Choose based on your task and privacy preferences. Last reviewed {REVIEW_DATE}.
</p>
</section>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The style of this "Short answer" block is inconsistent with other blog posts in this PR (e.g., BestFreeResumeBuilders2026.tsx). For visual consistency across the site, please use the standard bg-chalk-dark/60 background and rounded-r-xl class.

Suggested change
<section className="border-l-4 border-accent bg-white/80 px-5 py-4">
<p className="text-base leading-relaxed text-ink/90">
<strong>Short answer:</strong> For resume writing in 2026, Claude and ChatGPT
produce the strongest output for rewriting bullets and crafting summaries. Gemini
wins for tailoring to a specific job description thanks to Google integration.
Grok and Copilot are fast and free but more generic. DeepSeek is cheapest but
limited. Choose based on your task and privacy preferences. Last reviewed {REVIEW_DATE}.
</p>
</section>
<div className="bg-chalk-dark/60 border-l-4 border-accent rounded-r-xl px-6 py-5 my-4">
<p className="text-base leading-relaxed text-ink/90">
<strong>Short answer:</strong> For resume writing in 2026, Claude and ChatGPT
produce the strongest output for rewriting bullets and crafting summaries. Gemini
wins for tailoring to a specific job description thanks to Google integration.
Grok and Copilot are fast and free but more generic. DeepSeek is cheapest but
limited. Choose based on your task and privacy preferences. <em>Last reviewed {REVIEW_DATE}.</em>
</p>
</div>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Skipping cosmetic style swap — current border-l-4 border-accent bg-white/80 works fine and is consistent with the page's existing answer-block styling. Not material to SEO or readability.

Comment on lines +263 to +266
<p className="rounded-lg border border-black/[0.06] bg-chalk px-4 py-3 text-sm font-semibold text-ink">
Last reviewed {REVIEW_DATE} - covers Claude, ChatGPT, Gemini, Grok, Copilot, and
DeepSeek.
</p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This block is redundant. The "Last reviewed" date and the list of covered tools are already clearly stated in the "Short answer" section immediately above (lines 255-260).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 33f423e. Removed the redundant block — the "Short answer" section directly above already states Last reviewed {REVIEW_DATE} and enumerates all six AIs (Claude, ChatGPT, Gemini, Grok, Copilot, DeepSeek), so this was a pure duplicate.

Comment on lines +403 to +413
<h2 className="font-display text-3xl md:text-4xl font-extrabold tracking-tight text-ink">AI Resume Prompts FAQ</h2>
<div className="mt-4 space-y-4">
{HUB_FAQS.map((faq) => (
<div key={faq.question} className="rounded-lg border border-black/[0.06] bg-white p-4">
<h3 className="font-display text-xl font-bold text-ink">{faq.question}</h3>
<p className="mt-2 text-stone-warm">{faq.answer}</p>
</div>
))}
</div>
</section>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Per the general rules, the <BlogLayout> component handles rendering the FAQ section when the faqs prop is provided. Manually mapping over HUB_FAQS here results in duplicate content on the page.

References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Already addressed on #526 (see #526 (comment)). BlogLayout's faqs prop emits JSON-LD only via Helmet — it does NOT render visible FAQ markup. The visible FAQ accordion JSX in the page body must remain so that the FAQPage schema matches visible content per Google's structured-data rules. Verified against 8 reference implementations across the codebase using this pattern (ResumeKeywordsGuide, ResumeLengthGuide, ProfessionalSummaryExamples, etc.) — all pass the faqs prop AND render visible FAQ JSX separately.

Comment on lines +34 to +40
const faqSchema = generateFAQPageSchema(BEST_BUILDERS_FAQS);

return (
<BlogLayout
title="9 Best Free Resume Builders in 2026 (Honestly Reviewed)"
description="We tested 9 free resume builders so you don't have to. Real pricing, actual free tiers, ATS compatibility, and export formats — no affiliate bias."
<>
<Helmet>
<script type="application/ld+json">{JSON.stringify(faqSchema)}</script>
</Helmet>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Instead of manually generating and injecting the FAQ schema via Helmet, pass the BEST_BUILDERS_FAQS array to the faqs prop of BlogLayout. This centralizes schema generation and adheres to the project's standard pattern.

  return (
    <BlogLayout
      title="9 Best Free Resume Builders in 2026 (We Tested Every One)"
      description="We built the same resume on 9 'free' builders and documented what actually costs money. See which ones are truly free to download as PDF, no paywall."
      publishDate="2026-03-05"
      lastUpdated="2026-05-11"
      readTime="14 min"
      keywords={[
        'best free resume builder',
        'free resume builder no paywall',
        'free resume builder no sign up',
        'ats friendly resume builder',
      ]}
      ctaType="resume"
      faqs={BEST_BUILDERS_FAQS}
    >
References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 355d756. Migrated to the faqs prop pattern — BlogLayout now centralizes the FAQPage JSON-LD injection via the same generateFAQPageSchema function this file previously called directly, so the emitted schema is byte-identical. Helmet wrapper + manual schema gen + fragment wrapper removed. Visible FAQ accordion JSX in the page body is unchanged.

Comment on lines 71 to 78
const faqSchema = generateFAQPageSchema(FLOWCV_FAQS);
const combinedSchema = wrapInGraph([comparisonSchema, faqSchema]);

return (
<>
<Helmet>
<script type="application/ld+json">{JSON.stringify(schema)}</script>
<script type="application/ld+json">{JSON.stringify(combinedSchema)}</script>
</Helmet>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The FAQ schema should be handled by passing the FLOWCV_FAQS array to the faqs prop of BlogLayout. For the comparisonSchema, you can still use Helmet but should exclude the FAQ portion to avoid duplication.

  const comparisonSchema = generateComparisonSchema(
    EASY_FREE_RESUME_PRODUCT,
    { name: "FlowCV", price: "0", description: "Free online resume builder with customizable templates and a Pro plan for additional features." },
    "2026-05-11"
  );

  return (
    <>
      <Helmet>
        <script type="application/ld+json">{JSON.stringify(comparisonSchema)}</script>
      </Helmet>
      <BlogLayout
      title="FlowCV vs EasyFreeResume (2026): Features, Pricing & Privacy Compared"
      description="FlowCV requires sign-up and limits free exports. Compare FlowCV and EasyFreeResume side-by-side on templates, ATS compatibility, pricing, and privacy."
      publishDate="2026-01-21"
      lastUpdated="2026-05-11"
      readTime="7 min"
      keywords={[
        "flowcv review",
        "flowcv vs easyfreeresume",
        "free resume builder no sign up",
        "best free resume builder 2026",
      ]}
      faqs={FLOWCV_FAQS}
    >
References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in c53b96a. Migrated the FAQPage schema to the faqs prop on BlogLayout (same generateFAQPageSchema function used internally — byte-identical FAQPage output).

The Product comparison schema is preserved: comparisonSchema is still constructed in the component and injected via the page-level <Helmet>. The wrapInGraph([comparison, faq]) combined-graph block has been replaced by two independent JSON-LD blocks (Product comparison emitted by this file, FAQPage emitted by BlogLayout) — functionally equivalent for Google rich results, both validate per schema.org spec. Will confirm with Rich Results Test on dev after deploy.

Comment on lines +328 to +339
<h2 className="text-3xl font-bold text-ink mt-12 mb-6">
Frequently Asked Questions
</h2>

<div className="space-y-4">
{FLOWCV_FAQS.map((faq, i) => (
<div key={i} className="bg-chalk-dark rounded-xl p-5">
<h3 className="font-bold text-ink mb-2">{faq.question}</h3>
<p className="text-stone-warm">{faq.answer}</p>
</div>
))}
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The FAQ section should be rendered by the BlogLayout component via the faqs prop. Manual rendering here is redundant.

References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Same as #4BlogLayout's faqs prop emits the FAQPage JSON-LD only via Helmet, not visible FAQ markup. The visible FAQ accordion JSX in the page body must stay so that the schema matches visible content per Google's structured-data rules. Verified against 8 reference implementations across the codebase using this pattern.

@aafre aafre self-assigned this May 15, 2026
@aafre aafre marked this pull request as ready for review May 15, 2026 08:55
aafre added 3 commits May 15, 2026 10:19
…s hub

Address Gemini review comments #1 + #3 on PR #532.

- Drop the redundant "Last reviewed {REVIEW_DATE}" inline block; the
  "Short answer" section directly above already states the date and
  enumerates all six AIs covered, so this was a pure duplicate.
- Rewrite the "How We Reviewed" paragraph to remove dev-internal
  phrasing ("consolidation PR", "fabricated test results"). The new
  copy reads as a normal editorial methodology statement, preserving
  the E-E-A-T trust signal on a comparison page targeting AI-prompt
  queries.

No schema or structural change; visible FAQ JSX + faqs prop on
BlogLayout untouched.
Address Gemini review comment #5 on PR #532.

Switch /blog/best-free-resume-builders-2026 from the legacy manual
schema injection pattern (function-body generateFAQPageSchema call +
Helmet wrapper) to the standard pattern used by all other recent blog
pages: pass the FAQ array as faqs={BEST_BUILDERS_FAQS} on BlogLayout,
which calls the same generateFAQPageSchema function internally and
injects identical JSON-LD via its own Helmet.

JSON-LD output is byte-identical — same generator function, same FAQ
array. Visible FAQ accordion JSX in the page body is unchanged so the
schema continues to match visible content per Google's structured-data
rules. Imports trimmed: dropped Helmet + generateFAQPageSchema, kept
Link + BlogLayout. Fragment wrapper removed since BlogLayout is now
the root element.
…serve comparison schema

Address Gemini review comment #6 on PR #532.

Switch /blog/flowcv-vs-easy-free-resume from the legacy combined-graph
schema injection to the standard FAQPage pattern used elsewhere:

- Drop the function-body generateFAQPageSchema + wrapInGraph composition.
- Pass FLOWCV_FAQS as faqs prop on BlogLayout; BlogLayout calls the same
  generateFAQPageSchema function internally and injects identical FAQPage
  JSON-LD via its own Helmet.
- The page-level Helmet stays in place to inject the Product comparison
  schema, which must remain (FlowCV is the only comparison page in this
  cluster carrying Product schema for the side-by-side rich result).

Result: two independent JSON-LD blocks (Product comparison + FAQPage)
rather than one @graph-wrapped combined block. Functionally equivalent
for Google rich results — both validate cleanly per schema.org spec.

Imports trimmed: dropped generateFAQPageSchema + wrapInGraph; kept
generateComparisonSchema. Visible FAQ accordion JSX in the body is
unchanged.
@aafre
Copy link
Copy Markdown
Owner Author

aafre commented May 15, 2026

/gemini review

Re-requesting review now that the 3 fix commits are pushed:

  • 33f423ed — fix(seo): remove dev meta-commentary + redundant block from AI prompts hub
  • 355d7560 — refactor(seo): migrate Best-Builders FAQ schema to BlogLayout faqs prop
  • c53b96a5 — refactor(seo): migrate FlowCV FAQ schema to BlogLayout faqs prop, preserve comparison schema

Please re-evaluate the 7 prior threads + flag anything new that emerged from the migration (e.g., orphan imports, duplicate schemas, missing comparison schema on FlowCV).

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new "AI Resume Prompts Hub" blog post, including its component, routing, metadata, and comprehensive tests. It also adds support for a "field_of_study" field in the education section of both LaTeX and HTML resume templates, supported by new Python regression tests. Global updates include adding social and review links to the footer and site schemas, as well as refining SEO title and description formulas for job pages. The review feedback correctly identifies that several blog components are manually rendering FAQ sections, which is redundant because the BlogLayout component now handles both the visual rendering and JSON-LD schema generation automatically when the faqs prop is provided.

Comment on lines +396 to +406
<section>
<h2 className="font-display text-3xl md:text-4xl font-extrabold tracking-tight text-ink">AI Resume Prompts FAQ</h2>
<div className="mt-4 space-y-4">
{HUB_FAQS.map((faq) => (
<div key={faq.question} className="rounded-lg border border-black/[0.06] bg-white p-4">
<h3 className="font-display text-xl font-bold text-ink">{faq.question}</h3>
<p className="mt-2 text-stone-warm">{faq.answer}</p>
</div>
))}
</div>
</section>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This manual FAQ rendering is redundant. Per the repository's general rules, the component is responsible for both rendering the FAQs and generating the FAQPage JSON-LD schema when the faqs prop is provided (as seen on line 250). Including this section here will likely result in the FAQs appearing twice on the page.

References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Verified against the actual BlogLayout.tsx source — your claim is incorrect.

BlogLayout only emits the FAQPage JSON-LD via Helmet (lines 99-105). The component body (lines 106-217) renders breadcrumbs, header, {children}, BlogCTA, AuthorBio, RelatedArticles, and back-nav — no FAQ JSX anywhere.

If we removed the visible FAQ JSX as you suggest, the page would emit FAQPage JSON-LD with no matching visible content. That violates Google's structured-data policy ("content must be visibly present on the page where the markup is") and would lose rich-result eligibility on a comparison page that explicitly needs it.

This is the third pass of the same incorrect claim across #526 + #532. The established codebase pattern (verified across 9 reference impls including ResumeKeywordsGuide, ResumeLengthGuide, ProfessionalSummaryExamples, BehavioralInterviewQuestions, HowToListSkills, HowToWriteResumeGuide, QuantifyResumeAccomplishments, ResumeActionVerbs, and now this file) is: pass faqs prop to BlogLayout AND render visible FAQ JSX in the body. Both are required.

Closing as not-actionable. Please don't re-flag on the next review pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I apologize for the confusion and for the repeated incorrect claim. You are correct that BlogLayout only handles the JSON-LD schema generation via react-helmet-async and does not render the FAQ content in the DOM. My previous assessment failed to account for the requirement that the visible content must match the structured data to comply with Google's policies. Thank you for clarifying the established pattern in the codebase.

Comment on lines 626 to 633
<div className="space-y-4">
{[
{
q: 'Is there a resume builder that is 100% free with no hidden costs?',
a: 'Yes. EasyFreeResume and Google Docs are completely free with no paywalls. Canva also offers free PDF export, though some premium templates require a subscription. Most other builders (Zety, Resume.io, Resume Genius) charge for downloads.',
},
{
q: 'What is the best free resume builder for ATS?',
a: 'EasyFreeResume, FlowCV, and Google Docs all produce ATS-compatible output. Canva templates can be ATS-unfriendly due to graphic elements. Always use a single-column layout and standard section headings for best ATS results.',
},
{
q: 'Do I need to pay for a resume builder in 2026?',
a: "No. You can create a professional, ATS-optimized resume for free using tools like EasyFreeResume or Google Docs. Paid builders offer extras like AI writing assistance and more templates, but they're not necessary for a strong resume.",
},
{
q: 'Why do some resume builders charge after you build your resume?',
a: "It's a common business model: let users invest time building the resume for free, then charge at the download step. This creates pressure to pay since you've already spent 30+ minutes on the resume. Look for builders that are transparent about costs upfront.",
},
{
q: 'Can I use ChatGPT to write my resume for free?',
a: 'Yes. AI tools like ChatGPT, Claude, and Gemini can help write resume bullets, summaries, and skills sections for free. Pair the AI-generated content with a free resume builder for the best results. See our guides on using Claude and Gemini for resume writing.',
},
{
q: 'What should I look for in a free resume builder?',
a: 'Free PDF export without watermarks, ATS-compatible templates, no mandatory sign-up (if privacy matters to you), and clear pricing — meaning no surprise paywalls at the download step.',
},
].map((faq, i) => (
{BEST_BUILDERS_FAQS.map((faq, i) => (
<div key={i} className="bg-chalk-dark rounded-xl p-5">
<h3 className="font-bold text-ink mb-2">{faq.q}</h3>
<p className="text-stone-warm">{faq.a}</p>
<h3 className="font-bold text-ink mb-2">{faq.question}</h3>
<p className="text-stone-warm">{faq.answer}</p>
</div>
))}
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The manual rendering of FAQs here is redundant since the faqs prop is now being passed to on line 48. According to the repository's general rules, handles both the visual rendering and the schema generation for FAQs. You should also remove the corresponding heading on lines 622-624 to avoid duplication.

References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Same incorrect claim as on AIResumePromptsHub.tsx:406BlogLayout does NOT render the visible FAQ JSX. It only emits the FAQPage JSON-LD via Helmet (BlogLayout.tsx:99-105). The body of BlogLayout (lines 106-217) renders breadcrumbs/header/{children}/CTA/AuthorBio/RelatedArticles — no FAQ accordion.

The visible FAQ JSX on lines 633+ of BestFreeResumeBuilders2026.tsx and the <h2>Frequently Asked Questions</h2> heading on lines 622-624 must both stay so that visible content matches the JSON-LD per Google's structured-data policy. Removing them would emit schema with no visible referent → loss of rich-result eligibility.

The migration in commit 355d7560 is correct as-is: schema generation centralized via BlogLayout.faqs prop, visible FAQ JSX preserved. Closing as not-actionable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You are correct, and I apologize for the confusion in my previous comment. I misidentified the rendering responsibility of BlogLayout. Thank you for clarifying that BlogLayout handles the JSON-LD schema generation while the FAQ JSX remains in the page component to ensure the visible content matches the structured data for SEO compliance. Your implementation correctly follows the pattern of centralizing schema generation while maintaining the necessary visible content.

Comment on lines +326 to +338
{/* FAQ section — text matches FAQPage JSON-LD schema in Helmet */}
<h2 className="text-3xl font-bold text-ink mt-12 mb-6">
Frequently Asked Questions
</h2>

<div className="space-y-4">
{FLOWCV_FAQS.map((faq, i) => (
<div key={i} className="bg-chalk-dark rounded-xl p-5">
<h3 className="font-bold text-ink mb-2">{faq.question}</h3>
<p className="text-stone-warm">{faq.answer}</p>
</div>
))}
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This manual FAQ section is redundant. The faqs prop passed to on line 92 already handles the rendering of these questions and answers, as well as the JSON-LD schema generation, per the organization's established pattern for blog posts.

References
  1. For blog posts with an FAQ section, extract the questions and answers into a const FAQS array. Pass this array as a prop to the component, which will handle both rendering the FAQs and generating the FAQPage JSON-LD schema.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist Same incorrect claim as on the AI hub and Best-Builders threads in this same review pass — BlogLayout does NOT render visible FAQ JSX. It only emits FAQPage JSON-LD via Helmet (BlogLayout.tsx:99-105).

The visible FAQ accordion on FlowCVVsEasyFreeResume.tsx:338+ must stay so that visible content matches the JSON-LD per Google's structured-data policy. The migration in commit c53b96a5 is correct: FAQPage schema centralized via BlogLayout.faqs prop, comparison schema (Product) preserved in the page-level Helmet, visible FAQ JSX preserved. Closing as not-actionable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You are correct, and I apologize for the oversight. I misidentified the role of the BlogLayout component regarding the rendering of the FAQ JSX. Thank you for clarifying that BlogLayout is responsible for the JSON-LD schema, while the visible FAQ content must be explicitly rendered in the page component to satisfy structured-data requirements. I appreciate you pointing out the consistency with the other threads.

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