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
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -105,8 +105,13 @@ jobs:
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }}
# Boolean dispatch inputs become the strings "true"/"false". Off-dispatch
# this context is empty, so the quoted comparison below is false — the
# same as interpolating a blank ${{ }} into the script, without putting
# GitHub context into a shell.
REFRESH_LIGHTHOUSE_BASELINE: ${{ github.event.inputs.refresh_lighthouse_baseline }}
run: |
if [ "${{ github.event.inputs.refresh_lighthouse_baseline }}" = "true" ]; then
if [ "$REFRESH_LIGHTHOUSE_BASELINE" = "true" ]; then
# A baseline refresh is a focused measurement operation, not a
# synthetic full repository change. Keep the trusted workflow/perf
# contracts without starting unrelated build, UI, DB or container jobs.
Expand Down
7 changes: 5 additions & 2 deletions src/app/caring-contacts/layout.tsx
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
import type { Metadata } from "next";
import type { ReactNode } from "react";

import { PRIVATE_APP_ROBOTS_METADATA } from "@/lib/crawler-policy";

// Listed in the live tools catalogue by the owner's decision of 19 August 2026, but never
// indexed: this workspace holds invented patients only and must not appear in a search result
// where its synthetic nature is not visible.
// where its synthetic nature is not visible. Use the shared private-app robots object so
// this route does not emit a narrower child override beside the root noindex extras.
export const metadata: Metadata = {
title: "Caring Contacts - Clinical KB",
robots: { index: false, follow: false },
robots: PRIVATE_APP_ROBOTS_METADATA,
};

export default function CaringContactsLayout({ children }: { children: ReactNode }) {
Expand Down
17 changes: 17 additions & 0 deletions tests/ci-cache-safety.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -656,6 +656,23 @@ describe("Lighthouse budget routing", () => {
expect(workflow).toMatch(/workflow_dispatch:\n\s+inputs:\n\s+refresh_lighthouse_baseline:/);
});

it("does not interpolate the lighthouse refresh dispatch input into a run script", () => {
// github.event.inputs is untrusted in `run:` (shell injection). Bind it through
// env and quote the variable. Job-level `if:` expressions may still read the
// input context directly — those are not a shell.
const classifyStep = sourceSegment(workflow, "name: Classify changed files", "sync-pr-policy-body:", {
label: "CI change-scope classify step",
});
const runScript = classifyStep.split(/\n\s+run:\s*\|\n/)[1] ?? "";
expect(runScript, "could not read the classify step run script").not.toBe("");
expect(classifyStep).toMatch(
/REFRESH_LIGHTHOUSE_BASELINE:\s*\$\{\{\s*github\.event\.inputs\.refresh_lighthouse_baseline\s*\}\}/,
);
expect(runScript).toContain('"$REFRESH_LIGHTHOUSE_BASELINE"');
expect(runScript).not.toContain("github.event.inputs.refresh_lighthouse_baseline");
expect(runScript).not.toMatch(/\$\{\{[\s\S]*?\}\}/);
});

it("pairs promotion to pr-required with merge_group coverage", () => {
// The budget skips merge_group ONLY because it is advisory and outside
// pr-required, where it could add ~7 minutes of merge latency without ever
Expand Down
5 changes: 5 additions & 0 deletions tests/crawler-policy.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import { expect, it } from "vitest";

import { metadata as caringContactsMetadata } from "../src/app/caring-contacts/layout";
import robots from "../src/app/robots";
import { PRIVATE_APP_ROBOTS_METADATA } from "../src/lib/crawler-policy";

Expand All@@ -18,3 +19,7 @@ it("serves restrictive search metadata through crawlable routes", () => {
},
});
});

it("keeps Caring Contacts on the shared private-app robots object", () => {
expect(caringContactsMetadata.robots).toEqual(PRIVATE_APP_ROBOTS_METADATA);
});
Loading