_headers for CARLOS: releases a year, /1/ five minutes, hashed assets immutable; red then green open
On branch fiesta-1-1
Implementer: Claude Opus 5.5 (claude-opus-5-5), default reasoning setting.
Commits: b1e5fd0 (site.Build writes _headers), 326efa0 (the www test server applies it), 378b68d (the approved CHANGELOG line). Hub CI passed on 378b68d: gofmt, no-rastrillo, size, history and 50-suite@49, 49 of 49 shards, no FAIL lines.
What ships
site.Build now takes site.Rules{Redirects, Immutable} instead of variadic redirects. www.Rules(pages) supplies both, so fiesta site, the www tests and the cmd test can't keep one and drop the other. The built _headers has 24 paths, 3,278 bytes, and its longest line is 71 characters:
/1/*:public, max-age=300,Access-Control-Allow-Origin: *,Cross-Origin-Resource-Policy: cross-origin./1.0.0/*and/1.1.0/*:public, max-age=31536000, immutableand the same two cross-origin headers.- 21 hashed gallery assets under
/design-system/assets/, each listed exactly: a year and immutable, with no CORS headers. - Everything else has no rule, so it keeps
no-cache.
gallery.Fingerprinted lists a hashed name only when its plain twin sits beside it with the same bytes. A name that only looks hashed is not listed.
Build refuses each of these with a message:
- more than 90 paths: it says to give the hashed assets a directory of their own, or to ask CARLOS for a higher limit
- a line over 2,048 characters
- any header outside the five the edge allows, or a value the edge refuses
- an immutable address that is not a page, or that holds
:,*,%,?,#or white space - a page named
_headers
Red, then green
The first red run used a stub: Build wrote no _headers and Fingerprinted returned nothing.
--- FAIL: TestOneServesTheNewestOneX the site holds 13 files, want 14
--- FAIL: TestPagesCannotShadowAVersion/_headers Build accepted the page _headers
--- FAIL: TestTheHeadersCacheAReleaseForAYearAndAMajorForMinutes _headers is "" (want the whole file)
--- FAIL: TestANewReleaseAddsItsOwnHeadersRule with 1.0.0, _headers names ; want /1/* /1.0.0/*
--- FAIL: TestAnImmutableAddressMustBeAPage Build accepted the immutable address "/a/missing.css" (and 9 more)
--- FAIL: TestTheHeadersStopShortOfTheEdgesLimit Build wrote 91 paths into _headers
--- FAIL: TestTheHeadersSetOnlyWhatTheEdgeAllows Set-Cookie: a=b was accepted; the edge refuses it (and 8 more)
--- FAIL: TestFingerprintedNamesTheHashedTwinsOnly Fingerprinted is [] want [3 names]
--- FAIL: TestTheSitesHeaders the site has no _headersThe second red run used the old test server, with the handler commit stashed and the new tests kept:
--- FAIL: TestTheSitesHeaders/served_as_the_edge_serves_them
/1/fiesta.css gets Cache-Control "no-store"; want "public, max-age=300"
/1.1.0/fiesta.css gets Cache-Control "no-store"; want "public, max-age=31536000, immutable"
/_headers answers 200; the edge never serves it
--- FAIL: TestServeSiteSendsHeadersLikeTheEdge
GET /1/fiesta.css: 200, Cache-Control "no-store" ...; want ["public, max-age=300" "" "*"]
GET /gone/x: 404, Cache-Control "no-store" ...; want 404 ["" "" ""]Green, run locally:
go test ./internal/site ./internal/www ./cmd/fiesta ./internal/gallery -run Fingerprinted- the root package, with the role tests, TestVersionIsTheChangelogsNewestRelease and the release tests
-tags browser ./internal/www, in 305s- staticcheck on the touched packages
Each mutation below was applied, failed, and was reverted:
| | Mutation | Fails | |---|---|---| | M1 | releases get five minutes | TestTheHeadersCache..., TestTheSitesHeaders/exactly, /immutable | | M2 | /M/ gets a year | TestTheHeadersCache..., TestTheSitesHeaders/exactly | | M3 | plain assets listed too | TestFingerprinted..., TestTheSitesHeaders/exactly | | M4 | any name with two dots listed | TestFingerprinted... | | M5 | www.Rules drops Immutable | TestTheSitesHeaders/exactly (21 missing rules) | | M6 | stop at 100 instead of 90 | TestTheHeadersStopShortOfTheEdgesLimit | | M7 | any origin accepted | TestTheHeadersSetOnlyWhatTheEdgeAllows | | M8 | any header name accepted | TestTheHeadersSetOnlyWhatTheEdgeAllows | | M9 | no line limit | TestTheHeadersStopShortOfTheEdgesLimit | | M10 | immutable address not checked against the pages | TestAnImmutableAddressMustBeAPage | | M11 | CORS headers on the hashed assets | TestTheHeadersCache..., TestTheSitesHeaders/exactly | | D1 | headers on every status | TestServeSiteSendsHeadersLikeTheEdge (/gone/x 404) | | D2 | rules matched after the rewrite | TestTheSitesHeaders/served (/1/fiesta.css immutable), TestServeSite... | | D3 | _headers served as a file | both | | D4 | first rule wins | TestServeSiteSendsHeadersLikeTheEdge | | P1 | parser mirror takes any header | TestServeSiteRefusesAHeadersFileCarlosWouldRefuse | | P2 | parser mirror takes any origin | the same |
Validation with CARLOS's own code
carlos v0.31.1 cannot validate a static tree without shipping it:
carlos ship --checkis for rootfs images only.carlos vetchecks a release that has already shipped.
Instead I ran edge.PackDir, the function carlos ship --kind static calls, together with parseHeaders and lookup. They ran from a carlos/platform checkout whose internal/edge is identical to v0.31.1. The test file was added with go test -overlay, so nothing was written to that checkout. The tree was fiesta site -releases releases -site site at 378b68d.
PackDir(.../site/public): ok
parseHeaders: 24 rules
/1/fiesta.css map[Access-Control-Allow-Origin:[*] Cache-Control:[public, max-age=300] Cross-Origin-Resource-Policy:[cross-origin]]
/1.1.0/fiesta.css map[Access-Control-Allow-Origin:[*] Cache-Control:[public, max-age=31536000, immutable] Cross-Origin-Resource-Policy:[cross-origin]]
/1.0.0/i18n/es.json map[Access-Control-Allow-Origin:[*] Cache-Control:[public, max-age=31536000, immutable] Cross-Origin-Resource-Policy:[cross-origin]]
/design-system/assets/gallery.fc83762b4ec7573c.css map[Cache-Control:[public, max-age=31536000, immutable]]
/design-system/assets/gallery.css map[]
/design-system/ map[]
/index.html map[]
/static/site.css map[]
--- PASS: TestZZFiestaSiteTree (1.35s)Nothing was shipped.
Decisions
- The test server applies
_headers. It parses the file with a mirror of the edge's grammar, sendsno-cacheand then the matching rules on 200s only, and never serves either rules file. The browser drives still getno-store, so a second load reaches the handler andDelayandSeensee it. - The
_redirectslimit test changed._headersnow stops a site at 45 majors, so the test can no longer build 2,000 majors. It reaches 2,000_redirectsrules through the pages' rules instead. - No release rebuild. CHANGELOG.md is not embedded or copied into any release or page, so
releases/1.1.0is unchanged.