diff --git a/docs/seo/links-audit-20260528.md b/docs/seo/links-audit-20260528.md index 269c89aa7..0db4d43fc 100644 --- a/docs/seo/links-audit-20260528.md +++ b/docs/seo/links-audit-20260528.md @@ -6,75 +6,29 @@ Generated by `scripts/audit_blog_links.py`. One-shot snapshot. - Articles audited: 104 - Links checked: 10128 (8500 internal, 1628 external) -- Internal broken: 60 (6 distinct dead slugs) +- Internal broken: 0 (0 distinct dead slugs) +- Internal resolved via redirect: 60 (6 distinct slugs, not broken) - External broken: 100 - External flaky (informational): 1214 - Multilingual mismatches: 0 ## Internal broken links with redirect proposals -| From article | Lang | Broken link | Dead slug | Proposal | Confidence | -|---|---|---|---|---|---| -| can-personality-be-changed-coaching-therapy-evidence | en | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| can-personality-be-changed-coaching-therapy-evidence | ca | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| can-personality-be-changed-coaching-therapy-evidence | es | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| can-personality-be-changed-coaching-therapy-evidence | fr | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| can-personality-be-changed-coaching-therapy-evidence | de | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| can-personality-be-changed-coaching-therapy-evidence | da | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| do-personality-traits-change-over-a-lifetime | en | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| do-personality-traits-change-over-a-lifetime | ca | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| do-personality-traits-change-over-a-lifetime | es | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| do-personality-traits-change-over-a-lifetime | fr | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| do-personality-traits-change-over-a-lifetime | de | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| do-personality-traits-change-over-a-lifetime | da | `/blog/personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | personality-and-career-choice-what-big-five-predicts | 1.0 | -| how-to-design-meetings-for-all-personality-types | en | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| how-to-design-meetings-for-all-personality-types | ca | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| how-to-design-meetings-for-all-personality-types | es | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| how-to-design-meetings-for-all-personality-types | fr | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| how-to-design-meetings-for-all-personality-types | de | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| how-to-design-meetings-for-all-personality-types | da | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | en | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | ca | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | es | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | fr | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | de | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| mbti-vs-big-five-which-should-your-team-use | da | `/blog/forced-choice-personality-assessment-more-honest-data-more-honest-data` | `forced-choice-personality-assessment-more-honest-data-more-honest-data` | forced-choice-personality-assessment-more-honest-data | 1.0 | -| neurodiversity-and-personality-tests-what-to-know | en | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| neurodiversity-and-personality-tests-what-to-know | ca | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| neurodiversity-and-personality-tests-what-to-know | es | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| neurodiversity-and-personality-tests-what-to-know | fr | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| neurodiversity-and-personality-tests-what-to-know | de | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| neurodiversity-and-personality-tests-what-to-know | da | `/blog/personality-testing-in-hiring-ethics-and-best-practices` | `personality-testing-in-hiring-ethics-and-best-practices` | personality-testing-in-hiring-what-is-legal-what-is-ethical | 0.63 | -| personality-and-career-choice-what-big-five-predicts | en | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-career-choice-what-big-five-predicts | ca | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-career-choice-what-big-five-predicts | es | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-career-choice-what-big-five-predicts | fr | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-career-choice-what-big-five-predicts | de | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-career-choice-what-big-five-predicts | da | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | en | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | ca | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | es | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | fr | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | de | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-and-job-fit-how-to-think-about-person-environment-fit | da | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | en | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | ca | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | es | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | fr | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | de | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| personality-coaching-using-big-five-as-development-tool | da | `/blog/personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | personality-and-motivation-what-drives-each-big-five-profile | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | en | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | en | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | ca | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | ca | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | es | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | es | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | fr | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | fr | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | de | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | de | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | da | `/blog/introverts-in-extrovert-workplaces-what-research-says-what-research-says` | `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | introverts-in-extrovert-workplaces-what-research-says | 1.0 | -| why-meetings-drain-some-people-more-than-others-neuroscience | da | `/blog/what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | what-is-extraversion-beyond-the-introvert-extrovert-binary | 1.0 | +None. + +## Internal links resolved via redirect + +Targets not in the live slug set that 308 to a live successor +via blog_slug_redirects. These are NOT broken. + +| Dead slug | Instances | +|---|---| +| `forced-choice-personality-assessment-more-honest-data-more-honest-data` | 6 | +| `introverts-in-extrovert-workplaces-what-research-says-what-research-says` | 12 | +| `personality-and-career-choice-what-big-five-predicts-what-big-five-predicts` | 6 | +| `personality-and-motivation-what-drives-each-big-five-profile-what-drives-each-big-five-profile` | 24 | +| `personality-testing-in-hiring-ethics-and-best-practices` | 6 | +| `what-is-extraversion-beyond-the-introvert-extrovert-binary-beyond-the-introvert-extrovert-binary` | 6 | ## External broken links diff --git a/scripts/audit_blog_links.py b/scripts/audit_blog_links.py index 7972a3bd0..3c7b0d5cb 100644 --- a/scripts/audit_blog_links.py +++ b/scripts/audit_blog_links.py @@ -122,6 +122,24 @@ def probe_external(client: httpx.Client, url: str) -> tuple[str, int | None]: return "flaky", last_status +def resolves_via_redirect(client: httpx.Client, slug: str, cache: dict[str, bool]) -> bool: + """True if /blog/ resolves to a live article, following one 308. + + A slug missing from the live set is not necessarily broken: the + blog_slug_redirects table may 308 it to a live successor. Probe the API + (following redirects) and treat a final 200 as resolved. + """ + if slug in cache: + return cache[slug] + try: + r = client.get(f"/blog/{slug}", timeout=10, follow_redirects=True) + resolved = r.status_code == 200 + except httpx.HTTPError: + resolved = False + cache[slug] = resolved + return resolved + + def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--api", default=DEFAULT_API) @@ -145,6 +163,8 @@ def main() -> int: languages[slug] = langs_with_content(contents[slug]) internal_broken: list[dict] = [] + internal_redirected: list[dict] = [] + redirect_cache: dict[str, bool] = {} mismatches: list[dict] = [] external_seen: dict[str, tuple[str, int | None]] = {} external_broken: list[dict] = [] @@ -166,9 +186,15 @@ def main() -> int: continue _, target_slug = target if target_slug not in live_set: - internal_broken.append( - {"from": slug, "lang": lang, "url": url, "slug": target_slug} - ) + # Not a live slug, but it may 308 to a live successor. + if resolves_via_redirect(client, target_slug, redirect_cache): + internal_redirected.append( + {"from": slug, "lang": lang, "slug": target_slug} + ) + else: + internal_broken.append( + {"from": slug, "lang": lang, "url": url, "slug": target_slug} + ) elif lang not in languages.get(target_slug, []): mismatches.append( {"from": slug, "lang": lang, "target": target_slug, @@ -205,6 +231,10 @@ def main() -> int: lines.append(f"- Articles audited: {len(live_slugs)}") lines.append(f"- Links checked: {total_links} ({total_internal} internal, {total_external} external)") lines.append(f"- Internal broken: {len(internal_broken)} ({len(dead_slugs)} distinct dead slugs)") + lines.append( + f"- Internal resolved via redirect: {len(internal_redirected)} " + f"({len({r['slug'] for r in internal_redirected})} distinct slugs, not broken)" + ) lines.append(f"- External broken: {len(external_broken)}") lines.append(f"- External flaky (informational): {len(external_flaky)}") lines.append(f"- Multilingual mismatches: {len(mismatches)}") @@ -224,6 +254,22 @@ def main() -> int: lines.append("None.") lines.append("") + lines.append("## Internal links resolved via redirect") + lines.append("") + lines.append("Targets not in the live slug set that 308 to a live successor") + lines.append("via blog_slug_redirects. These are NOT broken.") + lines.append("") + redirected_slugs = sorted({r["slug"] for r in internal_redirected}) + if redirected_slugs: + lines.append("| Dead slug | Instances |") + lines.append("|---|---|") + for ds in redirected_slugs: + n = sum(1 for r in internal_redirected if r["slug"] == ds) + lines.append(f"| `{ds}` | {n} |") + else: + lines.append("None.") + lines.append("") + lines.append("## External broken links") lines.append("") if external_broken: