# 2026.08.25.01 Commanders become usable, agents get a credential that can be bounded, and a nudge finally arrives. 96 commits — the largest release so far, and most of it is one night's work on commanders driven by watching real setup runs. **One schema change**, additive and safe. **No dependency change** — `bun.lock` is untouched. **Do not run `bun install`.** **`edge-pertento-bridge` is NOT a target for this release.** It needs a schema step no other host does. See "Edge is excluded, and why" — this is the important part of this plan. ## What is in it - **Commanders.** The template a new commander is created with was rewritten across the night: it asks one question per turn in the owner's own language, proves its action on a single item before arming, writes its own `CLASSIFICATION.md` and `DISPATCHING.md` rather than editing the platform's file, and records per step in its ledger. The Stop control works, a deleted commander stays deleted, and its children no longer appear in `/chat`. - **Every agent session gets a key, scoped to `notify`.** A chat can send you a notification through the platform instead of having no sanctioned way to reach you. It is minted per session and revoked with it. - **Credential narrowing** — `api_keys.permissions` (an allow list) and `api_keys.excluded_kinds` (resolved against the live registry). Applied **before** the owner bypass, which makes it the only thing that can bound a Super Admin — the case that matters, because a commander holds the owner's identity unattended for months. This is the schema change. - **`notify` moved from `app` to `core`.** It was grantable, nothing granted it, and every Member's app was silently 403'd registering its own push token. On the bridges that is four accounts. - **The loopback notify door is closed.** `/_officer/` paths now require a producer secret the sidecar issues at registration. It previously accepted any caller that set a header. - **Sidecar proxy timeouts are OFF.** `TIMEOUTS_ON = false` in `create-proxy.ts`, one line to reverse. Bun's `server.timeout()` stores one byte, so declaring 1800 seconds gave a _shorter_ timeout than declaring nothing. More permissive than what these hosts run today — a long transcription or a film no longer gets cut. - **A nudge always arrives.** Three refusals removed from the nudger. Read that commit before judging it: the old guards meant no nudge could ever fire on a machine running commanders, silently. - **Plugin tasks appear in the file browser**, and a direct task run is direct — no modal, no job row. - **`/todos`**, a kanban board over the repo's own `todo/` tree, owner-only. - **Two editor fixes** — one buffer per file across panels, and the model picker reading the right id. ## The schema change Two nullable columns on `api_keys`: ``` permissions text[] null excluded_kinds text[] null ``` Both null means unnarrowed, which is what every existing key is — including the mobile apps'. **No existing credential changes behaviour.** Nothing is dropped and nothing is rewritten. `bun db:push` applies it. **Do not take the exit code as the answer** — drizzle-kit can print an error and still exit 0. Verify on each host, against that host's own database: ```sql select column_name from information_schema.columns where table_name = 'api_keys' and column_name in ('permissions','excluded_kinds'); ``` Two rows, or the host is not done. `permissions` alone is a half-applied push — stop and report it. ## Every host except edge 1. `git fetch origin --tags` then `git merge --ff-only deploy/2026.08.25.01` 2. `bun db:push` — then **run the query above**. Two rows. 3. `bun run plugins:update` 4. `pm2 restart officer` `officer` only at step 4. Step 3 restarts each plugin's own sidecar, which belongs in the report. **`macbookpro`:** append `/opt/homebrew/bin` to PATH for the `bun` commands, run `pm2` separately. It is a laptop and may be asleep — unreachable is a skip with a note, not a failure. ## Edge is excluded, and why `edge-pertento-bridge` came back on 2026-08-24 at `deploy/2026.08.22.02`, **288 commits behind**. It is a deploy target again and its row is in `docs/servers.md`, but it must not be rolled out with the rest, and the reason was verified on the host rather than inferred: - **It has no `roles` table.** `to_regclass('roles')` is null. - **Its two accounts hold `Super Admin` and `Developer`.** Current schema makes `users.role` a foreign key onto `roles.name`. Every other host got `roles` created and seeded by `2026.08.23.04`, then the foreign key added by `2026.08.23.05` — two releases, in order, against an already-populated table. Edge would do both in one `db:push`, and the seeder runs from `postdb:push`, which is **after** the push completes. So the constraint would be created against an empty `roles` while `users.role` holds two values that are not in it. That fails. Worse, it can fail and still exit 0, leaving the schema half-applied — which is exactly the warning `2026.08.23.05` carried. **It also missed two plans with one-off steps** that a single fast-forward does not replay: `2026.08.23.05`'s pre-flight SQL, and `2026.08.23.06`'s orphan-affordance cleanup. So: **name edge as skipped, do not attempt it, and do not report it as a failure.** Bringing it forward is a watched, by-hand operation — push, seed, push again, verify the constraint — and it is being done separately by the owner and this session, immediately after this rollout. **One more thing about edge, for whoever reaches it later:** from `officer-dev` the name `edge-pertento-bridge` does not resolve. It answers on **`100.64.0.15`**. `docs/servers.md` has the address; use it. ## What to check, this release **A commander can be created and asks its first question in your language.** That is the release, and it is the thing most likely to reveal a stale bundle — hard refresh before deciding anything failed. **A Member's phone can register for notifications.** `notify` is core now, so an account that was 403'ing should simply work. **Nothing else should have changed for an ordinary signed-in owner.** Every narrowing column is null on every existing key. No direct SQL is needed on any host in this release, and none is authorised. The query in "The schema change" is a read. ## Hosts - **`officer-dev`** — skip it. Restarting `officer` there ends the rollout. It is already on this code. - **`edge-pertento-bridge`** — skipped, see above. Not a failure. - **`alpha`**, **`fofos`**, **`production-pertento-bridge`**, **`deadpool-rezio-bridge`** — the targets. - **`macbookpro`** — a target if it is awake.