Skip to content

DOC-7047: Formalize DOC-6909 shortcode migration tooling - #3937

Merged
andy-stark-redis merged 4 commits into
mainfrom
DOC-7047-render-hook-migration-tooling
Sep 9, 2026
Merged

andy-stark-redis merged 4 commits into
mainfrom
DOC-7047-render-hook-migration-tooling

Conversation

@andy-stark-redis

@andy-stark-redis andy-stark-redis commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Checks in the three ad hoc converters used to migrate develop/clients/redis-py (the DOC-6909 proof section) as build/migrate_shortcode_links.py: relref→plain link, callouts→> [!NOTE] blockquote, absolute/plain→canonical /content/*.md. The originals lived only in a scratch session dir and were lost, so later sections would otherwise reinvent them.
  • Adds build/diff_rendered_hrefs.py, an href-fingerprint diff tool to verify a migration changed no rendered link (formalizes the ad hoc recipe used during DOC-6909, excludes the two known-nondeterministic page families by default).
  • Extends .claude/hooks/check_shortcode_paths.py to validate the new /content/*.md link form at edit time, mirroring the existing relref check (same diff-scoping, same mount-aware resolution). Previously only relref got this coverage, so a migrated page's broken link had no edit-time catch.

This is Phase 0 of DOC-7047 (rolling the DOC-6909 render-hook migration out to the rest of content/develop/clients/) — no content changes, prerequisite tooling only.

Test plan

  • build/migrate_shortcode_links.py all run against the already-converted develop/clients/redis-py produces 0 changes — proves the formalized tool reproduces the hand-done reference conversion exactly.
  • Piloted live on develop/clients/ioredis (DOC-7047: Migrate develop/clients/ioredis to render hooks #3938) — before/after Hugo build, 0 rendered href differences site-wide, no new warnings.
  • check_shortcode_paths.py --scan --all on current main: baseline 39 relref issues (known, pre-existing), 0 new link issues.
  • Negative test: a deliberately broken /content/*.md link is caught; a valid one is not.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com


Note

Low Risk
Adds and extends offline build/validation scripts and a Claude edit hook; no production runtime or content changes in this PR.

Overview
Phase 0 tooling only — no content edits. This checks in the workflow needed to roll the DOC-6909 render-hook migration (plain /content/*.md links + GitHub-style callout blockquotes) beyond the redis-py pilot.

Adds build/migrate_shortcode_links.py, a three-stage, idempotent converter: unwrap relref shortcodes in links, turn note/warning/etc. shortcodes into > [!NOTE] blockquotes (before link rewriting so links resolve in page context), then rewrite resolvable internal links to canonical /content/<path>.md targets. It reuses mount-aware resolution from the hook module but deliberately does not remap module mounts when rewriting paths.

Adds build/diff_rendered_hrefs.py to compare two Hugo public/ trees by per-page href fingerprints (optional URL prefix filter, known nondeterministic sections excluded by default) so migrations can prove reader-visible links did not change.

Extends check_shortcode_paths.py so edit-time validation covers the new markdown link form the same way it already covered absolute relref (diff-scoped against HEAD, mount-aware resolution, optional SHORTCODE_SKIP_PLAIN_LINK). Refactors HEAD diffing from relref-only lists to full prior text plus a shared _new_occurrences helper.

Reviewed by Cursor Bugbot for commit 9194bbc. Bugbot is set up for automated code reviews on this repo. Configure here.

Checks in the three ad hoc converters used to migrate develop/clients/redis-py
(relref->plain, callouts->blockquote, absolute->/content/*.md) as
build/migrate_shortcode_links.py, plus build/diff_rendered_hrefs.py to verify
a migration changed no rendered link. The originals lived only in a scratch
session dir and were lost, so later sections would have reinvented them.

Also extends check_shortcode_paths.py to validate the new /content/*.md link
form at edit time, mirroring the existing relref check (same diff-scoping,
same mount-aware resolution) -- previously only relref got this coverage, so
a migrated page's broken link had no edit-time catch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

DOC-7047

@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 13 related items from repository history:

Memory updated at 9194bbc

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread build/migrate_shortcode_links.py Outdated
Comment thread build/diff_rendered_hrefs.py
- migrate_shortcode_links.py: _find_content_file no longer follows the
  module-mount remapping (_mount_variants) when deciding what to rewrite a
  link to. The one real mount (Active-Active rs/rc) has an open product
  question attached (DOC-6909 mount probe: relref itself resolves an
  in-mount-target link to the rs permalink, leaving the rc mount) -- that's
  a pre-existing site behavior, not this tool's call to make, and silently
  rewriting to the mount source would bake it into a literal path forever.
  The validator (check_shortcode_paths.resolve_relref) still follows mounts,
  since "does this resolve at all" is a different question than "what
  literal path should a rewrite commit to." No effect on any content
  actually converted so far -- none of the DOC-7047 client-docs units touch
  Active-Active pages.
- diff_rendered_hrefs.py: comparing two empty page sets (wrong public/ path,
  or a prefix that matches nothing) now errors instead of silently exiting
  0 -- "0 pages compared" was indistinguishable from "0 differences found."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 8, 2026
Converts content/develop/clients/nodejs/ (12 files, flat directory) from
relref/callout shortcodes to the DOC-6909 render hooks, continuing the
DOC-7047 rollout: relref -> plain link -> canonical /content/<path>.md[#anchor]
form, and 7 {{< note >}} callouts -> `> [!NOTE]` blockquotes. No nesting, no
custom titles, no percent-delimited {{% alert %}} shortcodes in this
directory.

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Gaps: Human review (dwdougherty) spot-checked a handful of links rather than testing every one; the href-diff build comparison is the completeness evidence, not the manual review.
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread build/migrate_shortcode_links.py Outdated
andy-stark-redis added a commit that referenced this pull request Sep 8, 2026
Converts content/develop/clients/dotnet/ (12 files: 8 top-level plus the
nested nredisstack/ subdirectory) from relref/callout shortcodes to the
DOC-6909 render hooks, continuing the DOC-7047 rollout: relref -> plain
link -> canonical /content/<path>.md[#anchor] form, and 9 note/warning
callouts -> `> [!NOTE]`-style blockquotes across 119 converted links. No
percent-delimited `{{% alert %}}` forms in this section.

Also fixes a pre-existing malformed relref in nredisstack/vecsearch.md (a
stray backtick before the closing paren broke the link syntax, so the
converter's regex couldn't match it) -- folded into this PR as a bonus
rather than a separate ticket.

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Gaps: Human review (dwdougherty) was a skim/spot-check, not exhaustive; the href-diff build comparison is the completeness evidence, not the manual review.
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RELREF_RX required the markdown ')' immediately after '>}}', so
]({{< relref "X" >}}#anchor) and ]({{< relref "X" >}}?query) -- an anchor
or query written OUTSIDE the shortcode rather than inside the quoted path
-- never matched. Those relrefs silently stayed unconverted with no
warning (105 files site-wide use this form, e.g. develop/data-types/_index.md's
`?group=` command-reference links). Fixed by capturing the optional
suffix and re-appending it when unwrapping.

That alone would have introduced a real regression: linkify only
preserved a '#anchor' suffix when resolving the rewritten link, not a
'?query' one, so a freshly-unwrapped ?query link would have had its query
string silently dropped. Fixed linkify to split on the first '#' or '?'
and preserve whichever suffix is present.

Verified: idempotency proof still holds on redis-py (0 changes) and on
the already-merged nodejs/dotnet content (0 changes, checked in a
separate worktree against current main). Live-tested all three shapes
(in-quote anchor, outside-shortcode anchor, outside-shortcode query) --
the query case correctly stays unconverted when its target has no
_index.md, with the query string preserved rather than dropped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 8, 2026
Converts content/develop/clients/jedis/ (11 files, flat directory) from
relref/callout shortcodes to the DOC-6909 render hooks, continuing the
DOC-7047 rollout: relref -> plain link -> canonical /content/<path>.md[#anchor]
form, and 6 {{< note >}} callouts -> `> [!NOTE]` blockquotes. No nesting, no
custom titles, no percent-delimited {{% alert %}} shortcodes in this
directory.

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3f8cb7c. Configure here.

Comment thread .claude/hooks/check_shortcode_paths.py Outdated
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/go/ (13 files, flat directory) from
relref/callout shortcodes to the DOC-6909 render hooks, continuing the
DOC-7047 rollout: relref -> plain link -> canonical /content/<path>.md[#anchor]
form (121 links), and 10 {{< note >}} callouts -> `> [!NOTE]` blockquotes,
including one with an embedded fenced code block. No custom titles, no
nesting, no percent-delimited {{% alert %}} shortcodes.

Also fixes a cosmetic regression a reviewer caught: a note directly after a
{{< clients-example >}} widget rendered too close to it, because unwrapping
the shortcode to a native blockquote drops the <p>-wrap margin the old
shortcode form got for free (the same "benign <p>-wrapping" delta DOC-6909
already flagged as usually harmless). Fixed with a bare `&nbsp;` spacer
line, the same pattern already used the other way round in
go/scan-buffer.md. Confirmed this is the only such adjacency in this unit
and doesn't recur in any other DOC-7047 unit.

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Learned: unwrapping a callout shortcode to a native blockquote can visibly tighten spacing when it sits directly against another block element (e.g. clients-example) -- watch for this wherever the rollout touches such an adjacency, fix with a bare `&nbsp;` spacer line.
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/lettuce/ (10 files) from relref/callout
shortcodes to the DOC-6909 render hooks, continuing the DOC-7047 rollout:
relref -> plain link -> canonical /content/<path>.md[#anchor] form, and 5
{{< note >}} callouts -> `> [!NOTE]` blockquotes.

produsage.md keeps one {{% alert title="Tip" color="warning" %}} shortcode
untouched -- the percent-delimited custom-title form the converter doesn't
handle (an accepted gap; the redis-py reference conversion has the same
two instances left as-is).

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Gaps: produsage.md's {{% alert %}} (percent-delimited, custom title) is still an unconverted shortcode -- not a miss, the tool doesn't handle this form yet.
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/php/ (8 files) from relref/callout
shortcodes to the DOC-6909 render hooks, continuing the DOC-7047 rollout:
relref -> plain link -> canonical /content/<path>.md[#anchor] form, and 3
{{< note >}} callouts -> `> [!NOTE]` blockquotes.

Verified with a full-site Hugo build merged onto main alongside the rest of
its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/patterns/ (5 files, including the nested
indexes/ subdirectory) from relref shortcodes to the DOC-6909 render
hooks: relref -> plain link -> canonical /content/<path>.md[#anchor] form
(75 links total). No callouts in this section.

Also fixes a dead command-reference anchor a reviewer caught:
[text](/commands/#sorted_set) resolved without warning but did nothing --
the #sorted_set anchor doesn't exist on the current filterable /commands/
grid. Replaced with the query-string form the site's own
command-group.html shortcode generates internally (?group=sorted-set),
which the page's own JS reads on load to pre-select the group filter.
Confirmed the render-link hook preserves this query-string form intact
(it has explicit, pre-existing handling for exactly this case).

Verified with a full-site Hugo build merged onto main alongside the rest
of its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Directive: always `find <dir> -name '*.md'` recursively when scoping a DOC-7047 unit's files -- a one-level-deep glob misses nested subdirectories (this unit's indexes/ was the first instance).
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/rust/ (5 files) from relref/callout
shortcodes to the DOC-6909 render hooks, continuing the DOC-7047 rollout:
relref -> plain link -> canonical /content/<path>.md[#anchor] form, and 2
{{< note >}} callouts -> `> [!NOTE]` blockquotes.

Verified with a full-site Hugo build merged onto main alongside the rest
of its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/hiredis/ (8 files, including the nested
int-examples/ subdirectory) from relref shortcodes to the DOC-6909 render
hooks: relref -> plain link -> canonical /content/<path>.md[#anchor] form.
No callouts in this section.

Verified with a full-site Hugo build merged onto main alongside the rest
of its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts content/develop/clients/ruby/ (5 files) from relref/callout
shortcodes to the DOC-6909 render hooks, continuing the DOC-7047 rollout:
relref -> plain link -> canonical /content/<path>.md[#anchor] form, and 4
{{< note >}} callouts -> `> [!NOTE]` blockquotes.

Also fixes a spacing regression a reviewer caught in queryjson.md and
vecsearch.md: two `> [!NOTE]` blocks separated by only a blank line
rendered directly touching, since a bare blockquote doesn't get the free
<p>-wrap margin the old shortcode form got individually (same root cause
as the go/queryjson.md fix in #3948, a different sub-case: adjacent
alerts rather than an alert after another widget). Fixed with a bare
`&nbsp;` spacer line between them.

Verified with a full-site Hugo build merged onto main alongside the rest
of its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Directive: a `&nbsp;` spacer between two blockquotes needs a blank line on BOTH sides, not just after -- with none before, it lazily continues the preceding blockquote (CommonMark lazy-continuation) and produces zero visual change. Verify by grepping the rendered HTML for a real `<p> </p>` between the two alert divs, not by trusting the source diff.
Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andy-stark-redis added a commit that referenced this pull request Sep 9, 2026
Converts the 9 shared top-level content/develop/clients/*.md files
(pools-and-muxing, error-handling, client-side-caching, etc. -- the files
every per-client subdirectory links into) from relref/callout shortcodes
to the DOC-6909 render hooks: relref -> plain link -> canonical
/content/<path>.md[#anchor] form (109 links), and 4 {{< note >}} callouts
-> `> [!NOTE]` blockquotes across _index.md, client-side-caching.md, and
sch.md.

Verified with a full-site Hugo build merged onto main alongside the rest
of its batch: 0 rendered href differences, 0 new build warnings
(build/diff_rendered_hrefs.py, from #3937).

Ticket: DOC-7047
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bugbot: PLAIN_LINK_RX only allowed an optional #fragment after .md, so it
never matched /content/*.md?query -- the form linkify emits when it
preserves a `?` suffix (fixed in a prior commit on this branch, house
style's ?group= command-reference links). Such links got zero edit-time
validation even though resolve_plain_link/_norm_relref already strip both
`?` and `#` before resolving; the regex just never captured that href
shape to feed them in the first place.

Verified: full-repo baseline scan unchanged (39 relref, 0 link issues),
and two live tests -- a broken /content/*.md?query link is now caught, a
valid one passes silently, and the combined ?query#anchor form works too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@andy-stark-redis
andy-stark-redis merged commit 4a32c00 into main Sep 9, 2026
96 checks passed
@andy-stark-redis
andy-stark-redis deleted the DOC-7047-render-hook-migration-tooling branch September 9, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants