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
37 changes: 11 additions & 26 deletions .github/scripts/audit-app-zone-shell.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,17 @@ export const REQUIRED_NAV_LABELS = ["Research", "Model", "API", "Donate"];
const TOP_SHELL_SELECTOR =
'header, nav, [data-testid*="header" i], [data-testid*="site-header" i], a, button, img, [aria-label]';

// Routes intentionally served WITHOUT a child-rendered PolicyEngine header.
// These tools are embedded under policyengine.org, which already provides the
// site header/nav, so the maintainers chose not to duplicate the PolicyEngine
// shell inside the child app. These routes are still audited for liveness
// (HTTP status, runtime errors, blank pages) — only the top-shell brand/nav
// assertion is skipped. Remove an entry to re-enforce the full shell on it.
// /uk/scotland-income-tax-reform — PolicyEngine/scotland-income-tax-reform#8
// /uk/student-loan-visualisation — PolicyEngine/student-loan-visualisation#3
// /us/obbba-household-explorer — PolicyEngine/obbba-household-by-household#240
// /uk/uc-rebalancing — PolicyEngine/uc-rebalancing
// /uk/cancelling-fuel-duty-rise — PolicyEngine/cancelling-fuel-duty-rise
// /uk/young-worker-nics — PolicyEngine/young-worker-nics
// /uk/nics-exemption-inactive-employees — PolicyEngine/nics-exemption-inactive-employees
// /uk/electricity-vat-cut — PolicyEngine/electricity-vat-cut
// /uk/bus-fare-cap — PolicyEngine/bus-fare-cap
export const SHELL_BRAND_EXEMPT_SOURCES = [
"/uk/scotland-income-tax-reform",
"/uk/student-loan-visualisation",
"/us/obbba-household-explorer",
"/uk/uc-rebalancing",
"/uk/cancelling-fuel-duty-rise",
"/uk/young-worker-nics",
"/uk/nics-exemption-inactive-employees",
"/uk/electricity-vat-cut",
"/uk/bus-fare-cap",
];
// Routes temporarily served WITHOUT a child-rendered PolicyEngine header.
// Empty and it stays that way: every zone child now renders the site shell
// itself (the multizone rewrite does not inject the parent shell), and the
// zone-shell test asserts this list only ever shrinks. The last nine legacy
// entries were removed after each repo shipped its shell — see
// scotland-income-tax-reform#11, student-loan-visualisation#4,
// obbba-household-by-household#245, uc-rebalancing#4,
// cancelling-fuel-duty-rise#6, young-worker-nics#5,
// nics-exemption-inactive-employees#11, electricity-vat-cut#1, and
// bus-fare-cap#9.
export const SHELL_BRAND_EXEMPT_SOURCES = [];

export function isShellBrandExempt(source) {
return SHELL_BRAND_EXEMPT_SOURCES.some(
Expand Down
31 changes: 13 additions & 18 deletions .github/scripts/audit-app-zone-shell.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
inspectTopShellData,
isShellBrandExempt,
resolveDestinationForSource,
SHELL_BRAND_EXEMPT_SOURCES,
shouldAllowDestinationFallback,
sourcePathFromSitemapLoc,
} from "./audit-app-zone-shell.mjs";
Expand Down Expand Up @@ -250,31 +251,25 @@ describe("shouldAllowDestinationFallback", () => {
});

describe("isShellBrandExempt", () => {
test("exempts configured routes and their subpaths", () => {
assert.equal(isShellBrandExempt("/uk/scotland-income-tax-reform"), true);
test("exempts nothing — every zone child renders its own shell", () => {
// The list only ever shrinks; it drained to empty when the last nine
// legacy children shipped their shells. Keep it empty: new zone embeds
// must render the site shell from day one.
assert.equal(SHELL_BRAND_EXEMPT_SOURCES.length, 0);
});

test("enforces the shell on formerly exempt and never-exempt routes alike", () => {
assert.equal(isShellBrandExempt("/uk/scotland-income-tax-reform"), false);
assert.equal(
isShellBrandExempt("/uk/student-loan-visualisation/budget-impact"),
true,
);
assert.equal(isShellBrandExempt("/uk/uc-rebalancing"), true);
assert.equal(isShellBrandExempt("/us/obbba-household-explorer"), true);
assert.equal(isShellBrandExempt("/uk/young-worker-nics"), true);
assert.equal(
isShellBrandExempt("/uk/nics-exemption-inactive-employees"),
true,
false,
);
assert.equal(isShellBrandExempt("/uk/electricity-vat-cut"), true);
assert.equal(isShellBrandExempt("/uk/bus-fare-cap"), true);
});

test("does not exempt shelled children, other routes, or partial-name collisions", () => {
// snap-qc-sim renders the full site shell (PolicyEngine/snap-qc-sim#26),
// so its route left the exemption list and the audit enforces it.
assert.equal(isShellBrandExempt("/us/obbba-household-explorer"), false);
assert.equal(isShellBrandExempt("/uk/bus-fare-cap"), false);
assert.equal(
isShellBrandExempt("/us/snap-payment-error-simulator"),
false,
);
assert.equal(isShellBrandExempt("/uk/marriage"), false);
assert.equal(isShellBrandExempt("/uk/uc-rebalancing-extended"), false);
});
});
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const PolicyEngineLogo = "/assets/logos/policyengine/white.svg";

### The shell rule

**Everything served on policyengine.org carries the PolicyEngine site shell — header/nav and footer — and zone children must render it themselves** (the aca-calc / snap-qc-sim pattern: global nav with the wordmark, Research, Model, API, Donate, plus the site footer links). There is no shelled fallthrough for zone paths: the website proxies each tool's path straight to the child, and `AppPage`/apps.json alone has **no production route** (removing a zone rewrite 404s the path — see #1143/#1144). The `app-zone-shell-audit` enforces the header on zone routes; `SHELL_BRAND_EXEMPT_SOURCES` lists the remaining bare legacy children and should only ever shrink — remove a tool's entry in the same change that ships its shell.
**Everything served on policyengine.org carries the PolicyEngine site shell — header/nav and footer — and zone children must render it themselves** (the aca-calc / snap-qc-sim pattern: global nav with the wordmark, Research, Model, API, Donate, plus the site footer links). There is no shelled fallthrough for zone paths: the website proxies each tool's path straight to the child, and `AppPage`/apps.json alone has **no production route** (removing a zone rewrite 404s the path — see #1143/#1144). The `app-zone-shell-audit` enforces the header on zone routes; `SHELL_BRAND_EXEMPT_SOURCES` is empty and stays that way — the last nine bare legacy children shipped their shells in August 2026, and new zone embeds must render the shell from day one.

### Next.js multizones (default for all new tools)

Expand Down
1 change: 1 addition & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Enforce the site shell on the SNAP payment error simulator route and fix four stale app iframe sources
- Enforce the site shell on the last nine bare embedded tools, emptying the zone-shell audit exemption list
- Redirect /uk/cec (the retired Citizens' Economic Council simulator, embedded by citizensecon.org.uk) to /uk instead of a 404
Loading