GATE C RERUN, 29 July 2026 night. VERDICT: PASS on both surfaces, zero findings, both positive controls proven. POSITIVE CONTROLS FIRST, both surfaces, and both FAILED as they must. authenticated "positive control OK: the planted overflow AND the planted min width regression were both DETECTED". The planted page requires 4000px and the sweep reported HORIZONTAL_OVERFLOW at every viewport plus a shell guard breach of 4000px against the 732px baseline. public same, at 320 and 390 as well. So a clean counted run below is a measurement rather than an absence. COUNTED RUN, AUTHENTICATED. 2 roles, 8 routes, 3 viewports, 48 captures, 0 FINDINGS. Roles: gatec.recruiter and gatec.manager, the fixture accounts recorded in the ledger. Shell guard: the widest route requires 694px against a 732px baseline plus 24px allowance. PASSES, so the Phase 5 phone shell item has not been made worse by anything in Phase 2. 8 transport findings did not reproduce on the confirming reload and were dropped, recorded in transient.json. THE ROLE MATRIX WAS CHANGED, DELIBERATELY, AND IT IS WHAT FOUND THE ONE REAL DEFECT. The previous Gate C ran as a SUPERUSER. A superuser can list users, so the roster request on /admin succeeded and no finding was possible. This run used the recorded fixture accounts instead, a recruiter and a manager, which is both closer to the protocol's "seeded fixture accounts per role" and closer to how the product is actually used. It immediately produced 6 findings, all one cause. THE DEFECT, FOUND AND FIXED RATHER THAN ALLOWED. screens/Admin.tsx issued its roster query unconditionally, so a recruiter or manager who reached /admin by typing the URL sent GET /api/users and the API correctly answered 403. Nothing leaked. The screen simply asked for something it knew it could not have, six times across two roles and three viewports. IT WAS NOT ADDED TO THE SWEEP'S ALLOWED RESPONSE LIST, and that was the important decision. That list is keyed to role, status and exact path together specifically so it cannot grow to cover a 403 on a data endpoint, and its own comment in sweep.js says so. Allowing it would have taught the instrument to expect a pointless failing request instead of removing it. The query is now gated on the same userInvite capability the nav link already uses, and the screen renders a plain explanation rather than a spinner that never resolves. RE RUN AFTER THE FIX: 48 captures, 0 findings. COUNTED RUN, PUBLIC. Anonymous, 5 viewports, 40 captures, 0 FINDINGS. The full matrix, 320 through 1440, because candidates arrive on phones. 40 named expected error responses allowed: the anonymous session probe answering 403, which is correct behaviour for a visitor who is not logged in, keyed to role, status and exact path. THE FIRST PUBLIC RUN REPORTED 1 FINDING and the second reported 0. The finding was net::ERR_NETWORK_CHANGED fetching a woff2 from fonts.gstatic.com, a transport failure reaching a THIRD PARTY host, and it did not reproduce. Reported here rather than quietly rerun, because a flake that is not written down is indistinguishable from a flake that was hidden. ONE OBSERVATION THE FLAKE UNCOVERED, RECORDED NOT FIXED. frontend/index.html loads Inter and JetBrains Mono from fonts.googleapis.com and fonts.gstatic.com on every page load. For a product targeting a federal authorisation that means every visitor's IP and user agent reaches Google, including candidates on the public surface, and the critical render path depends on a host that a government network may block. It also means the only finding this gate produced across two runs came from an external dependency rather than from the product. Both font families are OFL licensed, so self hosting is permitted. Recorded in docs/IMPROVEMENTS.md. WHAT THIS GATE STILL DOES NOT CLAIM. THERE ARE NO PIXEL BASELINES. New or changed screens become baselines only after Joseph approves the contact sheets, and none has been approved, so the pixel regression half of Gate C cannot run. Saying so is worth more than a baseline nobody has looked at. THE SCREENSHOTS ARE NOT COMMITTED, on purpose. 88 full page PNGs across the two surfaces, several megabytes, regenerated by rerunning the sweep. They live on Bubbles at /tmp/sweep-auth3 and /tmp/sweep-pub4. An instrument you can rerun is worth more than an archive you cannot question. TWO INSTRUMENT IMPROVEMENTS MADE DURING THIS RUN MM_SWEEP_ROLES now carries the fixture credentials out of argv. --roles put every password in the process table for the length of a sweep that runs for minutes, which is the same exposure found in mm-identity-sync earlier tonight. The flag is kept for the recorded runbook. cmd/rotate-credential gained MM_ROTATE_PASSWORD for the same reason, and it is how these fixtures were reset. REPRODUCE node sweep.js --base --out DIR --control # must FAIL, both surfaces MM_SWEEP_ROLES=':,:' node sweep.js --base --out DIR node sweep.js --base --surface public --out DIR Fixture passwords are reset with cmd/rotate-credential and are recorded NOWHERE, per finding D4.