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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
33 changes: 26 additions & 7 deletions .gitattributes
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,12 +86,28 @@
# would defer the prose to OURS. See NOT_DRIVER_MANAGED for that entry.
#
# This is also the row where the header's own warning is answered rather than
# accepted: deferring is safe here because `scripts/check-system-context-census.mjs`
# still reddens on every PR from the required `Lint & Repo Gates` job — it
# RE-DERIVES the census from the tree, so it catches the stale anchors a merge
# leaves behind even when nothing conflicted, which is the majority case (#13625:
# 18 anchors stale, 5 marked). The driver removes hand-merge rounds; it is never
# the only signal.
# accepted: `scripts/check-system-context-census.mjs` still reddens on every PR from
# the required `Lint & Repo Gates` job — it RE-DERIVES the census from the tree, so
# it catches the stale anchors a merge leaves behind even when nothing conflicted,
# which is the majority case (#13625: 18 anchors stale, 5 marked). The driver removes
# hand-merge rounds; it is never the only signal.
#
# ⚠️ #14064 CORRECTED the sentence that used to open that paragraph — "deferring is
# safe here BECAUSE the census gate re-derives". The gate re-derives the census and
# the anchors. It re-derives no PROSE, because prose is derived from nothing, and
# this page is the one routed path that carries both. So the argument was true and
# its domain was half the risk surface: the driver drops a side WHOLE, and on this
# page that side can carry hand-written paragraphs the gate is constitutionally
# unable to miss. Measured, not reasoned — deleting a 3-line anchor-free paragraph
# and running ten doc-family gates returned ten exit 0 over deleted documentation.
#
# Routing this file is still RIGHT (the correct anchors are on neither side; nothing
# above changes). What #14064 added is the missing half: the row now declares
# `mixed: 'line-anchors'` in scripts/regen-artifacts.mjs, and the driver refuses to
# defer SILENTLY when the incoming side carries anything but anchor numbers — it
# text-merges instead, and conflicts loudly if that cannot be done. The cheap case
# stays cheap: of the last 25 main commits to this page, 24 changed nothing but
# anchor numbers.

#
# #13731 enumerated this file's blind spot and closed it: `check:merge-driver` now
Expand All@@ -105,7 +121,10 @@
# reference indexes (#13335) and both halves of the react-blocks contract. Their
# neighbours got the other answer for reasons recorded per path — the skill docs and
# the AI skills guide are MIXED (a spliced block in hand-written prose, so a deferral
# would launder the prose), the two per-package test-typecheck ledgers are shrink-only
# would launder the prose; #14064 added a THIRD answer for the mixed file whose
# generated half no hand merge can reach — see `mixed` in scripts/regen-artifacts.mjs
# — but "not routed" remains the first answer to reach for), the two per-package
# test-typecheck ledgers are shrink-only
# ratchets, the sdui lockstep record cannot be regenerated without an objectui
# checkout, and the openapi/sbom outputs are gitignored so git never merges them.
#
Expand Down
71 changes: 69 additions & 2 deletions scripts/doc-line-anchors.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -177,14 +177,21 @@ function countLines(text, offset) {
/**
* Every `file:line` anchor a docs page writes, in document order.
*
* `start`/`end` are byte offsets of the anchor's code span **in `rawText`**, not in
* the blanked copy this walks: `blankFencedBlocks` and `blankFrontmatter` both
* preserve length, so the two agree offset for offset. They exist so a caller can
* REWRITE an anchor in place -- `blankAnchorLineNumbers` below is the one in-tree
* consumer -- without re-deriving spans through a second, divergent parser.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {{ spelling: string, line: number, kind: 'full'|'continuation'|'range-end',
* raw: string, docLine: number }[]}
* raw: string, docLine: number, start: number, end: number }[]}
*/
export function extractLineAnchors(rawText) {
const text = blankFencedBlocks(blankFrontmatter(rawText));
const spans = extractCodeSpans(text);
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number }[]} */
/** @type {{ spelling: string, line: number, kind: string, raw: string, docLine: number,
* start: number, end: number }[]} */
const anchors = [];
let currentFile = null;
let previousSpan = null;
Expand All@@ -198,6 +205,8 @@ export function extractLineAnchors(rawText) {
kind: 'full',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -210,6 +219,8 @@ export function extractLineAnchors(rawText) {
kind: 'continuation',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -222,6 +233,8 @@ export function extractLineAnchors(rawText) {
kind: 'range-end',
raw: span.value,
docLine: span.line,
start: span.start,
end: span.end,
});
previousSpan = span;
continue;
Expand All@@ -237,6 +250,60 @@ export function extractLineAnchors(rawText) {
return anchors;
}

/**
* The same page with every anchor's LINE NUMBER replaced by `#`, and nothing else
* touched.
*
* ## What this is for, and why it lives here
*
* It answers one question: *do these two revisions of a page differ in anything
* but anchor line numbers?* Two texts that compare equal after this differ only in
* the half a generator re-derives -- `check-system-context-census.mjs --fix`
* rewrites exactly these numbers and nothing else -- so discarding either revision
* loses nothing a later regeneration cannot restore. Two texts that still differ
* carry a hand-written change, which no generator can restore, and discarding
* either one is a silent deletion.
*
* `scripts/git-merge-regen.mjs` is the consumer: the `merge=os-regen` driver keeps
* OURS whole and drops THEIRS whole, which is correct for a wholly generated file
* and destroys prose on a MIXED one. This function is how the driver tells those
* two cases apart before it defers.
*
* It is written here rather than in the driver because this module is already the
* ONE reader of these anchors (module header), and a second parser -- even a
* three-line regex -- is a second definition of "anchor" that would drift from the
* gate's. Measured on the corpus that motivated the module: a regex over
* `path.ts:NNNN` alone reads 24 of 25 revisions of `system-context.mdx` as
* prose-changing, because CONTINUATION and RANGE_END anchors are not that shape.
* The correct answer, using the walk below, is 1 of 25.
*
* ⚠️ NOT a general "is this page unchanged" test. It deliberately blinds itself to
* line numbers, so a caller that cares whether an anchor MOVED must compare the raw
* texts, or read the anchors themselves.
*
* @param {string} rawText The page source, frontmatter and fences included.
* @returns {string}
*/
export function blankAnchorLineNumbers(rawText) {
const anchors = extractLineAnchors(rawText);
let out = '';
let cursor = 0;
for (const a of anchors) {
// Anchors arrive in document order and their spans never overlap, so a single
// forward pass is enough; a defensive skip keeps that assumption from silently
// corrupting the output if it ever stops holding.
if (a.start < cursor) continue;
out += rawText.slice(cursor, a.start);
// The LAST run of digits in the span is the line number in all three shapes --
// `path.ts:1234`, `:1234` and a bare `1234` -- and a path that itself carries
// digits (`0112-codes.ts:55`) keeps them, because the lookahead requires that
// nothing but non-digits follows.
out += rawText.slice(a.start, a.end).replace(/\d+(?=\D*$)/, '#');
cursor = a.end;
}
return out + rawText.slice(cursor);
}

/**
* Every cited path that carries no line number, in document order.
*
Expand Down
Loading
Loading