# Backlog Things deliberately deferred, with enough context to pick them up cold. Not a wish list — everything here has been considered and consciously postponed, and each entry says what would trigger doing it. Last reviewed: 2026-08-10 --- ## Consolidating MCP tools **Trigger: the model starts picking the wrong tool, or `tools/list` overhead becomes measurable.** There are 23 tools. The often-quoted ceiling of ~20 is a rule of thumb, not a threshold, and mis-selection is caused by tools that resemble each other far more than by raw count — these are mostly distinct. It has been working well in practice, so this is not currently a problem. The question that prompted this: could one `modify_drink` take an `action` of create/update/delete and replace three tools? Technically yes, but it is the wrong merge: - **Annotations stop meaning anything.** A tool that *can* delete has to be marked `destructiveHint`, so a client that confirms destructive actions would start prompting when you add a drink — or you mark it false and lose the prompt when it matters. - **Scope enforcement gets coarser.** Reads need `drinks:read` and writes need `drinks:write`. A merged tool must demand the union (a read-only token loses read access) or branch internally, at which point the declared scope no longer describes the tool and the consent screen misleads. - **The schema gets harder for the model.** `id` is required for update/delete and meaningless for create. That is a discriminated union, which converts to `anyOf` — exactly the shape models fill in wrong. - **The audit trail gets vaguer.** `McpAuditLog` records the tool name; `delete_drink` says what happened, `modify_drink` does not. The rule worth keeping: **merge when it is the same gesture with a different parameter; keep separate when the intent or the risk differs.** `upsert_bar_item` ("I bought gin" / "I finished the gin") and `promote_recipe` (drink vs wishlist) are good merges. CRUD is not. Cheaper levers if the count ever needs to come down: 1. `MCP_CHATGPT_TOOLS=0` drops the `search`/`fetch` aliases: 23 → 21, free if ChatGPT is unused. 2. Remove low-value tools rather than merging good ones. `update_rating` is the weakest — a drink can simply be re-rated. 3. Sharpen descriptions. Selection failures are usually a description problem. Worth measuring the real `tools/list` payload cost before spending effort here. --- ## Refresh-token grace window **Trigger: a client is forced to re-authorize after a dropped network response.** Reuse of any refresh generation currently revokes the whole family, with no grace. That is correct against theft but harsh on a client whose response was lost in transit — it replays legitimately and destroys its own grant. The audit's advice was to build durable lineage first and only then consider grace. Lineage now exists (`OAuthRefreshToken`, one row per generation, atomic consume), so this is a safe change to make if it ever bites. Auth0 disables leeway by default; Okta defaults to 30 seconds, configurable to 60. A 5–10 second window limited to the immediately preceding generation would be reasonable — anything older must still revoke the family. --- ## Encrypt backup dumps **Trigger: backups leave the LAN, for any reason.** `pg_dump` output goes to TrueNAS unencrypted and contains every user's data plus bcrypt password hashes. Defensible on a NAS you control on your own network; not defensible in cloud storage, a friend's NAS, or off-site replication. `gpg` or `age` before the `scp` in `/usr/local/bin/drinktracker-backup` is a couple of lines. Much easier to add before there is history to re-encrypt. Related: the off-site copy is in the same building, so it survives losing the LXC or the Proxmox host but not fire or theft. --- ## Purge the retained Docker artifacts **Trigger: 2026-09-07.** Docker is installed but disabled on the LXC; old containers and volumes were kept as a rollback path after the move to native systemd. Nothing has needed them. Remove after that date. --- ## Establish why Docker actually failed in this LXC **Trigger: only if Docker is ever wanted on this host again.** `deploy/README.md` originally blamed unprivileged-LXC nesting. That is wrong — `/proc/self/uid_map` reads `0 0 4294967295`, an identity mapping, so the container is privileged. The AppArmor and `ip_unprivileged_port_start` failures were real and reproduced, but the cause was never established. The doc now says so rather than substituting a fresh guess. --- ## Rotate the TrueNAS account password **Trigger: do it whenever convenient.** `getent passwd` run as root on TrueNAS prints the SHA-512 hash, and one ended up in a session transcript. Salted and not trivially reversible, but crackable offline against a weak password.