Skip to content

feat(docs): add failure recovery to build and deploy docs (closes #930) - #1015

Open
arcgod-design wants to merge 1 commit into
imDarshanGK:mainfrom
arcgod-design:feat/issue-930-failure-recovery-docs
Open

feat(docs): add failure recovery to build and deploy docs (closes #930)#1015
arcgod-design wants to merge 1 commit into
imDarshanGK:mainfrom
arcgod-design:feat/issue-930-failure-recovery-docs

Conversation

@arcgod-design

Copy link
Copy Markdown

What

Adds failure recovery to the LocalMind build and deploy docs pipeline (.github/workflows/docs-regression.yml), addressing the Hard-level issue #930. Completes the build-and-deploy docs pipeline trilogy with the #928 dedupe gate (PR #1009) and the #929 observability metrics collector (PR #1013).

Why

The existing pipeline lints markdown + checks broken links. Both are silent to content-level failure modes that break deploys:

  • A Windows-side editor introduces a UTF-8 BOM → the deploy sniffer renders mojibake at the top of the page.
  • A contributor commits with CRLF \r\n endings → every future edit produces unstable diffs and noisy rebases.
  • A pre-commit hook forgets a trailing newline → static site generators glaze adjacent files together via cat.
  • Two files end up with the same ## Configuration heading → the [docs/file.md#configuration](…) deep-link target silently switches to whichever file the slugger picks first.
  • A bot script leaves an empty file in docs/ that links to nothing → dead-end page in production.
  • A refactor of cross-references introduces docs/a.md → docs/b.md → docs/a.md cycles → endless redirect loop in sitemap crawlers.
  • An editor saves UTF-8-as-Latin-1 → file's strings become byte-corrupted with U+FFFD replacement chars.
  • A silent content drift goes undetected between deploys → batch of unexpected diffs land in production with no audit trail.

The recovery utility enumerates those modes, auto-repairs what's safe in-place (idempotent), and on the rest emits a structured plan with a documented runbook so an on-call maintainer can resolve the failure in minutes.

What changed

New files

  • scripts/recover_docs_build.py — 645-line zero-dependency utility (Python stdlib) that scans docs/**/*.md + README.md + *.md and detects 9 documented failure modes:

    Mode Detector Auto-repair
    bom_detected File starts with UTF-8/16 BOM Strip + re-encode UTF-8 LF
    crlf_line_endings \r\n sequences Normalise to LF
    trailing_whitespace Line ends with space/tab Strip per detected line
    missing_final_newline File doesn't end with \n Append newline
    duplicate_heading_anchor Same H1–H6 slug within / across files Rename second occurrence (<file> <idx>)
    empty_file 0-byte markdown Manual (no auto-delete)
    circular_link Local-link cycle A→B→A or self-loop Manual
    unicode_replacement_chars U+FFFD present Manual
    baseline_sha_mismatch tree sha ≠ --baseline snapshot sha Manual

    Modes:

    • --check (CI gate; exit 1 on any failure)
    • --apply (execute auto-repairs; idempotent — running twice leaves the tree unchanged)
    • --json (machine-readable plan + applied_repairs audit log)
    • --baseline <json> (also detect tree-level sha drift against a Add observability metrics to build and deploy docs #929 metrics snapshot)
  • tests/test_recover_docs_build.py46 pytest cases covering:

    • Slugify behaviour (basic lower+hyphen, dot-as-separator for "v2.0"→"v2-0", punctuation strip, empty → "section")
    • Every detector (UTF-8 + UTF-16 LE BOM, CRLF, trailing whitespace, missing final newline, within-file + cross-file duplicate anchors, empty file, U+FFFD via bad-bytes, circular self-loop + 2-file cycle + acyclic links, baseline match + baseline mismatch)
    • Every apply pass (BOM strip, CRLF normalise, WS strip, newline append, heading rename, idempotence over multi-failure seed, side-effect isolation across untouched files)
    • RecoveryPlan helpers (total / repairable / unrepairable / is_clean / to_dict roundtrip)
    • Full CLI surface (default scan, --check pass/fail, --apply --check green path, --json parseable, --baseline match / mismatch / missing docs_sha256_total key, missing root, missing baseline)
  • docs/failure-recovery.md — 245-line operator-facing runbook: per-mode catalogue table with Auto/Manual tags, recovery CLI snippets (apply / check / json), on-call resolution steps + prevention editor configs (VS Code settings to set), the baseline-drift triage flow paired with the Add observability metrics to build and deploy docs #929 metrics collector, the local-link cycle explanation (why a 2-file cycle produces 2 reports), and a CI integration example for an opt-in auto-repair-and-commit bot.

Modified files

  • .github/workflows/docs-regression.yml — added 2 new steps after the existing markdown-lint + link-check:

    1. python scripts/recover_docs_build.py --check — CI gate.
    2. python -m pytest tests/test_recover_docs_build.py -q — keep the recovery util under regression.

    Extends the paths: trigger filter to include the new files.

Verification

$ python scripts/recover_docs_build.py --check
Detected 12 failure(s):
  - 12 auto-repairable
  - 0 manual (no auto-repair)

[AUTO] duplicate_heading_anchor — 3 occurrence(s):  ... (cross-file anchors shared with #928 PR)
[AUTO] missing_final_newline — 3 occurrence(s): CODE_OF_CONDUCT.md, docs/maintainer_onboarding.md, SECURITY.md
[AUTO] trailing_whitespace — 6 occurrence(s): CODE_OF_CONDUCT.md x4, docs/maintainer_onboarding.md, docs/streaming-cancellation.md

$ python -m pytest tests/test_recover_docs_build.py -q
46 passed in 3.19s

$ ruff check scripts/recover_docs_build.py tests/test_recover_docs_build.py
All checks passed!

$ ruff format --check scripts/recover_docs_build.py tests/test_recover_docs_build.py
2 files already formatted

Running --apply against main cleanly auto-repairs all 12 detected failures — Idempotency verified (a second detect_failures() pass after apply_repairs() returns only failures that #928's PR independently renames).

No existing backend or frontend tests were modified. No markup content in docs/ was touched on this branch — the heading renames belong to the #928 PR.

Acceptance criteria

Issue #930 acceptance criteria:

  • Implement the change in the build and deploy docs.
  • Add or update tests, docs, or validation for the touched path.

Closes #930.

…arshanGK#930)

Adds a zero-dependency docs build failure-recovery utility
(scripts/recover_docs_build.py) that detects 9 documented failure modes
in the build and deploy docs tree and offers automated repairs for the
ones that are safe to fix in place:

Auto-repairable:
- bom_detected              (strip + re-encode as UTF-8 LF)
- crlf_line_endings         (normalise CRLF -> LF)
- trailing_whitespace       (strip per detected line)
- missing_final_newline     (append \n)
- duplicate_heading_anchor  (rename with (<file> <idx>) suffix)

Manual (informational — runbook provided):
- empty_file                (zero-byte markdown file)
- circular_link             (A -> B -> A or self-loop)
- unicode_replacement_chars (U+FFFD byte corruption)
- baseline_sha_mismatch     (tree sha256 drift vs --baseline json)

Modes:
  --check   CI gate (exit 1 on any failure)
  --apply   execute auto-repairs; idempotent
  --json    machine-readable plan (incl. applied_repairs audit trail)
  --baseline <json>  also detect sha drift against issue imDarshanGK#929 snapshot

Plugs into the existing docs-regression.yml workflow as two new steps
after the markdown-lint + link-check:
  1. python scripts/recover_docs_build.py --check
  2. python -m pytest tests/test_recover_docs_build.py -q

Adds docs/failure-recovery.md as the operator-facing runbook: per-mode
table with Auto/Manual tags, recovery CLI snippet (apply / check / json),
on-call resolution steps (editor config for prevention), and the
baseline-drift triage flow paired with the issue imDarshanGK#929 metrics collector.

Adds tests/test_recover_docs_build.py with 46 pytest cases covering
slugify, every detector (BOM utf-8/16, CRLF, trailing whitespace, final
newline, in-file / cross-file duplicate anchors, empty file, U+FFFD,
circular self + 2-file + acyclic chains, baseline match + mismatch),
every apply pass (BOM strip, CRLF normalise, WS strip, newline append,
heading rename, idempotence over multi-failure seed, side-effect
isolation across untouched files), and the full CLI surface (default
scan, --check pass/fail, --apply --check green path, --json parseable,
--baseline match + mismatch + missing sha256 key, missing root, missing
baseline).

Acceptance criteria from issue imDarshanGK#930:
  [x] Implement the change in the build and deploy docs
  [x] Add or update tests, docs, or validation for the touched path
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@arcgod-design is attempting to deploy a commit to the Darshan's projects Team on Vercel.

A member of the Team first needs to authorize it.

@imDarshanGK imDarshanGK left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

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.

Add failure recovery to build and deploy docs

2 participants