Skip to content

Commit eeea74a

Browse files
authored
feat(hardening): safe tooling, ui layout, and workflow guards bundle (#2180)
1 parent 1dad6b8 commit eeea74a

10 files changed

Lines changed: 98 additions & 22 deletions

docs/scripts-index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ the task before you start.
135135
(`ledger:lookup` / `ledger:append` / `ledger:dedupe` / `ledger:rotate`). Never hand-write a row.
136136
- `merge-branch-review-ledger.mjs` — the `merge=ledger` union driver from `.gitattributes`;
137137
`check-branch-review-ledger.mjs` fails if that protection is lost.
138-
- `sync-open-pr-branches.mjs` (`sync:pr-branches`) — anti-churn sync for stale open PR heads;
138+
- `sync-open-pr-branches.mjs` (`sync:pr-branches`), `sync-pr-branches.mjs` (compatibility entry point) — anti-churn sync for stale open PR heads;
139139
refuses a missing or bot `gh` identity. `sweep-merged-branches.mjs` — merged-branch sweep.
140140
- `reconciliation-preflight.mjs`, `reconciliation-evidence-pack.mjs` — broad chat/worktree
141141
reconciliation entry point and its evidence bundle; see `docs/reconciliation-playbook.md`.

scripts/check-bundle-budget.mjs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,12 +463,16 @@ export function resolveBaselineCommitDistance(baselineSha, cwd = root, exec = ex
463463
return null;
464464
}
465465
try {
466-
const count = exec("git", ["-C", cwd, "rev-list", "--count", `${baselineSha.trim().toLowerCase()}..HEAD`], {
466+
const raw = exec("git", ["-C", cwd, "rev-list", "--count", `${baselineSha.trim().toLowerCase()}..HEAD`], {
467467
encoding: "utf8",
468468
stdio: ["ignore", "pipe", "ignore"],
469-
}).trim();
469+
});
470+
const count = typeof raw === "string" ? raw.trim() : "";
471+
if (!/^\d+$/.test(count)) {
472+
return null;
473+
}
470474
const n = Number.parseInt(count, 10);
471-
return Number.isFinite(n) ? n : null;
475+
return Number.isSafeInteger(n) ? n : null;
472476
} catch {
473477
return null;
474478
}
@@ -590,6 +594,10 @@ export function selfTest() {
590594
const nullSha = resolveBaselineCommitDistance("not-a-sha");
591595
check("resolveBaselineCommitDistance: invalid sha returns null", nullSha === null);
592596

597+
const nonNumericExec = () => "12trailing\n";
598+
const nonNumeric = resolveBaselineCommitDistance("a".repeat(40), root, nonNumericExec);
599+
check("resolveBaselineCommitDistance: non-numeric count returns null", nonNumeric === null);
600+
593601
const mockExec = () => "12\n";
594602
const distance = resolveBaselineCommitDistance("a".repeat(40), root, mockExec);
595603
check("resolveBaselineCommitDistance: mock exec returns count", distance === 12);

scripts/check-outstanding-issues.mjs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ import { execFileSync } from "node:child_process";
3333
import { readFileSync } from "node:fs";
3434
import { createHash } from "node:crypto";
3535

36-
import { canonicalLegacyIssueId, isIssueDisplayId, issueIdCitations, parseIssueIdCell } from "./issue-id.mjs";
36+
import {
37+
canonicalLegacyIssueId,
38+
isIssueDisplayId,
39+
issueIdCitations,
40+
normalizeIssueDisplayId,
41+
parseIssueIdCell,
42+
} from "./issue-id.mjs";
3743

3844
export const ISSUES_PATH = "docs/outstanding-issues.md";
3945

@@ -260,8 +266,7 @@ export function parseIssues(markdown) {
260266
// row plainly present in Open items, which made the optimistic-concurrency check
261267
// unreachable for exactly the rows that have it available (they carry a ULID).
262268
export function issueRowFingerprint(markdown, issueId) {
263-
const rawId = String(issueId).trim();
264-
const id = rawId.toUpperCase();
269+
const id = normalizeIssueDisplayId(issueId);
265270
const legacy = id.match(/^#(\d+)$/);
266271
const number = legacy ? Number(legacy[1]) : null;
267272
if (legacy) {

scripts/issue-id.mjs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ export function canonicalLegacyIssueId(number) {
5454
return `#${String(number).padStart(3, "0")}`;
5555
}
5656

57+
export function normalizeIssueDisplayId(value) {
58+
const trimmed = String(value ?? "").trim();
59+
if (LEGACY_ISSUE_ID_PATTERN.test(trimmed)) return trimmed;
60+
return trimmed.toUpperCase();
61+
}
62+
5763
export function isIssueDisplayId(value) {
58-
const id = String(value ?? "")
59-
.trim()
60-
.toUpperCase();
64+
const id = normalizeIssueDisplayId(value);
6165
if (DISPLAY_ISSUE_ID_PATTERN.test(id)) return true;
6266
if (!LEGACY_ISSUE_ID_PATTERN.test(id)) return false;
6367
const number = Number(id.slice(1));

scripts/sync-open-pr-branches.mjs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,30 @@ function main() {
102102

103103
const plan = [];
104104
for (const pr of prs) {
105+
const skipReason = shouldSkip(pr) || (pr.autoMergeRequest ? "auto-merge-armed" : null);
106+
if (skipReason) {
107+
plan.push({
108+
pr,
109+
behindBy: 0,
110+
requiredCiInFlight: false,
111+
stateUnavailable: false,
112+
action: "skip",
113+
reason: skipReason,
114+
});
115+
continue;
116+
}
117+
105118
let behindBy = 0;
106119
let requiredCiInFlight = false;
120+
let stateUnavailable = false;
107121
try {
108122
const cmp = ghJson([
109123
"api",
110124
`repos/${repo}/compare/${encodeURIComponent(BASE)}...${encodeURIComponent(pr.headRefName)}`,
111125
]);
112126
behindBy = cmp.behind_by ?? 0;
113127
requiredCiInFlight =
114-
behindBy > 0 && !shouldSkip(pr)
128+
behindBy > 0 && pr.headRefOid?.length > 0
115129
? hasRequiredCiInFlight(
116130
ghJson([
117131
"api",
@@ -122,9 +136,12 @@ function main() {
122136
} catch {
123137
behindBy = 0;
124138
requiredCiInFlight = false;
139+
stateUnavailable = true;
125140
}
126-
const decision = classifyPr({ ...pr, requiredCiInFlight }, behindBy);
127-
plan.push({ pr, behindBy, requiredCiInFlight, ...decision });
141+
const decision = stateUnavailable
142+
? { action: "skip", reason: "api-unavailable" }
143+
: classifyPr({ ...pr, requiredCiInFlight }, behindBy);
144+
plan.push({ pr, behindBy, requiredCiInFlight, stateUnavailable, ...decision });
128145
}
129146

130147
console.log(`Open PRs against ${BASE}: ${plan.length} (${APPLY ? "APPLY" : "dry-run"})`);
@@ -134,6 +151,12 @@ function main() {
134151
);
135152
}
136153

154+
const unavailable = plan.filter((row) => row.stateUnavailable).length;
155+
if (unavailable > 0) {
156+
console.error(`Incomplete scan: GitHub state unavailable for ${unavailable} PR(s); re-run to confirm.`);
157+
process.exitCode = 1;
158+
}
159+
137160
if (!APPLY) {
138161
console.log("\nDry-run only. Re-run with --apply to update behind branches.");
139162
return;

src/components/clinical-dashboard/favourites-command-library-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?:
13671367
<div className="mx-auto grid min-w-0 max-w-[40rem] gap-4 px-4 py-8 sm:px-6">
13681368
<header data-testid="favourites-command-library" className="flex min-w-0 flex-wrap items-baseline gap-x-3">
13691369
<h1 className="text-balance text-2xl-minus font-bold leading-tight tracking-tight text-[color:var(--text-heading)] sm:text-2xl">
1370-
Favourites
1370+
{sharedHomePresentation.favourites.title}
13711371
</h1>
13721372
<p className="text-pretty text-sm-minus font-medium leading-6 text-[color:var(--text-muted)]">
13731373
Sign up to save favourites and access them across devices.

src/components/clinical-dashboard/master-search-header.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,9 @@ export function MasterSearchHeader({
17831783
function renderSearchComposer(placement: "default" | "desktop-home" | "desktop-page") {
17841784
const isDesktopHomeComposer = placement === "desktop-home";
17851785
const isDesktopPageComposer = placement === "desktop-page";
1786+
const isDefaultComposer = placement === "default";
1787+
const isPageDesktopComposerPending =
1788+
isDefaultComposer && Boolean(desktopPageComposerSlotId) && !desktopComposerPortalFallback;
17861789
const usesAnswerFooterStyle = isAnswerFooterComposer && !isDesktopHomeComposer;
17871790
const usesMobileBottomStyle = isMobileBottomComposer && !isDesktopHomeComposer;
17881791
const usesBottomComposerPlacement = usesAnswerFooterStyle || (usesMobileBottomStyle && usesPhoneSearchLayout);
@@ -1903,6 +1906,7 @@ export function MasterSearchHeader({
19031906
"universal-top-search-edge mx-auto box-border w-full px-3 py-3 sm:px-4",
19041907
stickySearchOwnedByOuterStack ? "relative z-20" : cn("sticky z-20", stickySearchTopClass),
19051908
),
1909+
isPageDesktopComposerPending && "sm:hidden",
19061910
usesBottomComposerPlacement && "answer-footer-search-edge",
19071911
usesPhoneFooterDock && "answer-footer-search-dock",
19081912
usesCompactMobileBottomStyle && "document-mobile-search-compact",
@@ -2495,16 +2499,18 @@ export function MasterSearchHeader({
24952499
const portalPlacement = desktopHomeComposerSlotId ? "desktop-home" : "desktop-page";
24962500
const homePortalPending =
24972501
Boolean(desktopHomeComposerSlotId) && homeComposerMediaEligible && !desktopComposerPortalFallback;
2498-
const pagePortalPending =
2499-
Boolean(desktopPageComposerSlotId) && !usesPhoneSearchLayout && !desktopComposerPortalFallback;
2500-
const portalPending = homePortalPending || pagePortalPending;
2502+
const portalPending = homePortalPending;
25012503
const searchComposer = searchComposerVisible ? (
25022504
<>
25032505
{/* ModeHomeTemplate and desktop page slots reserve their settled geometry
25042506
in SSR, so a temporary header fallback would make the stack grow and
25052507
shift all main content when the portal attaches (CLS 0.118 on desktop
2506-
/documents/search). A failed adoption restores the header fallback after
2507-
the bounded retry window above. */}
2508+
/documents/search). Mode home routes suppress the header fallback entirely
2509+
because the hero slot in the page body reserves geometry during SSR;
2510+
generic page slots keep the phone fallback rendered during SSR and
2511+
unknown media state while applying sm:hidden so the desktop fallback
2512+
never renders over the reserved page slot. A failed adoption restores
2513+
the header fallback after the bounded retry window above. */}
25082514
{desktopComposerPortalActive && desktopComposerPortalHost
25092515
? null
25102516
: portalPending

src/components/clinical-dashboard/use-home-mode-seed.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,11 @@ export function useHomeModeSeed({
172172
* race that redirect and leave the landing preference ignored.
173173
*/
174174
useEffect(() => {
175+
if (pathname !== "/") {
176+
homeModeSeededRef.current = false;
177+
return;
178+
}
175179
if (homeModeSeededRef.current) return;
176-
if (pathname !== "/") return;
177180
if (searchParams.has("mode") || searchParams.has("q") || searchParams.has("query")) return;
178181
homeModeSeededRef.current = true;
179182
if (landingModeForPreference(readAppPreferences().landing)) return;

tests/bundle-budget.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,17 @@ describe("bundle baseline provenance", () => {
306306
expect(resolveBaselineCommitDistance("not-a-sha")).toBeNull();
307307
});
308308

309+
it("returns null when git output is malformed or non-numeric", () => {
310+
const mockExecTrailing = vi.fn(() => "12 commits\n");
311+
expect(
312+
resolveBaselineCommitDistance(gitHead, process.cwd(), mockExecTrailing as unknown as typeof execFileSync),
313+
).toBeNull();
314+
315+
const mockExecNonNumeric = vi.fn(() => "fatal: bad revision\n");
316+
expect(
317+
resolveBaselineCommitDistance(gitHead, process.cwd(), mockExecNonNumeric as unknown as typeof execFileSync),
318+
).toBeNull();
319+
});
309320
it("defines a default stale baseline commit distance threshold", () => {
310321
expect(STALE_BASELINE_COMMIT_DISTANCE_THRESHOLD).toBe(50);
311322
});

tests/search-route-ownership.test.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,11 @@ describe("shared-search route ownership", () => {
196196
expect(headerSource).toMatch(
197197
/const homePortalPending =\s*Boolean\(desktopHomeComposerSlotId\) && homeComposerMediaEligible && !desktopComposerPortalFallback/,
198198
);
199+
expect(headerSource).toContain("const portalPending = homePortalPending;");
199200
expect(headerSource).toMatch(
200-
/const pagePortalPending =\s*Boolean\(desktopPageComposerSlotId\) && !usesPhoneSearchLayout && !desktopComposerPortalFallback/,
201+
/const isPageDesktopComposerPending =\s*isDefaultComposer && Boolean\(desktopPageComposerSlotId\) && !desktopComposerPortalFallback/,
201202
);
202-
expect(headerSource).toContain("const portalPending = homePortalPending || pagePortalPending;");
203+
expect(headerSource).toContain('isPageDesktopComposerPending && "sm:hidden"');
203204
expect(headerSource).toContain("setHomeComposerMediaEligible(mediaQuery.matches)");
204205
expect(headerSource).toContain("const portalFallbackDelayMs = 8_000");
205206
expect(headerSource).toContain("let portalFailureStartedAt: number | null = null");
@@ -456,4 +457,19 @@ describe("shared-search route ownership", () => {
456457
// mount cannot clear a restored answer thread.
457458
expect(arrival).toContain("previousPathname === null || previousPathname === pathname");
458459
});
460+
461+
it("keeps the phone composer rendered during SSR while preserving desktop layout reservation", () => {
462+
const headerSource = readFileSync(
463+
resolve(process.cwd(), "src/components/clinical-dashboard/master-search-header.tsx"),
464+
"utf8",
465+
);
466+
// SSR and unknown media state must not blank out search for phone viewports
467+
expect(headerSource).not.toMatch(
468+
/const pagePortalPending =\s*Boolean\(desktopPageComposerSlotId\) && !usesPhoneSearchLayout/,
469+
);
470+
expect(headerSource).toMatch(
471+
/const isPageDesktopComposerPending =\s*isDefaultComposer && Boolean\(desktopPageComposerSlotId\) && !desktopComposerPortalFallback/,
472+
);
473+
expect(headerSource).toContain('isPageDesktopComposerPending && "sm:hidden"');
474+
});
459475
});

0 commit comments

Comments
 (0)