# Plan 14 census overlay fix-empty-app-key (branch fix-empty-app-key, 2026-10-16; loaded last, after home-part2). RULED by the Plan 14 controller 2026-10-16 as census-ruling-fix-empty-app-key. Envelope-only, at main's dispositions: no root-read unit is added, removed or changed, so no row and no frozen declaration moves, and the legacy seal stands. # The fix: the apps///{logs,errors,analytics}/ key builders (internal/applog FamilyPrefix, FamilyBoxPrefix, FamilyHourPrefix, FamilyChunkKey and their logs, errors and analytics wrappers, and analytics DayKey) return an error, applog.ErrBadKeySegment, for an empty or invalid account or app, or an empty or slash-bearing box, hour or date. They never build a key with an empty segment. An app-less route on the dev box leo-climbing (proof.dev.carlosframework.com: kind instance, account bab, app "", an Addr upstream nobody listens on) had its refused 502s filed under (bab, "") and shipped to apps/bab//errors/, which hostops.Apps refuses to walk. # Envelopes: internal/applog gains the checks (CheckApp, CheckSegment, ErrBadKeySegment) and ReadWindow returns the builder's error. internal/apperr and internal/analytics change their builder signatures, and their readers (apperr.ReadWindow, analytics.ReadRange) and analytics.UpsertDay return the error before any store call. internal/host's logshipFlush, errshipFlush, chunkPrune and usageFlush skip a refused app and log once (unshippable, unshippableSeen); usageFlush drops a refused analytics delta instead of re-queueing it. None of these reads anything new, from the box or the bucket; the only reads that change are ones a refused key no longer makes. @coverage internal/analytics e7461d39edd1660289669fdb5185f5231ef52e380978ce1d5765e91878f2c907 @coverage internal/apperr 8d2d53a1065bcdbdacdc178723b132746d00e580fe056bdc8ff2a5634f2b03d5 @coverage internal/applog fa9fd3a53e62d989d7fd834c86da8e784a396a991a33fa934f79b2db506dc669 @coverage internal/host 498a7d4be8bd9c2b566daa4536698b9eee14be7aa6ae14ccd14dba33a88018e1