Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion hydra-gates/scripts/lib/check_spec_anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,24 @@ def has_anchor(md_path: str, fragment: str) -> bool:
"""True when *fragment* names a heading or task item in *md_path*."""
frag_slug = slugify(fragment)
frag_alt = slugify(re.sub(r'^task-', '', fragment))
frags = (frag_slug, frag_alt)
# The fragment VERBATIM, alongside its slugified forms.
#
# `heading_aliases` already produces both spellings of a heading — the
# kebab one and the one GitHub actually publishes — but the fragment was
# being pushed through `slugify()` first, and that rewrites `_` to `-`.
# A tag copied from a real GitHub link therefore stopped matching the
# gh alias it was copied FROM:
#
# heading ### Requirement: Trace-scoped call correlation via
# call_log.sessionId (REQ-011)
# gh alias …-via-call_logsessionid-req-011 ← what GitHub links to
# fragment …-via-call_logsessionid-req-011 ← what the author wrote
# slugified …-via-call-logsessionid-req-011 ← what was compared
#
# which matches neither alias: not the gh one (underscore rewritten) and
# not the kebab one (`call-log-sessionid`, split at the underscore).
# Reported as "anchor not found" on a tag whose link works.
frags = (frag_slug, frag_alt, fragment.lower())
pos_m = re.fullmatch(r'(?:task-)?(\d+)', fragment)
positional = int(pos_m.group(1)) if pos_m else None
item_count = 0
Expand Down
46 changes: 46 additions & 0 deletions hydra-gates/scripts/lib/test_check_spec_anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,52 @@ def test_a_wholly_invented_fragment_is_still_reported(self):
self.assertIn("anchor not found", findings[0])


class FragmentCopiedFromAGitHubLink(unittest.TestCase):
"""A tag copied from a real GitHub link must resolve.

``heading_aliases`` already emits both spellings of a heading — the kebab
one and the one GitHub publishes — but the FRAGMENT was pushed through
``slugify()`` before comparison, and that rewrites ``_`` to ``-``. So a
fragment matched neither alias: not the gh one (its underscore had been
rewritten) and not the kebab one (which splits at the underscore).
Observed on openconnector ``SynchronizationService`` (2026-08-07).
"""

MD = (
"# HTTP call engine\n\n"
"### Requirement: Trace-scoped call correlation via call_log.sessionId (REQ-011)\n\n"
"Body.\n"
)

def test_the_github_spelling_resolves(self):
# What GitHub links to: underscore kept, dot dropped.
self.assertTrue(_anchor(
self.MD,
"requirement-trace-scoped-call-correlation-via-call_logsessionid-req-011",
))

def test_the_kebab_spelling_still_resolves(self):
self.assertTrue(_anchor(
self.MD,
"requirement-trace-scoped-call-correlation-via-call-log-sessionid-req-011",
))

def test_a_near_miss_does_not_resolve(self):
# The control. Accepting the raw fragment must not become "accept any
# fragment that shares a prefix" — this one names a heading the file
# does not have.
self.assertFalse(_anchor(
self.MD,
"requirement-trace-scoped-call-correlation-via-call_logrequestid-req-011",
))

def test_an_underscore_fragment_against_a_file_without_it_does_not_resolve(self):
self.assertFalse(_anchor(
"# Other\n\n### Requirement: Something else entirely (REQ-012)\n",
"requirement-trace-scoped-call-correlation-via-call_logsessionid-req-011",
))


class GateIsNotBlind(unittest.TestCase):
"""One consolidated demonstration that the gate still has teeth.

Expand Down
Loading