carlos / platform Public
- P1 — A shared snapshot can move the moves worker backwards (§4.3). A pass can capture snapshot A after reconciliation, pause, and acquire
w.passafter another pass has already processed newer snapshot B. Today, the record walk happens insidew.pass(internal/host/movespass.go:1061, :1079). This matters because move state selects physical operations, including landing and reverting (internal/host/movesoffline_land.go:63). Taking a copy “after Reconcile” does not preserve that ordering. Require: select and validate the snapshot underw.pass, reject superseded snapshots, and define the lock order. HoldingreconcileMuacross the moves pass is unsafe because move operations acquire it themselves (internal/host/movesoffline.go:1065).
- P1 — The backstop can overwrite a newer view (§§4.3–4.4). The specified wholesale replacement permits this interleaving: a backstop lists an app before a record appears; an incremental refresh sees the creation and reconciliation mints its route; the older backstop finishes and publishes a view without that record. Reading generations before the walk prevents swallowing a concurrent bump, but does not prevent publishing stale absence. Reconcile treats absence as retirement (internal/provision/reconcile.go:1533); the existing serialization explicitly protects against this grant-losing race (internal/host/agent.go:1529). Require: serialize backstop acquisition/publication with incremental refreshes and consuming passes, or discard/revalidate any rebuild overtaken by a newer publication. Add this exact interleaving to §8.
- P1 — The wrapper omits mutation and streaming capabilities required today (§§3.3, 7). Forwarding only the three named extensions loses
ConditionalDeleter:DeleteIfMatchexplicitly refuses stores without it (internal/release/conditional_delete.go:29), and the hostops boundary implements even ordinary deletion through that extension (internal/hostops/mutation_boundary.go:188). Merely forwarding conditional deletion would also bypass a hook attached only toDelete.PutFromis another independent mutation path, and losing streaming introduces a 64 MiB fallback limit (internal/release/store.go:260). These regressions ship even with indexed readers disabled. Require: preserve all existing capabilities, includingConditionalDeleter,PutStreamer,Opener,SizeLister,PageLister, andBucketListDirs; bump after successful mutations through every applicable method.
- P2 — A 304 prevents failed scopes from retrying (§4.2). After observing index generation 42, suppose an app refresh fails. Its applied generation correctly remains 41, but the next conditional index GET can return 304. Step 1 then returns before retrying the outstanding scope, contradicting step 3. Existing strict readers reject failed record reads rather than returning a complete set (internal/provision/reconcile.go:1962). Require: retain the observed index separately from successfully applied generations; retry outstanding scopes on 304, preserving incompleteness until they succeed.
- P2 — The view names the wrong manifest and omits the sidecar manifest (§§4.1–4.2). Provisioning reads
instances.json, notmanifest.json(internal/provision/provision.go:321). Its contents supply seeds and update policy (internal/provision/reconcile.go:995). Sidecars separately readsidecars.json(internal/provision/sidecar.go:122); that manifest affects placement and therefore which records enter the removal-protecting set (internal/provision/sidecar_reconcile.go:328). Require: explicitly cache both manifests through their existing parsers, retaining their distinct missing/error semantics.
- P2 — The hostops integration is unspecified (§3.3). Wrapping outside the mutation boundary sends the index PUT through a classifier that has no
control/changes.jsonfamily and rejects unmatched keys (internal/hostops/mutation_classifier.go:47, :168). Wrapping underneath avoids that rejection, but the outer boundary must also expose the new read extensions; its current forwarding surface is explicit (internal/hostops/mutation_boundary.go:108). The console constructs multiple layers and a separate control writer (internal/console/claim_runtime.go:33). Require: specify the exact wrapper order and bump authority, then test through the final claim-enabled store handles.
- P2 — Fleet-door readers cannot read the index (§§3.4, 6–7). The door allows selected control keys and tenant-scoped prefixes, but not
control/changes.json(internal/edge/storedoor.go:431). Its object GET also implements neither conditional GET nor the proposed ETag listing (internal/edge/storedoor.go:149). Simply exposing the global index would disclose other tenants’ account/app names. Require: define a tenant-filtered index protocol with suitable capabilities, or explicitly retain ordinary polling for fleet-door stores. A denied index read must not look like an empty, unchanged deployment.
- P2 — Home-map gating misses mesh-key changes (§§3.2, 5).
homeMap.Refreshrefreshes both hosts and authentication keys (internal/edge/homemap.go:320). Those keys arecontrol/edge-mesh-keyand.next, outside every proposed scope (internal/edgeproof/edgeproof.go:58). Gating the refresh oncontrol/edgestherefore delays key creation and rotation independently of whether their writers are upgraded. Require: give keys an explicit invalidation path or retain their independent polling. Preserve the refresh health heartbeat on unchanged polls too: it expires after five minutes (internal/lbhealth/lbhealth.go:36, :174).
- P2 — Telemetry has no specified backstop (§5). Unlike config and canary reads, telemetry is gated only on generations. A bypassed or failed bump can therefore leave the cached setting unchanged indefinitely; rebuilding
RecordViewdoes not refresh telemetry bodies. Today each pass resolves telemetry for every served app (internal/host/telemetry_pass.go:35). Require: a maximum successful-read age for both app and account telemetry, plus first-use reads and error retries independent of index movement.
- P2 — Every index change still causes unrelated per-app reads (§§2, 4.5, 9). The existing full pass invokes unrestricted adoption and config synchronization (internal/host/agent.go:2827). Adoption reads each distinct app/channel pointer (internal/release/adopt.go:682); config synchronization fetches app bundles (internal/appconfig/syncexec.go:174). Optimizing record walks and canary polling leaves this fan-out intact, while adding index-triggered full passes. Require: carry changed scopes into these legs or cache their reads with appropriate invalidation and backstops. Demonstrate that changing one app does not re-read every unrelated app.
- P2 — Wrapping the deployment opener breaks box publication (§§3.3, 7). The publication command uses
openDeployStoreEnv(internal/host/boxpublish.go:326), then requires the returned value to be exactly*release.S3Store(internal/host/boxpublish.go:259). A decorator fails that assertion even with readers disarmed. Require: replace the concrete-type test with an explicit backend capability/identity check and cover publication through the wrapped opener.
Verdict: reject at bbae135c. P1 findings 1–3 block acceptance: moves ordering, backstop publication ordering, and preservation of existing store operations.