Skip to content
Merged
133 changes: 68 additions & 65 deletions data/outstanding-issues-snapshot.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": 2,
"id": "7dc2d09a-b05c-4d10-b198-263af3c0f45e",
"createdOn": "2026-08-22",
"action": "update",
"payload": {
"id": "#RSD9EJ",
"source": "PR #2298 push attempts 2026-08-22/23 (squash-merged as f3d1a3cce2c943ad3083425ed9c7c46dbef23087; the feature branch and its commits 90b5de61f/7703188ef/55f3a47fa are NOT reachable from any ref -- view them on the PR page, not via git); scripts/guard-push.mjs; scripts/check-ledger-write-discipline.mjs; commit 9a382a050 (#2294) is the reconcile whose applied/ records triggered the false positive and IS on main. REPRODUCTION without those commits: branch from main, let a reconcile PR land on main, merge origin/main into the branch, then push -- guard-push refuses with 'applied/<uuid>.json was introduced without moving the identical pending request from the base' while 'node scripts/check-ledger-write-discipline.mjs --base origin/main --head HEAD' passes on the same tree. The divergence between those two results is the bug.",
Comment thread
cursor[bot] marked this conversation as resolved.
"baseRowFingerprint": "b687cb212853e6b4c4df866ea79d625556cad416a0c3e406a2044f82691a4d4b"
}
}
14 changes: 11 additions & 3 deletions docs/outstanding-issues.md

Large diffs are not rendered by default.

139 changes: 84 additions & 55 deletions scripts/check-docs-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@
* Blocking for maintained docs: runs in verify:cheap and CI. Historical
* directories and dated point-in-time records stay excluded unless --all is
* requested, so preserved history cannot block unrelated PRs.
*
* Outstanding-issues inbox citations are special: an immutable request is
* queued at `docs/outstanding-issues-inbox/<uuid>.json` and, after reconcile,
* lives at `docs/outstanding-issues-inbox/applied/<uuid>.json`. Ledger rows
* (and the request's own source/detail) keep citing the pending path because
* the JSON is immutable. Treat the applied sibling as the same file.
*/

import { existsSync, readFileSync, readdirSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";

import { applyRequestBatch, validateRequest } from "./ledger-inbox.mjs";

Expand Down Expand Up @@ -66,10 +72,28 @@ const VERBATIM_DIRS = new Set(["codex-cloud-review"]);
const APP_ROUTE_GROUPS = ["(search-app)"];
const OUTSTANDING_ISSUES = "docs/outstanding-issues.md";
const OUTSTANDING_ISSUES_INBOX = "docs/outstanding-issues-inbox";
const INBOX_REQUEST_NAME = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.json$/i;

/**
* Pending inbox UUID paths keep being cited after reconcile moves the file
* into `applied/`. Return that applied sibling, or null when the path is not
* a pending inbox request citation.
*/
export function appliedInboxFallbackPath(repoRelative) {
const cleaned = repoRelative.replace(/\/$/, "");
const prefix = `${OUTSTANDING_ISSUES_INBOX}/`;
const appliedPrefix = `${OUTSTANDING_ISSUES_INBOX}/applied/`;
if (!cleaned.startsWith(prefix) || cleaned.startsWith(appliedPrefix)) return null;
const name = cleaned.slice(prefix.length);
if (name.includes("/") || !INBOX_REQUEST_NAME.test(name)) return null;
return `${appliedPrefix}${name}`;
}

function repoPathExists(repoRelative) {
const cleaned = repoRelative.replace(/\/$/, "");
if (existsSync(path.join(repoRoot, cleaned))) return true;
const applied = appliedInboxFallbackPath(cleaned);
if (applied && existsSync(path.join(repoRoot, applied))) return true;

if (!cleaned.startsWith("src/app/") || cleaned.includes("src/app/(")) return false;
const appRelative = cleaned.slice("src/app/".length);
Expand Down Expand Up @@ -176,68 +200,73 @@ function isExternalLink(value) {
return /^([a-z][a-z0-9+.-]*:|\/\/)/i.test(value) || value.startsWith("#");
}

let missing = 0;
let checked = 0;

for (const target of defaultTargets()) {
const absoluteTarget = path.join(repoRoot, target);
if (!existsSync(absoluteTarget)) continue;
const markdown = markdownForTarget(target, absoluteTarget);
const targetDir = path.posix.dirname(target);
const failures = [];

const check = (repoRelative, label) => {
if (ALLOWLIST.has(repoRelative)) return;
checked += 1;
if (!repoPathExists(repoRelative)) failures.push(label);
};

// Inline code spans: repo-root-relative repo paths.
for (const rawCandidate of codeSpanCandidates(markdown)) {
const value = stripSuffixes(rawCandidate);
const base = ROOT_PREFIXES.some((prefix) => value.startsWith(prefix)) ? globBaseDir(value) : null;
if (base !== null) {
if (ALLOWLIST.has(value)) continue;
function main() {
let missing = 0;
let checked = 0;

for (const target of defaultTargets()) {
const absoluteTarget = path.join(repoRoot, target);
if (!existsSync(absoluteTarget)) continue;
const markdown = markdownForTarget(target, absoluteTarget);
const targetDir = path.posix.dirname(target);
const failures = [];

const check = (repoRelative, label) => {
if (ALLOWLIST.has(repoRelative)) return;
checked += 1;
if (!existsSync(path.join(repoRoot, base))) failures.push(`${value} (glob base '${base}' missing)`);
continue;
if (!repoPathExists(repoRelative)) failures.push(label);
};

// Inline code spans: repo-root-relative repo paths.
for (const rawCandidate of codeSpanCandidates(markdown)) {
const value = stripSuffixes(rawCandidate);
const base = ROOT_PREFIXES.some((prefix) => value.startsWith(prefix)) ? globBaseDir(value) : null;
if (base !== null) {
if (ALLOWLIST.has(value)) continue;
checked += 1;
if (!existsSync(path.join(repoRoot, base))) failures.push(`${value} (glob base '${base}' missing)`);
continue;
}
if (!looksLikeRootPath(value)) continue;
check(value, value);
}
if (!looksLikeRootPath(value)) continue;
check(value, value);
}

// Markdown link targets: repo docs use both repo-root-relative targets
// (`src/lib/env.ts`) and file-relative targets (`codebase-index.md`,
// `../AGENTS.md`). Accept whichever resolves, confined to the repository.
for (const rawCandidate of linkCandidates(markdown)) {
if (isExternalLink(rawCandidate)) continue;
const value = stripSuffixes(rawCandidate);
if (value === "" || value.includes("*") || /[<>{}$\\]/.test(value) || /\s/.test(value)) continue;
const relative = path.posix.normalize(path.posix.join(targetDir === "." ? "" : targetDir, value));
if (relative.startsWith("..")) {
// Markdown link targets: repo docs use both repo-root-relative targets
// (`src/lib/env.ts`) and file-relative targets (`codebase-index.md`,
// `../AGENTS.md`). Accept whichever resolves, confined to the repository.
for (const rawCandidate of linkCandidates(markdown)) {
if (isExternalLink(rawCandidate)) continue;
const value = stripSuffixes(rawCandidate);
if (value === "" || value.includes("*") || /[<>{}$\\]/.test(value) || /\s/.test(value)) continue;
const relative = path.posix.normalize(path.posix.join(targetDir === "." ? "" : targetDir, value));
if (relative.startsWith("..")) {
checked += 1;
failures.push(`${rawCandidate} (escapes repository root)`);
continue;
}
const rootStyle = path.posix.normalize(value);
const candidates = rootStyle === relative || rootStyle.startsWith("..") ? [relative] : [rootStyle, relative];
if (candidates.some((candidate) => ALLOWLIST.has(candidate))) continue;
checked += 1;
failures.push(`${rawCandidate} (escapes repository root)`);
continue;
const found = candidates.some((candidate) => repoPathExists(candidate));
if (!found)
failures.push(rawCandidate === relative ? relative : `${rawCandidate} (tried ${candidates.join(", ")})`);
}

if (failures.length > 0) {
missing += failures.length;
console.error(`\n${target}:`);
for (const failure of failures) console.error(` MISSING ${failure}`);
}
const rootStyle = path.posix.normalize(value);
const candidates = rootStyle === relative || rootStyle.startsWith("..") ? [relative] : [rootStyle, relative];
if (candidates.some((candidate) => ALLOWLIST.has(candidate))) continue;
checked += 1;
const found = candidates.some((candidate) => repoPathExists(candidate));
if (!found)
failures.push(rawCandidate === relative ? relative : `${rawCandidate} (tried ${candidates.join(", ")})`);
}

if (failures.length > 0) {
missing += failures.length;
console.error(`\n${target}:`);
for (const failure of failures) console.error(` MISSING ${failure}`);
if (missing > 0) {
console.error(`\ndocs link check FAILED: ${missing} missing path(s) across ${checked} checked references.`);
process.exit(1);
}
}

if (missing > 0) {
console.error(`\ndocs link check FAILED: ${missing} missing path(s) across ${checked} checked references.`);
process.exit(1);
console.log(`docs link check passed: ${checked} repo path references resolve.`);
}

console.log(`docs link check passed: ${checked} repo path references resolve.`);
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
if (invokedDirectly) main();
15 changes: 15 additions & 0 deletions scripts/ci-change-scope.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,15 @@ const perfExclusionPatterns = [
"src/instrumentation.ts",
"src/sentry.server.config.ts",
"src/sentry.edge.config.ts",
// Developer-hub payload only. `src/lib/developer-area/ledger-snapshot.ts`
// imports this JSON, and the only route importers are under
// `src/app/mockups/development/` (already excluded; 404 in production). A
// ledger reconcile that closes the last P1 must not pay a 7-minute
// Lighthouse budget run, and must not fail merge on TBT noise from
// `/documents/search`. Measured on PR #2302: this file alone flipped
// perf_changed and the job failed mobile TBT +32.7% against a baseline
// the same change cannot move.
"data/outstanding-issues-snapshot.json",
];

function isPerfChangedPath(filePath) {
Expand Down Expand Up @@ -983,6 +992,12 @@ function selfTest() {
["public/therapy-compass-data/therapies-home.json", "data/medications-snapshot.json"],
{ perf_changed: true },
);
// Mockup-only ledger snapshot: same `data/` root as medications, but it
// cannot reach a budgeted route. Closing the last P1 on PR #2302 otherwise
// forced Lighthouse onto a docs/ledger reconcile.
assertScope("perf-off-for-outstanding-issues-snapshot", ["data/outstanding-issues-snapshot.json"], {
perf_changed: false,
});
assertScope("perf-on-for-build-config", ["next.config.ts", "postcss.config.mjs", "tsconfig.json"], {
perf_changed: true,
});
Expand Down
20 changes: 20 additions & 0 deletions tests/check-docs-links.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";

import { appliedInboxFallbackPath } from "../scripts/check-docs-links.mjs";

describe("appliedInboxFallbackPath", () => {
it("maps a pending inbox UUID citation to the applied sibling", () => {
expect(appliedInboxFallbackPath("docs/outstanding-issues-inbox/edebb730-91d9-42f5-bd93-ca2abb9678bc.json")).toBe(
"docs/outstanding-issues-inbox/applied/edebb730-91d9-42f5-bd93-ca2abb9678bc.json",
);
});

it("does not wrap an already-applied path or a nested inbox file", () => {
expect(
appliedInboxFallbackPath("docs/outstanding-issues-inbox/applied/edebb730-91d9-42f5-bd93-ca2abb9678bc.json"),
).toBeNull();
expect(appliedInboxFallbackPath("docs/outstanding-issues-inbox/README.md")).toBeNull();
expect(appliedInboxFallbackPath("docs/outstanding-issues.md")).toBeNull();
expect(appliedInboxFallbackPath("docs/outstanding-issues-inbox/not-a-uuid.json")).toBeNull();
});
});
23 changes: 14 additions & 9 deletions tests/developer-hub-page.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ vi.mock("@/components/account-data-provider", () => ({
* snapshot — so the band is exercised against the shape the route actually
* loads rather than a hand-built fixture that could drift from it. `null` means
* "do not override", which is also how the assertions below read the true
* count. The committed snapshot has `p1 === 2`, so without this the singular
* and zero branches are never reached at all.
* count. The committed snapshot can have `p1 === 0` (no open P1s), so the
* override is what reaches the singular and non-zero branches.
*/
const p1 = vi.hoisted(() => ({ value: null as number | null }));

Expand Down Expand Up @@ -109,12 +109,17 @@ describe("developer hub page — synthetic-data warning", () => {
describe("developer hub page — needs-you-now band", () => {
it("reports the snapshot's own P1 count", () => {
// No override, so this is the real committed snapshot: the band must agree
// with the data the route actually loads, not merely with itself.
// with the data the route actually loads, not merely with itself. When the
// snapshot has no P1s the page must omit the band rather than render a
// settled-looking "0 blocking items" line — that is the same contract as
// the explicit zero-override case below.
const { counts } = loadLedgerSnapshot();
expect(counts.p1).toBeGreaterThan(0);

render(<DeveloperHubPage />);
expect(screen.getByTestId("developer-hub-needs-you-now")).toHaveTextContent(String(counts.p1));
if (counts.p1 > 0) {
expect(screen.getByTestId("developer-hub-needs-you-now")).toHaveTextContent(String(counts.p1));
} else {
expect(screen.queryByTestId("developer-hub-needs-you-now")).toBeNull();
}
});

it("carries no text beyond the computed count", () => {
Expand Down Expand Up @@ -144,9 +149,9 @@ describe("developer hub page — needs-you-now band", () => {
});

it("renders nothing rather than a reassuring all-clear when there are no blockers", () => {
// The committed snapshot has p1 = 2, so this branch is otherwise never
// exercised. A band reading "0 blocking items" would be a settled-looking
// statement about work the page cannot see.
// A band reading "0 blocking items" would be a settled-looking statement
// about work the page cannot see. The override keeps this branch explicit
// even on days the committed snapshot already has no P1s.
p1.value = 0;
render(<DeveloperHubPage />);

Expand Down
Loading