docs/verification.md

Verification record

Local checks ran on 2026-09-06; production verification ran on 2026-09-07. This records implementation evidence, not a promise that third-party menus never change.

Deterministic checks

Results: strict TypeScript checking passed; all 41 deterministic tests passed; both workerd integration scenarios passed; deployment dry-run passed with a 51.25 KiB bundle, 13.18 KiB gzipped.

Run npm run check for strict TypeScript checking and fixture/state/HTTP tests. Run npm run test:runtime for two integration scenarios in actual local workerd with KV and SQLite Durable Objects:

  1. Empty storage, known snapshot, combined/individual agreement, HEAD, JSON errors, CORS, ETag/304, and no public KV writes.
  2. Real scheduled entry point dispatching to the private collector, all three provider formats through controlled outbound responses, one completed snapshot, duplicate same-hour triggers, public API reads, and a cache hit after the underlying local KV key is removed.

Provider tests cover preserving rendered items, dates, validated closure, missing publication, malformed responses, 304 and content fingerprints, optional dietary/nutrition fields, and per-date source failures. State tests cover unchanged timestamps, partial outages, same-date fallback, rollover and old-date pruning, missed scheduler staleness, explicit closure, recovery, bounded concurrency, and KV failures. A failed KV read does not overwrite the previous snapshot.

Response-body limits are enforced even if Content-Length is absent. Requests have cancellation signals and 15-second deadlines covering download. Diagnostics and tests never need Cloudflare credentials.

npm run build produces a deployment bundle with KV and the private Durable Object binding. The legacy PHP directory is not bundled. CI runs type checks, deterministic tests, runtime tests, and bundle verification. Live network checks are separate so unpublished school menus cannot masquerade as code-test failures.

Live source evidence

npm run sources at 2026-09-06T22:38:21Z requested September 6 and 7 in California. Counts below are published menu-item occurrences across meals and stations, not unique recipes. They can change on a later check.

HallSept 6 itemsSept 7 itemsResult
Hoch104178Both dates available
Malott246543Both dates available
McConnell383566Both dates available
Collins441828Both dates available
Frank7870Both dates available
Frary6755Both dates available
Oldenborg00Neither date published

The live diagnostic intentionally exits 1 when any requested date is unavailable. Oldenborg's response is a successful HTTP request containing old dates, not evidence of a current closure. The API returns unavailable with MENU_NOT_PUBLISHED.

The complete live pipeline was also tested with npm run dev and Wrangler's local scheduled hook. The actual local Durable Object fetched school sources, saved local KV, and /v1/menus returned HTTP 200 with the six available halls and the explicit Oldenborg failure. This supplements the controlled runtime test; it still does not measure remote Cloudflare CPU or provision the user's account.

Sources and formats

  • Bon Appetit legacy legacy.cafebonappetit.com/api/2/menus returned HTTP 403 with an authentication-required message. Current dated pages are https://collins-cmc.cafebonappetit.com/cafe/collins/YYYY-MM-DD/, https://scripps.cafebonappetit.com/cafe/malott-dining-commons/YYYY-MM-DD/, and https://pitzer.cafebonappetit.com/cafe/mcconnell-bistro/YYYY-MM-DD/. The adapter reads strict JSON from Bamco.menu_items, matches dated meal containers, and links rendered item IDs to that JSON. It does not execute page scripts. Last-Modified is used only with previously validated data for the exact URL/date; unchanged bodies reuse parsed results.
  • Hoch uses https://api-prd.sodexomyway.net/v0.2/data/menu/13147001/15258?date=YYYY-MM-DD. Its API-Key is shipped in HMC's public browser client. Responses contain meal arrays, groups, and items. No validators were observed, so the adapter compares response fingerprints. The requested date in this date-specific endpoint supplies the service date; an empty array is unpublished, not closed.
  • Pomona feeds are https://api.pomona.edu/eatec/Frank.json, Frary.json, and Oldenborg.json. They currently return menuData(...) JSONP with explicit service dates, meal records, recipes, nutrient-column labels, and dietary answers. ETag/Last-Modified support conditional requests. The parser also accepts plain JSON. Nutrition follows the named CAL column; unknown dietary answers remain absent. Closure records cannot erase other published meals on the same day.

Public feed dates, upstream caching, and publication quality remain controlled by the schools. Hours are currently supplied only where the integrated menu source provides them. The API reports missing information rather than inventing it.

Local performance evidence

npm run benchmark at 2026-09-06T22:39:16Z captured 11 upstream requests and a roughly 548 KB snapshot. With responses replayed from memory:

OperationMedian elapsed time
Full parse and collection58.1 ms
Collection with unchanged downloaded bodies33.1 ms
Combined HTTP response before edge caching1.37 ms

These measurements explain the private Durable Object collector. They are not Cloudflare billed CPU timings. The ordinary scheduled Worker only calls it and receives a small summary. The collector has a 30-second default CPU allowance, while free ordinary Workers have 10 ms. Approximate daily usage and official quota links are in README.md.

Deployment status

Seven-day public window

The collector now stores today through six days ahead in America/Los_Angeles. Public ?date= values outside that window still return HTTP 400. Deterministic tests cover the seven-day range, including spring-forward and fall-back calendar arithmetic. All 44 deterministic tests and both workerd scenarios passed. Dry-run bundle is 53.25 KiB, 13.90 KiB gzipped.

npm run sources at 2026-09-12T22:19:36Z requested September 12 through 18. Six halls returned verified menus for every date in that window (Frank closed on the 12th, one breakfast item on the 18th). Oldenborg remains unpublished. Hoch's Sodexo feed continues through September 25. Bon Appétit dated pages for Collins, Malott, and McConnell still contain menus on sampled dates through November 1. Production still served only today and tomorrow until the next deployment and hourly collection after this change.

September 7 Collins correction

Deployed version a3af9c61-d643-4cbe-b6a0-81d4a1073bb4 reconciles Collins dated special hours. Live-source parsing produces September 7 Brunch 10:30-12:30 and Dinner 16:30-18:30; September 8 retains its four regular periods. The previous five-period September 7 response copied source menu sections without reconciling the holiday schedule.

All 43 deterministic tests and both workerd scenarios pass. Regression coverage includes other dates/halls, dinner-only exceptions, explicitly retained breakfast, missing special-period menus, and invalidating old parsed caches. Public KV changes on the next hourly collection after deployment; deployment alone does not rewrite the snapshot. The bundle is 53.10 KiB, 13.84 KiB gzipped.

Initial deployment

Deployed at https://five-c-menu-api.kainoanewton.workers.dev. Initially verified version: 16c4bbf8-aafc-4299-8a37-7fcb229e27f8. Wrangler confirmed the production KV binding, SQLite-backed MenuCollector Durable Object, and hourly 0 * * * * Cron Trigger. That bundle was 51.25 KiB, 13.18 KiB gzipped.

Production requests at approximately 2026-09-07T16:53Z confirmed a saved snapshot checked by the hourly collector at 2026-09-07T16:01:25.611Z. No manual snapshot upload was needed. Both September 7 and September 8 returned HTTP 200 with six ok halls and Oldenborg unavailable with MENU_NOT_PUBLISHED.

Live assertions passed for all fourteen individual hall/date responses matching their combined response, CORS, ETag/304, empty HEAD bodies, invalid-date 400, unknown-hall 404, and the mcconnel alias. The development-only scheduled hook returned 404 in production. The root and hall-directory routes returned 200.

This verifies production collection, persisted menu reads, and the public response contract. Cloudflare billed CPU, duration, and account-wide quota consumption have not been measured in this record. Continued upstream availability and future cron delivery are not guaranteed by a successful deployment.