| 1 | | Finding | Disposition | Why | |
| 2 | |---|---|---| |
| 3 | | R2-1: stale alias authority | Partly fixed | Failed claim listings now stop renewal, but derived instance aliases can survive a failed dependency read while reconciliation returns success; see 1. | |
| 4 | | R2-2: fills from undeclaring builds | Fixed | Admission checks the answering build’s declaration, and the home signals rejection to the far edge with expiry `0`. | |
| 5 | | R2-3: billing for uncacheable fills | Fixed | Acknowledgement transfers billing independently of storage eligibility. | |
| 6 | | R2-4: response delay counted twice | Fixed | Initial expiry now uses response time plus lifetime minus corrected initial age. | |
| 7 | | R2-5: rejected responses shared among waiters | Fixed | Coalescing shares only admitted entries; rejected responses reach only their initiating requester. | |
| 8 | | R1-3: indefinitely renewed stale authority | Partly fixed | App-scope confirmation is addressed, but domain-derived eligibility still has uncovered dependencies; see 1 and 2. | |
| 9 | | R1-6: renewed lifetime at far edge | Fixed | Far copies inherit the home’s absolute deadline, and the initial-expiry calculation is corrected. | |
| 10 | |
| 11 | 1. **P1 — Instance catch-all aliases can still renew stale ownership indefinitely.** |
| 12 | |
| 13 | **Claim:** The revised alias freshness rule excludes a dependency that exists in the current code. |
| 14 | |
| 15 | **Evidence:** [Design:273](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:273) uses successful alias reconciliation to renew authority and states that catch-all aliases are never published. However, instance catch-alls produce ordinary exact-name aliases ([aliases.go:69](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/domains/aliases.go:69)); the publisher excludes only names beginning `*.` ([edgesfile.go:517](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/host/edgesfile.go:517)). |
| 16 | |
| 17 | When `InstanceLabelCounts` fails, derivation preserves existing aliases before checking whether an exact claim now supersedes them ([aliases.go:161](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/domains/aliases.go:161)). That failure is logged, and `ReconcileAliases` still returns nil ([aliases.go:594](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/domains/aliases.go:594)). Thus, an exact name reassigned to another account can remain served under its previous derived alias while successful instance confirmation and nominally successful alias passes keep renewing `at`. |
| 18 | |
| 19 | **Smallest fix:** Distinguish wildcard aliases from exact aliases derived by instance catch-alls. Require explicit dependency-completeness reporting, including the label-count read and registry writes, before advancing authority. Test an exact claim replacing a derived alias while label-count reads fail for over an hour. |
| 20 | |
| 21 | 2. **P1 — A primary hostname’s path-routing exclusion has no domain freshness bound.** |
| 22 | |
| 23 | **Claim:** Domain-read failures can leave a primary hostname caching the wrong route indefinitely, even after its authoritative configuration adds a path target. |
| 24 | |
| 25 | **Evidence:** [Design:184](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:184) excludes hostnames with path-prefix rows. Those rows come from domain claims through `ReconcileAliases` ([aliases.go:315](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/domains/aliases.go:315)), but [design:256](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:256) bounds a primary hostname only by its app-record scope; the domain-reconciliation bound applies specifically to aliases. |
| 26 | |
| 27 | Start with primary hostname H serving cached `/assets/` from A. Publish a domain path mapping directing `/assets/` to B, then fail domain listings while app-record confirmation continues succeeding. No path row appears, so H remains eligible and its authority keeps renewing. Cached A responses can continue indefinitely instead of reaching B and its gates. |
| 28 | |
| 29 | **Smallest fix:** Bound primary-host eligibility by successful domain reconciliation too, because proving the absence of path routing requires those reads. Add this failed-domain-listing case to §12, with a gated path target and both home and far caches. |
| 30 | |
| 31 | 3. **P2 — Coalesced waiters and fill duration are unbounded.** |
| 32 | |
| 33 | **Claim:** A stalled fill can retain disk reservations indefinitely and accumulate waiting requests outside the existing backhaul concurrency limit. |
| 34 | |
| 35 | **Evidence:** [Design:345](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:345) limits active fills but allows concurrent requests for one key to wait without any waiter limit or deadline. Reservations remain held until the fill exits ([design:554](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:554)). Existing backhaul deliberately has no response-body deadline ([edge.go:2837](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/edge/edge.go:2837)). |
| 36 | |
| 37 | An app can send admissible headers and then stall its body. Repeated requests for that key join the same fill indefinitely. Only the leader takes a backhaul slot; the waiters therefore escape the containment that otherwise refuses excess requests ([edge.go:2903](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/internal/edge/edge.go:2903)). A byte ceiling does not bound this case. |
| 38 | |
| 39 | **Smallest fix:** Specify a finite cache-fill deadline covering body consumption, bounded waiter counts, and reservation release when caching is abandoned. Test a stalled body with excess same-key requests and verify bounded waiters and timely reservation release. |
| 40 | |
| 41 | 4. **P2 — Cacheable HTML fallbacks still satisfy every admission rule.** |
| 42 | |
| 43 | **Claim:** A declared asset prefix can cache an SPA fallback for a year, contrary to the explicit anti-goal. |
| 44 | |
| 45 | **Evidence:** [Design:111](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:111) forbids caching `200 index.html` as a script. The admission rules at [design:417](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:417) contain no HTML exclusion. A declaring build returning `200 text/html`, `Cache-Control: public, max-age=31536000`, and no cookies or disallowed `Vary` passes them all. This can happen when asset-path cache headers also apply to the SPA fallback. The proposed test uses `no-cache` ([design:644](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:644)), so it cannot detect this omission. |
| 46 | |
| 47 | **Smallest fix:** Explicitly refuse HTML responses at admission and add a fallback test with a positive, long cache lifetime. |
| 48 | |
| 49 | 5. **P3 — The prescribed first rollout test cannot exercise the cache.** |
| 50 | |
| 51 | **Claim:** The rollout sequence asks for a successful far-edge fill before arming its home. |
| 52 | |
| 53 | **Evidence:** [Design:207](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:207) correctly requires an armed home to publish asset declarations. But [design:613](/home/paulca/amadan.net/carlos/platform-edge-asset-cache/docs/superpowers/specs/2026-10-24-edge-asset-cache-design.md:613) says to arm only a far dev box, prove the acceptance gate, then arm the rest. With the home unarmed, that first box cannot catch the test app’s assets. |
| 54 | |
| 55 | **Smallest fix:** Arm the test app’s home first, then one far dev edge, and run the two-edge acceptance gate before expanding. |
| 56 | |
| 57 | reject |