feat: people-pages for all TODO institutions markers
- Created 27 new people-pages for all TODO-marked persons
- Replaced all TODO: people-page markers with wiki-links in institutions
- Cross-referenced back to institutions from each people-page
- 0 TODO markers remaining
New people-pages:
shayne-coplan, jim-zemlin, wang-changhu, yang-bingyang, demis-hassabis,
clement-delangue, thomas-kurian, fei-fei-li, christopher-manning,
sam-altman, ilya-sutskever, moritz-kaminski, elon-musk, nicolas-burtey,
elizabeth-stark, olaoluwa-osuntokun, jan-leike, oren-etzioni,
ali-farhadi, jaime-sevilla, max-tegmark, daniela-rus, jared-kaplan,
alex-atallah, andrew-moore, tuomas-sandholm, mitchell-baker
2026-06-25 21:44:25 +02:00
#!/usr/bin/env python3
"""
Pre - commit hook : Validate all [ [ . . . ] ] wiki - links in wiki / * . md files .
Link format : [ [ path / to / file . md ] ] or [ [ path / to / file . md | Label ] ]
Resolution : SOURCE - RELATIVE ( matches Forgejo / Gitea Markdown rendering behavior )
- Links resolve relative to the current file ' s directory
- [ [ foo . md ] ] in wiki / architecture / bar . md → wiki / architecture / foo . md
- [ [ . . / tools / foo . md ] ] in wiki / architecture / bar . md → wiki / tools / foo . md
- [ [ concepts / llm / foo . md ] ] in wiki / concepts / llm / bar . md → wiki / concepts / llm / concepts / llm / foo . md ( BROKEN )
The third case is the " path confusion " bug — links that look root - relative but
double - prefix when interpreted source - relative . This script flags them so they
can be rewritten using either same - directory basename or proper . . / - prefix .
Usage :
python3 scripts / check - wiki - links . py # check + report
python3 scripts / check - wiki - links . py - - strict # exit 1 on warnings too
"""
import os
import re
import sys
from pathlib import Path
REPO_ROOT = Path ( __file__ ) . resolve ( ) . parent . parent
WIKI_DIR = REPO_ROOT / " wiki "
LINK_RE = re . compile ( r ' \ [ \ [([^ \ ]|]+)(?: \ |([^ \ ]]+))? \ ] \ ] ' )
def find_wiki_files ( ) :
""" Find all .md files under wiki/. """
files = [ ]
for root , dirs , filenames in os . walk ( WIKI_DIR ) :
for f in filenames :
if f . endswith ( ' .md ' ) :
files . append ( Path ( root ) / f )
return sorted ( files )
2026-06-25 21:47:40 +02:00
def strip_code_spans ( content : str ) - > str :
""" Remove inline-code spans (between backticks) so we don ' t check links inside them.
Also removes fenced code blocks ( ` ` ` . . . ` ` ` ) .
"""
# Remove fenced code blocks first
lines = content . split ( ' \n ' )
out = [ ]
in_fence = False
for line in lines :
if line . strip ( ) . startswith ( ' ``` ' ) :
in_fence = not in_fence
continue
if not in_fence :
out . append ( line )
content = ' \n ' . join ( out )
# Remove inline code spans
content = re . sub ( r ' `[^` \ n]+` ' , ' ' , content )
return content
feat: people-pages for all TODO institutions markers
- Created 27 new people-pages for all TODO-marked persons
- Replaced all TODO: people-page markers with wiki-links in institutions
- Cross-referenced back to institutions from each people-page
- 0 TODO markers remaining
New people-pages:
shayne-coplan, jim-zemlin, wang-changhu, yang-bingyang, demis-hassabis,
clement-delangue, thomas-kurian, fei-fei-li, christopher-manning,
sam-altman, ilya-sutskever, moritz-kaminski, elon-musk, nicolas-burtey,
elizabeth-stark, olaoluwa-osuntokun, jan-leike, oren-etzioni,
ali-farhadi, jaime-sevilla, max-tegmark, daniela-rus, jared-kaplan,
alex-atallah, andrew-moore, tuomas-sandholm, mitchell-baker
2026-06-25 21:44:25 +02:00
def resolve_link ( link_path : str , source_file : Path ) - > Path :
"""
Resolve a wiki link source - relative ( matching Forgejo behavior ) .
Also tries with . md suffix appended if needed .
"""
if link_path . startswith ( ' http ' ) :
return None
# Strip anchor
if ' # ' in link_path :
link_path = link_path . split ( ' # ' , 1 ) [ 0 ]
# Strip trailing slash
link_path = link_path . rstrip ( ' / ' )
# Try as-is (relative to source file's directory)
target = ( source_file . parent / link_path ) . resolve ( )
if target . exists ( ) :
return target
# Try with .md suffix
if not link_path . endswith ( ' .md ' ) :
target = ( source_file . parent / ( link_path + ' .md ' ) ) . resolve ( )
if target . exists ( ) :
return target
return None
def has_path_confusion ( link_path : str , source_file : Path ) - > bool :
"""
Detect ' path confusion ' : link like [ [ concepts / llm / foo . md ] ] from a file in
wiki / concepts / llm / bar . md would resolve to wiki / concepts / llm / concepts / llm / foo . md .
Returns True if the link looks root - relative but is used from inside a subdir .
"""
if link_path . startswith ( ' http ' ) or link_path . startswith ( ' ../ ' ) or ' / ' not in link_path :
return False
# Check if link_path starts with a directory the source file is already in
try :
rel = source_file . relative_to ( WIKI_DIR )
source_dir_parts = rel . parent . parts
link_parts = Path ( link_path ) . parts
# If link starts with the same subdirectory chain
if len ( link_parts ) > = len ( source_dir_parts ) :
if link_parts [ : len ( source_dir_parts ) ] == source_dir_parts :
return True
except ValueError :
pass
return False
def check_all_links ( ) :
files = find_wiki_files ( )
errors = [ ]
warnings = [ ]
stats = {
' total_links ' : 0 ,
' valid_links ' : 0 ,
' broken_links ' : 0 ,
' confusion_warnings ' : 0 ,
' files_checked ' : 0 ,
}
for source_file in files :
stats [ ' files_checked ' ] + = 1
rel_source = source_file . relative_to ( REPO_ROOT )
content = source_file . read_text ( encoding = ' utf-8 ' )
2026-06-25 21:47:40 +02:00
# Strip code spans so we don't false-positive on example syntax in backticks
content = strip_code_spans ( content )
feat: people-pages for all TODO institutions markers
- Created 27 new people-pages for all TODO-marked persons
- Replaced all TODO: people-page markers with wiki-links in institutions
- Cross-referenced back to institutions from each people-page
- 0 TODO markers remaining
New people-pages:
shayne-coplan, jim-zemlin, wang-changhu, yang-bingyang, demis-hassabis,
clement-delangue, thomas-kurian, fei-fei-li, christopher-manning,
sam-altman, ilya-sutskever, moritz-kaminski, elon-musk, nicolas-burtey,
elizabeth-stark, olaoluwa-osuntokun, jan-leike, oren-etzioni,
ali-farhadi, jaime-sevilla, max-tegmark, daniela-rus, jared-kaplan,
alex-atallah, andrew-moore, tuomas-sandholm, mitchell-baker
2026-06-25 21:44:25 +02:00
for match in LINK_RE . finditer ( content ) :
link_path = match . group ( 1 ) . strip ( )
stats [ ' total_links ' ] + = 1
if link_path . startswith ( ' http ' ) :
stats [ ' valid_links ' ] + = 1
continue
# Resolve source-relative
target = resolve_link ( link_path , source_file )
if target :
stats [ ' valid_links ' ] + = 1
else :
2026-06-25 21:47:40 +02:00
# Skip template placeholders like {slug}
if ' { ' in link_path :
stats [ ' valid_links ' ] + = 1
continue
feat: people-pages for all TODO institutions markers
- Created 27 new people-pages for all TODO-marked persons
- Replaced all TODO: people-page markers with wiki-links in institutions
- Cross-referenced back to institutions from each people-page
- 0 TODO markers remaining
New people-pages:
shayne-coplan, jim-zemlin, wang-changhu, yang-bingyang, demis-hassabis,
clement-delangue, thomas-kurian, fei-fei-li, christopher-manning,
sam-altman, ilya-sutskever, moritz-kaminski, elon-musk, nicolas-burtey,
elizabeth-stark, olaoluwa-osuntokun, jan-leike, oren-etzioni,
ali-farhadi, jaime-sevilla, max-tegmark, daniela-rus, jared-kaplan,
alex-atallah, andrew-moore, tuomas-sandholm, mitchell-baker
2026-06-25 21:44:25 +02:00
# Try alternative suggestions
basename = Path ( link_path ) . name
matches = [ m for m in WIKI_DIR . rglob ( basename ) if ' test-fixtures ' not in str ( m ) ]
error = {
' source ' : str ( rel_source ) ,
' link ' : link_path ,
' suggestions ' : [ str ( m . relative_to ( REPO_ROOT ) ) for m in matches [ : 3 ] ] ,
}
errors . append ( error )
stats [ ' broken_links ' ] + = 1
# Check for path confusion (link works repo-relative but broken source-relative)
2026-06-25 21:47:40 +02:00
# Skip template placeholders like {slug}
if ' { ' not in link_path and has_path_confusion ( link_path , source_file ) :
feat: people-pages for all TODO institutions markers
- Created 27 new people-pages for all TODO-marked persons
- Replaced all TODO: people-page markers with wiki-links in institutions
- Cross-referenced back to institutions from each people-page
- 0 TODO markers remaining
New people-pages:
shayne-coplan, jim-zemlin, wang-changhu, yang-bingyang, demis-hassabis,
clement-delangue, thomas-kurian, fei-fei-li, christopher-manning,
sam-altman, ilya-sutskever, moritz-kaminski, elon-musk, nicolas-burtey,
elizabeth-stark, olaoluwa-osuntokun, jan-leike, oren-etzioni,
ali-farhadi, jaime-sevilla, max-tegmark, daniela-rus, jared-kaplan,
alex-atallah, andrew-moore, tuomas-sandholm, mitchell-baker
2026-06-25 21:44:25 +02:00
# Only warn if the link is actually broken source-relative
if not resolve_link ( link_path , source_file ) :
correct_basename = Path ( link_path ) . name
warnings . append ( {
' source ' : str ( rel_source ) ,
' link ' : link_path ,
' note ' : f " Path confusion — link starts with { link_path . split ( ' / ' ) [ 0 ] } / which doubles the source-directory prefix. Use same-directory basename or proper ../-prefix. " ,
' suggestion ' : correct_basename ,
} )
stats [ ' confusion_warnings ' ] + = 1
return errors , warnings , stats
def main ( ) :
strict = ' --strict ' in sys . argv
errors , warnings , stats = check_all_links ( )
print ( " Wiki Link Report " )
print ( " ================ " )
print ( f " Files checked: { stats [ ' files_checked ' ] } " )
print ( f " Total links: { stats [ ' total_links ' ] } " )
print ( f " Valid links: { stats [ ' valid_links ' ] } " )
print ( f " Broken links: { stats [ ' broken_links ' ] } " )
print ( f " Path confusion warns: { stats [ ' confusion_warnings ' ] } " )
print ( )
if errors :
print ( f " BROKEN LINKS ( { len ( errors ) } ): " )
print ( " - " * 60 )
for e in errors [ : 30 ] :
print ( f " { e [ ' source ' ] } " )
print ( f " Link: [[ { e [ ' link ' ] } ]] " )
if e [ ' suggestions ' ] :
print ( f " Did you mean: { e [ ' suggestions ' ] [ 0 ] } " )
print ( )
if len ( errors ) > 30 :
print ( f " ... and { len ( errors ) - 30 } more " )
if warnings :
print ( f " \n PATH CONFUSION WARNINGS ( { len ( warnings ) } ): " )
print ( " - " * 60 )
for w in warnings [ : 20 ] :
print ( f " { w [ ' source ' ] } " )
print ( f " Link: [[ { w [ ' link ' ] } ]] " )
print ( f " { w [ ' note ' ] } " )
print ( f " Suggestion: [[ { w [ ' suggestion ' ] } ]] (same dir) or [[../...]] " )
print ( )
if len ( warnings ) > 20 :
print ( f " ... and { len ( warnings ) - 20 } more " )
failed = False
if stats [ ' broken_links ' ] > 0 :
print ( f " \n ❌ { stats [ ' broken_links ' ] } broken link(s) — commit blocked. " )
failed = True
if stats [ ' confusion_warnings ' ] > 0 :
print ( f " \n ⚠️ { stats [ ' confusion_warnings ' ] } path confusion warning(s). " )
if strict :
print ( " Strict mode: commit blocked. " )
failed = True
if not failed :
print ( " \n ✅ All wiki links valid. " )
sys . exit ( 1 if failed else 0 )
if __name__ == ' __main__ ' :
main ( )