Repository navigation
fix(cache): lane compile-cache writes per pull request and bound them at admission - #239
Conversation
… at admission A compile-cache token now carries a lane: base (default-branch executions), pr-<n> (one pull request) or read-only. A pull request writes only its own lane and reads it before base, so no pull request can plant an entry another pull request or a default-branch gate links. contextful-gate names base only from a default-branch push. Every PUT reserves its length in a per-repository CompileCacheLedger Durable Object against the repository bound and a per-execution budget; the workflow ends the execution's record when its run finishes, which refuses that token's further writes. A deploy without the binding serves reads and refuses writes. The overlay merge carries the template's Durable Object bindings and migrations into an overlay whose migrations lead the template's. The sweep resumes from a stored cursor, holds one repository's listing at a time, measures repositories it lists whole into the ledger and releases what it deletes from the rest. A GET of an entry past half its TTL rewrites it, so eviction and expiry count from last use. Only an agent run that armed the AgentBudget kills it at run end, and the command wrapper puts its closing parenthesis on its own line. Spec: specs/adr/0007-cost-efficient-execution.md
There was a problem hiding this comment.
AI code review — 🛑 Request changes
Risk tier: full · 1 critical · 1 warnings · 0 suggestions
Reviewers: security
1. ⚠️ Warning — Token version bump invalidates active executions
📍 apps/dispatcher/src/compile-cache-token.ts:27
Changing the token version from cc1 to cc2 makes every token already issued by a prior deployment unverifiable, even though those tokens may remain valid until the execution ceiling. A rolling deployment can therefore cause in-flight workflows to lose compile-cache access or fail cache requests. Keep cc1 verification support during a compatibility window (while minting cc2), or explicitly drain/complete active executions before rollout.
2. 🛑 Critical — Compile-cache ledger binding is not provisioned by this change
📍 apps/dispatcher/src/env.ts:433-440
Declaring 'COMPILE_CACHE_LEDGER' in the 'Env' type does not create a Durable Object binding or migration in Wrangler configuration. The supplied diff adds no 'durable_objects.bindings' entry or migration to the deployment config, so production deployments will have this binding undefined and every compile-cache PUT will be refused with 503, despite the new ledger implementation. Add the binding and 'CompileCacheLedger' migration to the committed Wrangler configuration for each deployment.
Follow-up to #238. Pull-request code holds its compile-cache token, so the cache must not trust what a pull request writes. This PR confines a pull request's writes to that pull request and enforces the byte bounds when a write is admitted.
Problem & Insight
Take
cc2). The lane isbase(default-branch executions),pr-<n>orread-only.pr-<n>writes its own lane and reads it, then@base. No token reads another PR's lane. Paths cannot name a lane (@is outside the segment charset).contextful-gatepassesbaseonly from a default-branch push. A PR gate passespr-<n>, and a dispatch that names no lane getsread-only. A caller that could namebasealready holdsHMAC_SECRET, so the lane grants it nothing new.CompileCacheLedgerDurable Object, one per repository. Every PUT reserves its length against the repository bound and a new per-execution budget (COMPILE_CACHE_MAX_EXECUTION_BYTES, default 10 GiB). When the run ends, the workflow ends the execution's record, and the token can write nothing more. A deploy without the binding serves reads and returns 503 for every PUT.waitUntil). The copy comes from R2 itself, so a PR token cannot change what the copy holds.infra/overlay-limits.mjscarries the template's Durable Object bindings and migrations into an overlay whose migration tags are a strict prefix of the template's. An overlay whose migrations diverge is left unchanged.run-end.ts). Before this, every compile-cache part made an AgentBudget RPC.)on its own line, so a command that ends in a#comment still runs.Key actions / todos
@base. With the two-tier gate,@baseholds only what the heavy parts compile, so light parts share no compiles across PRs. Follow-up: run the light parts' compiles on default-branch pushes, or measure the hit rate before deciding.proposed.lint 0, typecheck 0,
pnpm test: 219 files, 3045 passed.wrangler deploy --dry-runlists theCOMPILE_CACHE_LEDGERbinding.