Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
74b2a0a
Navigation refresh: index the 69 packets integrated 26-29 September 2026
claude Sep 29, 2026
9e9024c
Navigation refresh: resolve the record's links from docs/integration …
claude Sep 29, 2026
720f0a5
Navigation refresh: bare review URLs; document-relative link checker …
claude Sep 30, 2026
02a4544
Navigation refresh: label paraphrased review passages as summaries; q…
claude Sep 30, 2026
18ab283
Merge remote-tracking branch 'origin/main' into claude/navigation-ref…
claude Sep 30, 2026
e89cde1
Merge remote-tracking branch 'origin/main' into claude/navigation-ref…
claude Sep 30, 2026
70f9e22
PROOF_INDEX: availability-only pointer to the GP-DATA-114 publication…
claude Sep 30, 2026
1a69a14
Merge remote-tracking branch 'origin/main' into claude/navigation-ref…
claude Sep 30, 2026
7141706
Merge remote-tracking branch 'origin/main' into claude/navigation-ref…
claude Sep 30, 2026
9ca8d8b
Merge main f9ebba1 into claude/navigation-refresh-20260929 (rebase on…
claude Oct 1, 2026
1085639
Navigation refresh: state the 1 October rebase in the two date lines …
claude Oct 1, 2026
bb3c444
Navigation refresh: correct the rebase base in the date lines (789f6b…
claude Oct 1, 2026
a4ee8f1
Merge main fb6ee97 into claude/navigation-refresh-20260929 (rebase on…
claude Oct 1, 2026
6cae072
Navigation refresh: date lines name the current base fb6ee97 (merges …
claude Oct 1, 2026
e495635
Merge main 6f74f7a into claude/navigation-refresh-20260929 (rebase on…
claude Oct 2, 2026
4ff4f1d
Navigation refresh: date lines name the current base 6f74f7a (merges …
claude Oct 2, 2026
243dfb8
Merge current main into reviewed navigation PR163
d6g8k5htny-coder Oct 4, 2026
66427a3
Merge current main into reviewed navigation PR163
d6g8k5htny-coder Oct 4, 2026
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
40 changes: 40 additions & 0 deletions .github/workflows/navigation-links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Navigation document links and quotations

'on':
pull_request:
paths:
- 'PROOF_INDEX.md'
- 'docs/integration/**'
- 'tools/check_doc_links.py'
- 'tools/check_doc_quotes.py'
- 'tests/test_navigation_links.py'
- 'tests/test_navigation_quotes.py'
- '.github/workflows/navigation-links.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
document-relative-links:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: '3.11.16'
- name: Resolve every link from the containing document and check every file-sourced quotation, in both interpreter modes
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
set -euo pipefail
python -B -S tools/check_doc_links.py PROOF_INDEX.md docs/integration/*.md
python -B -O -S tools/check_doc_links.py PROOF_INDEX.md docs/integration/*.md
python -B -S tools/check_doc_quotes.py PROOF_INDEX.md docs/integration/2026-09-29-navigation-refresh.md
python -B -O -S tools/check_doc_quotes.py PROOF_INDEX.md docs/integration/2026-09-29-navigation-refresh.md
python -B -S -m unittest -v tests.test_navigation_links tests.test_navigation_quotes
python -B -O -S -m unittest -v tests.test_navigation_links tests.test_navigation_quotes
git diff --exit-code
104 changes: 103 additions & 1 deletion PROOF_INDEX.md

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions docs/integration/2026-09-29-navigation-refresh.md

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions tests/test_navigation_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Tests for tools/check_doc_links.py: document-relative resolution, escaping paths,
commentary inside link targets, and the two navigation documents themselves."""
import os
import pathlib
import shutil
import sys
import tempfile
import unittest

ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'tools'))
import check_doc_links # noqa: E402

NAV_DOCS = ['PROOF_INDEX.md', 'docs/integration/2026-09-29-navigation-refresh.md']


class NavigationDocumentsResolve(unittest.TestCase):
def test_navigation_documents_have_no_failing_links(self):
for doc in NAV_DOCS:
failures = check_doc_links.check_document(str(ROOT / doc), str(ROOT))
self.assertEqual(failures, [], doc)

def test_main_exit_status(self):
rc = check_doc_links.main([str(ROOT / d) for d in NAV_DOCS] + ['--root', str(ROOT)])
self.assertEqual(rc, 0)


class DocumentRelativeSemantics(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp(prefix='doclinks-')
self.root = pathlib.Path(self.tmp)
(self.root / 'reviews').mkdir()
(self.root / 'reviews' / 'x.md').write_text('x\n')
(self.root / 'docs' / 'nested').mkdir(parents=True)

def tearDown(self):
shutil.rmtree(self.tmp)

def _doc(self, rel, body):
path = self.root / rel
path.write_text(body)
return str(path)

def test_root_joined_target_in_nested_document_is_rejected(self):
# The file exists at the repository root, but the link is inside docs/nested and
# therefore resolves to docs/nested/reviews/x.md, which does not exist.
doc = self._doc('docs/nested/a.md', '[x](reviews/x.md)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [('missing', 'reviews/x.md')])

def test_document_relative_target_in_nested_document_is_accepted(self):
doc = self._doc('docs/nested/a.md', '[x](../../reviews/x.md) and [y](../../reviews/x.md#frag)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [])

def test_root_document_root_relative_target_is_accepted(self):
doc = self._doc('index.md', '[x](reviews/x.md)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [])

def test_escaping_target_is_rejected(self):
doc = self._doc('docs/nested/a.md', '[bad](../../../outside.md)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [('escapes-tree', '../../../outside.md')])

def test_commentary_inside_url_is_rejected(self):
doc = self._doc('index.md',
'[r](https://github.com/o/r/pull/1#pullrequestreview-2 (rebound at abc))\n')
failures = check_doc_links.check_document(doc, self.tmp)
self.assertEqual([f[0] for f in failures], ['commentary-in-url'])

def test_commentary_inside_local_target_is_rejected(self):
doc = self._doc('index.md', '[r](reviews/x.md (blob 0123, 5 B))\n')
failures = check_doc_links.check_document(doc, self.tmp)
self.assertEqual([f[0] for f in failures], ['commentary-in-target'])

def test_anchor_only_and_bare_external_are_accepted(self):
doc = self._doc('index.md', '[a](#section) [b](https://example.org/p?q=1#frag)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [])

def test_missing_local_target_is_rejected(self):
doc = self._doc('index.md', '[m](reviews/nope.md)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [('missing', 'reviews/nope.md')])

def test_images_are_ignored(self):
doc = self._doc('index.md', '![img](https://example.org/a b.png)\n')
self.assertEqual(check_doc_links.check_document(doc, self.tmp), [])


if __name__ == '__main__':
unittest.main()
107 changes: 107 additions & 0 deletions tests/test_navigation_quotes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""Tests for tools/check_doc_quotes.py: verbatim excerpts versus labelled summaries in
navigation tables, checked against the linked local record (offline)."""
import pathlib
import shutil
import sys
import tempfile
import unittest

ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'tools'))
import check_doc_quotes # noqa: E402

NAV_DOC = 'docs/integration/2026-09-29-navigation-refresh.md'
HEADER = ('| PR | Records — “verbatim excerpt” or summary: |\n'
'|---|---|\n')
RECORD = ('# Review\n\nVerdict: **ACCEPT** at the stated `fixed-d` scope. No defect was found.\n\n'
'Item 4 | pins \\| minors | CONFIRMED.\n')


class NavigationRecordOffline(unittest.TestCase):
def test_navigation_record_file_sourced_passages_are_verbatim_or_labelled(self):
report = check_doc_quotes.check_document(str(ROOT / NAV_DOC), str(ROOT))
self.assertEqual(report['failures'], [])
self.assertGreater(report['checked'], 0)
self.assertGreater(report['quotes'], 0)
self.assertGreater(report['summaries'], 0)

def test_main_exit_status(self):
rc = check_doc_quotes.main([str(ROOT / NAV_DOC), '--root', str(ROOT)])
self.assertEqual(rc, 0)


class QuoteSemantics(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp(prefix='docquotes-')
self.root = pathlib.Path(self.tmp)
(self.root / 'reviews').mkdir()
(self.root / 'reviews' / 'r.md').write_text(RECORD, encoding='utf-8')
(self.root / 'docs' / 'nested').mkdir(parents=True)

def tearDown(self):
shutil.rmtree(self.tmp)

def _report(self, row):
doc = self.root / 'docs' / 'nested' / 'a.md'
doc.write_text(HEADER + row + '\n', encoding='utf-8')
return check_doc_quotes.check_document(str(doc), self.tmp)

def test_verbatim_excerpt_is_accepted(self):
report = self._report('| #1 | [r](../../reviews/r.md) — “Verdict: ACCEPT at the stated fixed-d scope. No defect was found.” |')
self.assertEqual(report['failures'], [])
self.assertEqual((report['checked'], report['quotes']), (1, 1))

def test_omissions_in_order_are_accepted(self):
report = self._report('| #1 | [r](../../reviews/r.md) — “Verdict: ACCEPT … No defect was found … CONFIRMED.” |')
self.assertEqual(report['failures'], [])

def test_pieces_out_of_order_are_rejected(self):
report = self._report('| #1 | [r](../../reviews/r.md) — “No defect was found … Verdict: ACCEPT” |')
self.assertEqual([f[0] for f in report['failures']], ['not-verbatim'])

def test_paraphrase_in_quotation_marks_is_rejected(self):
report = self._report('| #1 | [r](../../reviews/r.md) — “Items 1–4: CONFIRMED; verdict ACCEPT at scope.” |')
self.assertEqual([f[0] for f in report['failures']], ['not-verbatim'])

def test_escaped_pipe_inside_cell_matches_source(self):
report = self._report('| #1 | [r](../../reviews/r.md) — “Item 4 \\| pins \\| minors \\| CONFIRMED.” |')
self.assertEqual(report['failures'], [])

def test_labelled_summary_with_source_verdict_words_is_accepted(self):
report = self._report('| #1 | [r](../../reviews/r.md) — summary: items 1–4 CONFIRMED; ACCEPT at the stated scope |')
self.assertEqual(report['failures'], [])
self.assertEqual((report['checked'], report['summaries']), (1, 1))

def test_labelled_summary_with_foreign_verdict_word_is_rejected(self):
report = self._report('| #1 | [r](../../reviews/r.md) — summary: continuum HOLD; finite ACCEPT |')
self.assertEqual([f[0] for f in report['failures']], ['verdict-word-not-in-source'])

def test_passage_without_preceding_link_is_rejected(self):
report = self._report('| #1 | “Verdict: ACCEPT” — [r](../../reviews/r.md) |')
self.assertEqual([f[0] for f in report['failures']], ['no-source-link'])

def test_missing_source_file_is_rejected(self):
report = self._report('| #1 | [r](../../reviews/none.md) — “Verdict: ACCEPT” |')
self.assertEqual([f[0] for f in report['failures']], ['source-missing'])

def test_github_link_is_unchecked_offline_not_failed(self):
report = self._report('| #1 | [c](https://github.com/o/r/pull/1#issuecomment-2) — “anything at all” |')
self.assertEqual(report['failures'], [])
self.assertEqual((report['checked'], report['unchecked_offline']), (0, 1))

def test_nearest_preceding_link_governs_each_passage(self):
(self.root / 'reviews' / 's.md').write_text('Disposition: AMEND the ledger.\n', encoding='utf-8')
row = ('| #1 | [r](../../reviews/r.md) — “No defect was found.”'
'<br>[s](../../reviews/s.md) — “Disposition: AMEND the ledger.” |')
self.assertEqual(self._report(row)['failures'], [])
row_wrong = ('| #1 | [r](../../reviews/r.md) — “No defect was found.”'
'<br>[s](../../reviews/s.md) — “No defect was found.” |')
self.assertEqual([f[0] for f in self._report(row_wrong)['failures']], ['not-verbatim'])

def test_header_row_quotation_marks_are_ignored(self):
report = self._report('| #1 | [r](../../reviews/r.md) — summary: no defect |')
self.assertEqual((report['quotes'], report['summaries']), (0, 1))


if __name__ == '__main__':
unittest.main()
77 changes: 77 additions & 0 deletions tools/check_doc_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Document-relative Markdown link check (standard library only; fail-closed).

For every Markdown link ``[text](target)`` in the given documents:

* an external target (``http://`` / ``https://``) must be a bare URL: no whitespace,
parentheses or quotes inside the target (commentary belongs outside the link);
* an anchor-only target (``#...``) is accepted as written;
* a local target is resolved FROM THE DIRECTORY OF THE CONTAINING DOCUMENT (not the
repository root), must stay inside the repository tree, and must exist in the
checked-out tree.

Exit status 0 only when every link in every document passes; otherwise the failing
links are listed and the exit status is 1. Usage::

python -B -S tools/check_doc_links.py PROOF_INDEX.md docs/integration/FILE.md

The optional ``--root DIR`` names the repository root (default: the parent of this
file's directory). Scientific effect: NONE — this checks navigation only.
"""
import argparse
import json
import os
import re
import sys

LINK = re.compile(r'(?<!!)\[[^\]]*\]\(([^)]*)\)')


def check_document(doc_path, root):
"""Return a list of (kind, target) failures for one document."""
root = os.path.realpath(root)
doc_real = os.path.realpath(doc_path)
if not doc_real.startswith(root + os.sep):
return [('document-outside-root', doc_path)]
with open(doc_real, encoding='utf-8') as handle:
text = handle.read()
doc_dir = os.path.dirname(doc_real)
failures = []
for match in LINK.finditer(text):
target = match.group(1)
if target.startswith('http://') or target.startswith('https://'):
if re.search(r'[\s()"\']', target):
failures.append(('commentary-in-url', target))
continue
if target.startswith('#'):
continue
if re.search(r'[\s()"\']', target) or not target:
failures.append(('commentary-in-target', target))
continue
path_part = target.split('#', 1)[0]
resolved = os.path.realpath(os.path.join(doc_dir, path_part))
if resolved != root and not resolved.startswith(root + os.sep):
failures.append(('escapes-tree', target))
continue
if not os.path.exists(resolved):
failures.append(('missing', target))
return failures


def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument('documents', nargs='+')
parser.add_argument('--root', default=os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
args = parser.parse_args(argv)
report = {'root': os.path.realpath(args.root), 'documents': {}, 'passed': True}
for doc in args.documents:
failures = check_document(doc, args.root)
report['documents'][doc] = {'failures': [list(f) for f in failures]}
if failures:
report['passed'] = False
print(json.dumps(report, indent=2, sort_keys=True))
return 0 if report['passed'] else 1


if __name__ == '__main__':
raise SystemExit(main())
Loading
Loading