# IMPROVEMENTS: THE BETTERMENT BACKLOG Licensed by docs/BUILD_CONTINUATION_PROTOCOL.md Section 0 item 4 and governed by its Section 4. THE RULES THAT MAKE THIS FILE SAFE RATHER THAN A WISHLIST: 1. Ideas go HERE with a one line rationale, never straight into code. 2. Joseph triages at each Gate E. 3. Each phase may carry AT MOST ONE approved improvement item alongside its scoped work. That is the entire licensed channel for unscoped betterment; anything more is drift by definition. 4. Nothing in this file is a commitment. An item sitting here unbuilt is the system working, not a backlog rotting. --- ## APPROVED AND ALLOCATED ### 3. The office format reader, xlsx and a minimal docx text path **Allocated to: PHASE 3. Pre approved by Joseph, 29 July 2026, as Phase 3's single improvement item under Section 4 item 4. Phase 3's slot is now spent.** Closing finding two of the four that held Phase 2 open. That finding is closed by DECISION for Phase 2, where office formats are stored, hashed and versioned with a graceful `unsupported_format` status and nothing is invented from them, and the reader itself moves here rather than being dropped. **Scope, exactly.** An xlsx reader chosen by a MEASURED Go library bake off, with the real Package A spreadsheet as the fixture, `05_PWS_Att4_Course_Schedule.xlsx`, sha256 `0135bc9b4361a0a7367fe2f01fbc3064d85e29da8dcf230cfaa7957994163574`. Plus a minimal docx text path. Nothing beyond those two formats. **The bake off is measured, not argued**, the same way the PDF reader was chosen: each candidate library runs against the real fixture and the winner is picked on what it actually recovers, not on its README. A course schedule is a TABLE, so the existing table reader and its readability bar are the consumers of whatever text comes out, and the acceptance question is whether the schedule's rows arrive as citable rows. **It inherits the rules the PDF path learned the hard way.** A format it cannot read honestly is refused BY NAME rather than run through a decoder that emits plausible output. Any new resource limit or worker constraint is subject to the D18 standing rule, so `docs/evidence/phase2/functional/known-good-fixture.txt` gets rerun and matched before the change counts. And the reader runs behind the same provenance binding: a chunk from a customer's spreadsheet is born `tenant_upload` and cannot declare otherwise. ### 2. Tier two, the rolling corpus harvest **Allocated to: PHASE 2. Pre approved by Joseph, 28 July 2026.** **Phase 2's slot was free when this was approved. It is now spent.** **What it is.** `sam-attachment-sync` today is TIER ONE: every tracked opportunity pulls its full package on first tracking and on any amendment. Tier two harvests a rolling corpus by a configured NAICS list, whether or not anyone has tracked the notice, so the corpus and the ontology grow from the market rather than only from what one customer happened to pursue. **What it needs that tier one does not.** - a configured NAICS list, not a hardcoded one - a STORAGE BUDGET with a ceiling, alerting through the Track H notifier before the ceiling rather than after. Bubbles is at 82 percent of a 221G root disk and the backups live on that same disk, so an unbounded harvest threatens the backups before it threatens anything else - a retention rule, because a rolling corpus that only grows is a disk incident with a calendar **Why it is a real improvement and not scope creep.** The learning boundary below says ingestion may grow the corpus, the embeddings, and the ontology suggestions. Tier two is the only mechanism that does that at market scale. Without it the ontology only ever learns the vocabulary of opportunities someone already found. **Not started.** Recorded, approved, and allocated. Tier one shipped first and alone, deliberately, because a harvest that runs before its budget alerting exists is the thing that fills the disk. --- ### 1. Phone capable application shell **Allocated to: PHASE 5. Pre approved by Joseph, 28 July 2026.** **Rationale.** The authenticated application does not fit below about 732 pixels of content width, because the shell is a fixed left sidebar plus a content column, which is a desktop administration layout. Measured at Gate C on 28 July: all nine authenticated routes overflow at 320 and 390; 768, 1024, and 1440 are clean. **Why Phase 5 and not sooner.** Phase 5 rebuilds the desk screens anyway, so the shell work lands where the screens it affects are already open. Doing it earlier would mean touching every screen twice, and doing it mid gate would be exactly the unscoped drift the protocol exists to prevent. **Why this is not urgent.** The supported viewport floor for authenticated surfaces is 768 during the design partner program, a recorded product decision. Below the floor is UNSUPPORTED, not broken. Public surfaces are unaffected and keep the full matrix, because candidates arrive on phones. **What done looks like.** The authenticated sweep passes at 320 and 390 with no horizontal overflow, the sidebar collapses to a drawer under the floor, and the Gate C matrix for authenticated surfaces is restored to all five viewports in the same change that makes it true. **Guard in force until then.** No new screen may raise the shell's minimum width. The visual sweep measures minimum content width per route and fails a regression, so "we support 768" cannot decay into "we support whatever the widest screen happens to need". --- ## PROPOSED, NOT TRIAGED Items land here from retros, from Gate D findings that are quality rather than correctness, and from anything noticed in passing that would be drift to act on immediately. ### P2. Optical recognition, and character map aware PDF decoding Recorded 29 July 2026 as a FUTURE CANDIDATE by owner decision, closing finding one of the four that held Phase 2 open. Not allocated to a phase. **Finding one is closed by decision, not by this item.** A scanned or undecodable text layer is a GRACEFUL TERMINAL STATE: the document is stored, hashed and versioned, the status is `no_text_layer`, the screen explains both causes, and zero chunks are stored so no reviewer can cite noise. That is the settled behaviour and it is correct. **What this candidate would add** is text where there is currently none, in two distinct cases that are worth keeping distinct because they need different work: 1. **A genuinely scanned page** has no text layer at all and needs OCR. 2. **An undecodable text layer** HAS text and cannot be mapped to characters, because the embedded font ships no usable `ToUnicode` CMap. This is the SCA wage determination's case and it needs CMap aware decoding, not OCR. It is probably the cheaper of the two and it is the one with a real document behind it: the wage determination is the legal floor under every bill rate in a proposal. **Why it is a candidate rather than a plan.** OCR pulls in a native dependency, which runs straight into the cloud portability mandate, and any model based recognition would have to answer to the provenance rules before a single recovered word could be cited. Neither is a small decision and neither is Phase 2's. **The honest cost of leaving it undone**, stated so it is not rediscovered as a surprise: the wage determination's rate grid is not available to the product today, and the product says so rather than guessing. ### P3. Self host the two web fonts instead of loading them from Google Found by the Gate C rerun, 29 July 2026, and found sideways: the ONLY finding the public sweep produced across two runs was a transport failure fetching a woff2 from fonts.gstatic.com, which did not reproduce. Chasing the flake surfaced the dependency. `frontend/index.html` preconnects to fonts.googleapis.com and fonts.gstatic.com and loads Inter and JetBrains Mono from them on every page load, authenticated and public alike. **Why it matters for THIS product rather than in general.** Every visitor IP and user agent reaches Google, and candidates arrive on the public surface, so it is their data as much as a customer employee's. A federal network may block the host outright, and it sits on the critical render path. It is also an external runtime dependency in a codebase whose stated mandate is no single vendor lock in and whose crypto story is built on staying inside a validated boundary. **Cost is small and the licence is clear.** Inter and JetBrains Mono are both OFL, so self hosting is permitted. Two woff2 files, a local -face block, and the two preconnect hints come out. It also stabilises Gate C, which is not the reason to do it but is a real benefit. **Not fixed when found**, because changing font delivery alters the render of every screen at once and doing that inside a gate run would invalidate the run that found it. ### P1. The session endpoint answers 403 where 401 is the correct code Noticed during the Gate C public sweep, 28 July 2026. `GET /api/auth/session` returns 403 with "no session" to a visitor who is not logged in. `internal/modules/auth/handler.go` uses that status at four call sites. 403 means authenticated but not permitted. 401 means not authenticated. This is the second case, so the wire is saying something slightly untrue about why the request failed, and any client written against the status code has to know the difference by convention rather than by reading it. Not fixed when found, because changing an auth status code in the middle of a gate run touches the auth contract and the frontend's handling of it, which is exactly the unscoped drift the protocol exists to prevent. The visual sweep allows this one response by name so the boundary is visible rather than papered over, and the allowance is keyed to role, status, and exact path together so it cannot widen quietly. Cost is small. Risk is that a client somewhere branches on 403 today. --- ## DECLINED, kept so the reasoning is not relitigated Nothing yet. When an item is declined it stays here with the reason, because the cost of rediscovering a rejected idea is paid every time it is proposed again.