Files
cc-ci-orchestrator/memory/weekly-upgrader-opencode-go-glm.md
T

112 lines
8.6 KiB
Markdown

---
name: weekly-upgrader-opencode-go-glm
description: weekly upgrade run now defaults to opencode-go/glm-5.3-flash (PR #20, 2026-09-14); zen tier dead on this host; how backend/model + monitoring are wired
metadata:
node_type: memory
type: project
originSessionId: 85355980-5e4f-4f90-b1ca-d0e4fe82f04b
---
The weekly recipe-upgrade run (`cc-ci-plan/launch-upgrader.py`, fired by the
`cc-ci-upgrade-all` systemd timer Thu 22:00 Boston) now **defaults to backend=opencode,
model=opencode-go/glm-5.3-flash** when no env is set (operator choice 2026-09-08 via
`/srv/cc-ci/upgrader.env`; PR #20 hardcoded the same defaults into the launchers 2026-09-14 —
for all three launchers: upgrader, report, supervisor). Model default tracks the
backend — an explicit `LOOP_BACKEND=claude` (no model) falls back to `sonnet`.
**2026-09-14 — ZEN IS DEAD ON THIS HOST.** The ZEN provider models (`opencode/glm-5.2`) are
gone from the provider catalogue AND the host holds no ZEN key, so any launch that bypassed
upgrader.env died instantly with `ProviderModelNotFoundError: opencode/glm-5.2` — this is what
killed the 2026-09-11 report agent at launch and every manual retry (all valid ZEN credentials
mean nothing without the ZEN models existing server-side). If a launcher log shows
`ProviderModelNotFoundError`, the model id does not resolve on this host: run
`opencode models | grep glm` for what does, then fix the tier/model default. See PR
recipe-maintainers/cc-ci-orchestrator#20.
**Provider:** "OpenCode **Go**" — a flat $10/mo subscription, DISTINCT from "opencode Zen"
(pay-as-you-go). Endpoint `https://opencode.ai/zen/go/v1/` (OpenAI-compatible), model id
**`opencode-go/glm-5.2`** (Zen tops out at glm-5.1 — `opencode.ai/zen/v1` does NOT have 5.2;
updating the opencode CLI does NOT change this, the catalog is server-side). API key lives in
`~/.local/share/opencode/auth.json` (mode 600, entry `opencode-go` type api) — **never in the
repo**. opencode CLI installed: 1.15.13.
**Override per run without a rebuild:** drop `/srv/cc-ci/upgrader.env` (read via
`EnvironmentFile=-` on the unit) with e.g. `LOOP_BACKEND=claude` / `LOOP_MODEL=sonnet`. Absent
file → the glm-5.2 default.
**Monitoring (opencode backend):** the launcher does `opencode run -m <model> --share --attach
http://127.0.0.1:4096`. `opencode-web.service` (=`opencode serve` :4096, nginx →
http://oc.commoninternet.net, tailnet-only) must be active. `OPENCODE_SHARE=1` (default) also
mints a public `https://opncd.ai/share/<id>` link (printed in the TUI / readable from the server
API `GET /session/<id>``.share.url`). Note: launch-upgrader.py's `session_busy()` regex is
claude-tuned, so it misreports an active opencode run as "idle/finishing" — check the process
(`opencode run`) or web UI instead.
Gotcha fixed: opencode `-m/--model`/`--attach`/`--share`/`--title` are flags on the `run`
SUBCOMMAND — they must come AFTER `run` (a global `opencode --model X run` is silently ignored).
`launch-report.py` also defaults to opencode-go/glm-5.2 now (overridable via REPORT_BACKEND/REPORT_MODEL).
In the weekly flow the upgrader writes the summary inline AND auto-launches the report (upgrade-all §6).
**Usage limit + auto-resume watchdog (added 2026-06-23):** opencode-go enforces a ROLLING usage window
(~5h) capped by dollar value. A full glm-5.2 upgrade run is heavy — ~8 recipes (≈2h20m wall) exhausted
it mid-run, returning HTTP 429 + `retry-after` (seconds) on the go endpoint. When limited, the
`opencode run` agent loop ENDS and does NOT self-resume (process lingers but idle). Fix: `launch-upgrader.py`
now has `resume` (continue the SAME session, context preserved, via `opencode run -s <id> --continue`
finds the session from the web server, kills the idle proc via /proc scan NOT `pkill -f` which self-matches)
and `watchdog` (polls the session log; on >15min stall, waits out the 429 retry-after then auto-resumes).
An opencode `start` auto-spawns the watchdog; logs to `cc-ci-upgrader-watchdog.log`. To resume by hand:
`python3 /srv/cc-ci/cc-ci-plan/launch-upgrader.py resume`. (Subcommand `watchdog`, matching agents.py —
renamed from `babysit` 2026-06-23.)
**Watchdog covers ALL parts of the weekly run (2026-06-23):** (1) it runs in a tmux session
(`<session>-watchdog`) under the persistent tmux server, NOT a Popen child — so it survives the
`cc-ci-upgrade-all` systemd Type=oneshot whose cgroup is reaped on exit (a Popen child would be killed).
(2) The watchdog is generic (env: `UPGRADER_SESSION/_MODEL/_DONE_MARKER/_RESUME_FILE`), so `launch-report.py`
reuses it for the REPORT job (`cc-ci-report` session, marker `RECIPE REPORT COMPLETE`) — the report runs on
glm-5.2 sharing the same opencode-go budget the upgrade run drains, so it can 429-stall too. `_run_pids()`
is scoped to the managed session (title or `-s <sid>`) so the two watchdogs never kill each other's process.
Weekly timer: Thu 22:00 America/New_York; no `LOOP_BACKEND`/`upgrader.env` override ⇒ glm-5.2 defaults.
See [[orchestrator-backend-switch-gotcha]].
**2026-07-04 hardening (after the 2026-07-03 run stalled on a HOST DISK-FULL and never resumed —
the log-idle/429 watchdog does NOT cover an environmental wedge).** Three changes (commits 1bd156e,
399e999), host rebuilt:
- **Step-0 stale-image pre-reclaim** (`launch-upgrader.py` `prereclaim_cc_ci()`): before each weekly
run, `ssh cc-ci docker image prune -af --filter until=168h` — only images unused AND >1 week old, so
this week's likely-reused images stay. Best-effort; env `UPGRADER_PRERECLAIM*`. A full run's images
pile up week-over-week and had run root to 100%. (The 2026-07-03 pile was 93 GB / 194 images; pruning
took it 88%→17%.)
- **watchdog proc-death detection**: an `opencode run` proc EXITS when the model ends its turn, so a
long /upgrade-all dies+restarts many times before completing (and the log mtime freezes on death,
making log-idle useless). `watchdog()` now resumes on "no live run proc for the session + not
`_completed()`", not just log-idle. Capped at `UPGRADER_MAX_RESUMES` (20) → hands off if stuck.
`resume()` now auto-spawns a watchdog if none is alive (skips when the watchdog itself called it).
- **Hourly glm-5.2 SUPERVISOR** (`launch-supervisor.py`, systemd `cc-ci-upgrade-supervisor` .service +
.timer, hourly at :07): cheap deterministic gate — no-ops (0 tokens) if the run is complete /
progressing / a per-run watchdog is alive; only when a run stalled+died AND the watchdog gave up does
it launch a short-lived glm-5.2 agent to diagnose (disk/wedge) + drive to completion. `force`/`status`/
`stop` subcommands. Progress judged by `lu._run_pids(sid)` + log mtime (session_busy() is claude-tuned,
misreads a headless opencode run as idle — do NOT use it for opencode). Window guard: won't
auto-resurrect a session >96h old. Overrides via `/srv/cc-ci/upgrader.env` (e.g. `SUPERVISOR_MODEL=`).
Ownership: **watchdog = prompt recovery (single writer while alive); supervisor = backstop when the
watchdog is absent/gave up.** They're mutually exclusive (gate defers while watchdog tmux alive).
**2026-07-04 finish + more watchdog fixes (commits 69dd178, ddedea8, b6a95c4).** Finishing the stalled
2026-07-03 run exposed that a glm-5.2 `opencode run` is FLAKY for long autonomous jobs — its turn ENDS
(proc exits) repeatedly, `--continue` of a bloated session often doesn't advance, and it can wedge on a
hung subagent. Bugs fixed in `launch-upgrader.py`: (1) `_session_idle_min()` now measures staleness
across the whole session TREE (parent + descendant subagents) via the opencode server time.updated —
NOT the tmux log mtime (freezes on `--continue`) and NOT the parent alone (a 20-40min recipe subagent
made the parent look idle → false-resume killed the productive run). (2) watchdog loop is wrapped in
try/except so a transient blip can't kill it. (3) **`_completed()` false-positive**: it matched
DONE_MARKER inside TOOL parts (a subagent `task` prompt / bash echoing "print UPGRADE RUN COMPLETE") →
watchdog exited early, run wedged. Now requires the marker in the LAST assistant TEXT (prose) message.
(4) supervisor gate also treats the run as done once the weekly summary file exists (a run can finish
work+summary+report yet never print a clean marker). **Operational fallback that WORKED:** when the
report agent wedged twice, published the report by hand from the deterministic path —
`recipe-report.py survey <date>` → build the spec JSON from survey.json → `render``publish`. This
is reliable and bypasses the flaky agent entirely; keep it as the go-to when an agent-driven report
stalls. The 2026-07-03 report is live at report.ci.commoninternet.net/week-2026-07-03.html (16 GREEN,
plausible + lasuite-drive RED, discourse 5-PR reconcile).