Skip to content

fix(deploy): rewrites pointed at .html under cleanUrls, so / was a 404 - #27

Merged
qmarcelle merged 1 commit into
mainfrom
hac/341-vercel-rewrite-cleanurls
Aug 18, 2026
Merged

fix(deploy): rewrites pointed at .html under cleanUrls, so / was a 404#27
qmarcelle merged 1 commit into
mainfrom
hac/341-vercel-rewrite-cleanurls

Conversation

@qmarcelle

Copy link
Copy Markdown
Contributor

interlock.marcellelabs.io served every asset correctly but returned 404 on /, /cockpit and /storyboard. The cockpit itself was deployed and healthy the whole time — only the pretty URLs were broken.

Evidence

Path Before
/ 404
/cockpit 404
/storyboard 404
/media/hac-341/cockpit 200 ✅
/media/hac-341/cockpit.html 308 → clean URL → 200 ✅
evidence/view-model.json · lib/arm-view.mjs · assets/styles.css · assets/fonts/*.woff2 200 ✅

Cause

cleanUrls: true strips .html from Vercel's output filesystem. The page is served at /media/hac-341/cockpit; /media/hac-341/cockpit.html survives only as a 308 redirect rule.

A rewrite destination is resolved against that filesystem, not re-run through the redirect table. So destination: "/media/hac-341/cockpit.html" points at a path that no longer exists internally, and all three rewrites fell through to 404.

The probe that isolates it: /media/hac-341/cockpit.html gives 308 → 200 when requested externally, but the identical string as a rewrite destination gives 404. That is only possible if destinations resolve pre-redirect.

Fix

Drop .html from the three destinations. Three lines, no other change.

cleanUrls stays on — turning it off would fix the 404 too, but it hands a judge a .html URL, which is the worse trade for a judge-facing surface.

Not affected

The deep links in media/hac-341/README.md still work as written: the 308 preserves the query string, so ?run=hac330-local&proof=local&state=run.local.perturbed lands on 200 with the query intact. Verified against the live domain.

No evidence, gate, capture or cockpit source touched. 9 gates PASS on this branch.

interlock.marcellelabs.io served every asset correctly — view model,
arm-view.mjs, styles, fonts all 200 — but /, /cockpit and /storyboard
returned 404 while /media/hac-341/cockpit returned 200.

cleanUrls strips .html from the output filesystem: the page is served at
/media/hac-341/cockpit, and /media/hac-341/cockpit.html survives only as a
308 redirect rule. A rewrite destination is resolved against that
filesystem rather than re-run through the redirect table, so a destination
ending in .html points at a path that no longer exists internally.

The probe that isolates it: /media/hac-341/cockpit.html gives 308 -> 200
externally, but the identical string as a rewrite destination gives 404.
That is only possible if destinations resolve pre-redirect.

Dropping .html from the three destinations fixes it. cleanUrls stays on —
the alternative hands a judge a .html URL.

The deep links in media/hac-341/README.md are unaffected: the 308 preserves
the query string, so ?run=...&state=run.local.perturbed still lands on 200.

No evidence, gate or cockpit source touched.
Copilot AI lite review requested due to automatic review settings August 18, 2026 12:00
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
interlock Ready Ready Preview Aug 18, 2026 12:00pm

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qmarcelle
qmarcelle merged commit caf40db into main Aug 18, 2026
23 checks passed
qmarcelle added a commit that referenced this pull request Aug 23, 2026
…kit (#33)

## `/` and `/cockpit` rendered nothing

`vercel.json` rewrites `/` and `/cockpit` onto `media/hac-341/cockpit.html`. A
rewrite serves those bytes without changing the request URL, and a
document-relative specifier resolves against the *request* URL — so

    import { armView } from './lib/arm-view.mjs'

resolved to `/lib/arm-view.mjs` and aborted. Same for `./lib/guide.mjs` and the
`./evidence/view-model.json` fetch. Both entry points returned HTTP 200 and
rendered a blank page with the body text "Skip to the run". Confirmed against
the live deployment: `/media/hac-341/cockpit.html` renders 1628 characters,
`/` and `/cockpit` render 15.

These are the two URLs a judge visits. #27 made `/` stop 404ing; it did not
make it work.

Nothing caught it because every gate loads the file path directly, which is the
one path where the relative form happens to resolve. The three references are
now anchored at `/media/hac-341/`, and the cockpit gate refuses a
document-relative import or fetch statically — no server, no deployment, and it
fails on the specifier rather than on the blank page. One negative proof.

## Cold-read kit

`media/hac-341/cold-read/` — moderator script and per-reader response sheet.
Status is READY_FOR_HUMAN_TEST: prepared, not run, nothing claimed.

It names the public custom domain, because every `*.vercel.app` URL for this
project is behind Vercel SSO (`ssoProtection: all_except_custom_domains`) and
would send a cold reader to a login page. All six tester URLs verified 200
logged out.

The sheet records verbatim answers rather than scores, and lists the automatic
failures — inferring Agent Runtime or Agent Gateway, collapsing the two proof
classes into one experiment, reading ALLOW as authorized or EXECUTED as
OBSERVED — as overriding any score.

pnpm run check exit 0, 645 tests, typecheck clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants