THIS ONE CHANGES THE DATABASE, and the plan says so with the command that proves
what the change is rather than the claim alone:
command_hq_sessions
+ plugin text null
+ plugin_config jsonb null
both nullable, no default, no constraint — so every existing row is valid the
moment they exist and there is nothing to backfill. no rename, nothing dropped, no
create-then-constrain, and therefore no --force needed or permitted.
THE PLAN DEMANDS A QUERY, NOT AN EXIT CODE. drizzle-kit push can fail and still
exit 0, so step 3 is a verification that must print exactly `plugin plugin_config`
and whose OUTPUT goes in the report per host — not "schema ✓". I ran that exact
command here before asking seven hosts to run it.
NO bun install step: package.json and bun.lock are untouched since .01. that also
sidesteps the step-order bug the last rollout found, where the standard
plugins:update runs before a plan's install and imports a package that is not there
yet.
the backup step reads the env variable name rather than assuming it. several hosts
use OFFICERDEV_POSTGRES_URL and not POSTGRES_URL, which is the kind of detail that
turns a backup step into a file containing an error message.
av-club's commander template reaches each host through the standard plugins:update
step rather than through this plan — worth saying, because av-club moving is
expected here rather than a surprise.
gate before tagging: typecheck clean, docs:check green, locale parity green. the
ModelSelector test still fails and is still not mine — same chat code, untouched by
this range.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
120 lines
4.9 KiB
Markdown
120 lines
4.9 KiB
Markdown
# 2026.08.29.02 — a plugin can ship a commander
|
|
|
|
**This one changes the database.** Two columns, both nullable additions, on a table that already exists.
|
|
There is no rename in it, nothing is dropped, and no existing row changes — but `db:push` is still not
|
|
to be trusted on its exit code, so there is a per-host query below and the report must carry its answer
|
|
for each host.
|
|
|
|
No dependency change: `package.json` and `bun.lock` are untouched since `2026.08.29.01`. **There is no
|
|
`bun install` step in this plan** — which also sidesteps the step-order problem the last rollout found,
|
|
where the standard `plugins:update` ran before the plan's install.
|
|
|
|
## What is in it
|
|
|
|
- **Plugins can ship commanders.** A plugin with `commanders/<name>/COMMANDER.md` offers one; it appears
|
|
in Command HQ under a new **From plugins** section, arrives **not running**, and must be configured
|
|
before it can arm.
|
|
- **av-club ships the first one** — *Convert on arrival*: watches a folder, converts what lands in it.
|
|
It reaches each host through the standard `bun run plugins:update` step, not through this plan.
|
|
- Marketplace, file browser and Convert Image work from earlier today rides along.
|
|
|
|
## Schema — CHANGED. `db:push` is required.
|
|
|
|
```
|
|
command_hq_sessions
|
|
+ plugin text null which plugin's template made this commander
|
|
+ plugin_config jsonb null the owner's answers to that template's inputs
|
|
```
|
|
|
|
Verified as the whole of it:
|
|
|
|
```bash
|
|
git diff --name-only deploy/2026.08.29.01..deploy/2026.08.29.02 | grep -iE 'schema|/db/|databases'
|
|
# → src/databases/officer_db/src/command-hq/queries.ts
|
|
# src/databases/officer_db/src/command-hq/schema.ts
|
|
```
|
|
|
|
`queries.ts` is code, not schema. The only structural change is the two columns above.
|
|
|
|
**Both are nullable with no default and no constraint**, so every existing row is valid the moment they
|
|
exist and there is nothing to backfill. This is the shape `db:push` handles cleanly — there is no
|
|
create-then-constrain here, and no `--force` is needed or permitted.
|
|
|
|
## Every host — the commands
|
|
|
|
After the standard steps in `prompts/deploy-commander.md` (dirty check, `git fetch origin --tags`,
|
|
`git merge --ff-only deploy/2026.08.29.02`, `bun run plugins:update`), in the checkout from PM2's
|
|
`pm_cwd`:
|
|
|
|
### 1. Back the database up first
|
|
|
|
```bash
|
|
pg_dump "$(grep -m1 -E '^(OFFICERDEV_)?POSTGRES_URL=' .env | cut -d= -f2-)" > ~/db-backups/officer-$(date +%Y%m%d-%H%M).sql
|
|
```
|
|
|
|
Read the variable name rather than assuming it: **several hosts use `OFFICERDEV_POSTGRES_URL`**, not
|
|
`POSTGRES_URL`. `mkdir -p ~/db-backups` first if it is not there.
|
|
|
|
### 2. Push the schema
|
|
|
|
```bash
|
|
bun db:push < /dev/null
|
|
```
|
|
|
|
**stdin closed**, so it can never sit waiting on a prompt in an unattended run — it fails instead, which
|
|
is the outcome you want. Expect `[✓] Changes applied` and then the two post-push scripts.
|
|
|
|
### 3. VERIFY BY QUERY. The exit code is not the answer.
|
|
|
|
```bash
|
|
bun -e "
|
|
const { db } = await import('officerdb/db'); const { sql } = await import('drizzle-orm');
|
|
const r = await db.execute(sql\`select column_name from information_schema.columns
|
|
where table_name='command_hq_sessions' and column_name in ('plugin','plugin_config') order by column_name\`);
|
|
console.log([...((r as any).rows ?? r)].map((x:any)=>x.column_name).join(' '));
|
|
process.exit(0)"
|
|
```
|
|
|
|
Must print exactly:
|
|
|
|
```
|
|
plugin plugin_config
|
|
```
|
|
|
|
**Put that line in the report, per host.** Not "schema ✓" — the output. A host printing one column, or
|
|
none, is a **failed host**: report it, move on, and do not attempt a repair you invented.
|
|
|
|
### 4. Restart
|
|
|
|
```bash
|
|
pm2 restart officer
|
|
```
|
|
|
|
`officer` only. `plugins:update` has already restarted the plugin sidecars itself.
|
|
|
|
### 5. Check it came back
|
|
|
|
`pm2 jlist`: `officer` online and `restart_time` moved, then `/api/health` on the host's own port
|
|
(9010 on `macbookpro` and `production-pertento-bridge`, 9000 elsewhere). `200` or `401` are both healthy.
|
|
|
|
## Per host
|
|
|
|
| Host | Note |
|
|
| --- | --- |
|
|
| `officer-dev` | **SKIP ENTIRELY.** The rollout runs here. Its schema is already pushed; nothing else is needed. Name it as skipped. |
|
|
| `alpha` | Nothing special. |
|
|
| `fofos` | The one that matters this time — `av-club` is installed there and the commander template is the point of the release. |
|
|
| `macbookpro` | macOS, port **9010**, a laptop. Unreachable is a SKIP with a note. |
|
|
| `macbruno` | macOS, login `brunoalcadapadezmonteirodaconceicao`. |
|
|
| `production-pertento-bridge` | Port **9010**. |
|
|
| `edge-pertento-bridge` | Nothing special. |
|
|
| `deadpool-rezio-bridge` | Login **`br`**, checkout `/home/br/officerdev/platform`. |
|
|
|
|
## What "done" looks like
|
|
|
|
Per host: HEAD before and after, whether the backup was written, `db:push` result, **the exact output of
|
|
the verification query**, and `officer` back online with a moved `restart_time`.
|
|
|
|
The line that matters in the summary: **which hosts print `plugin plugin_config`**. That is the only
|
|
thing this release actually changes about a machine.
|