Skip to content

Promote main → prod (b8fa4ac) - #471

Merged
thedancingdeveloper merged 12 commits into
prodfrom
main
Aug 29, 2026
Merged

Promote main → prod (b8fa4ac)#471
thedancingdeveloper merged 12 commits into
prodfrom
main

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

Promotion of #459/#460/#466 + terminal duplicate-output fix from main → prod.

Triggers build.yml → prod-. Carries:

main HEAD: b8fa4ac (promoted from dev via #470).

thedancingdeveloper and others added 12 commits August 29, 2026 20:40
The tagged release builds the merged `vogt-stack` image prod deploys, but it
passed `INSTALL_CADASTRE_MCP=true` and never `INSTALL_AI_CLIENTS`, so the
Dockerfile default (false) stood: v0.3.0 shipped with no `claude` or `codex`.
Production registered the `Claude Code (protected)` / `Codex (protected)`
session templates from config.rs yet could not start them — the CLIs were not
in the image. `build.yml` learned this for dev/prod (#23); `release.yml` had
not.

- release.yml: pass INSTALL_AI_CLIENTS=true and INSTALL_THECLAWBAY=true to both
  the candidate and pushed stack builds; extend the smoke step to run claude,
  codex and theclawbay so a missing CLI fails the release (NFR-Q7). Flutter
  stays out by design — a release is the `lean` pod variant (#184).
- deploy-production.yml: resolve the stack image by its published tag
  (`vogt-stack:${TAG#v}`), since docker/metadata-action strips the `v`; the old
  `:$TAG` lookup would fail the signed-digest resolve for a v-prefixed tag.
- test_deploy.py: guard the release build the way the dev build is guarded.
- Bump to 0.3.1 across pyproject, package, __init__ and build.yml; changelog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
The version lives in more places than the first commit touched: the ci.yml
product-version gate arg, the deploy compose/overlay/.env image tags, the
deployment docs, and the product-version test's expected value. Matches the
scope of the 0.3.0 bump commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
…s operator-private

A reader following DEPLOYMENT.md could pin a plain release image for
production and get a pod whose Claude Code / Codex session templates register
but cannot start — the binaries are only ever added by the §3.2
VOGT_INSTALL_AI_CLIENTS build flag, which no published image sets. §3.2 now
says so directly, and §7 states the boundary: the production desired-state and
overlays live in the operator's own repository (#204), the signed release
digests are CLI-free, and the maintainer's estate is a private deployment, not
a turnkey scenario reproducible from this tree.
…#466)

`connect()` reassigned `ws` without closing the previous socket, and several
paths reach it while a socket is still open or connecting: the reactive
park/resume effect, `onWake`, the cache-replay `.then()`, and — since the
recent silent-socket work — `recycleSocket()`, which calls `ws.close()` *and*
`scheduleReconnect(100)` so the socket's own delayed `close` event can spawn a
second `connect()`. Every leftover socket keeps its `message` listener attached
and writes the same PTY output into the one xterm instance, so lines render two
or three times. Seen live on dev and prod.

Make `connect()` the single owner of the socket: drop any existing socket
first, then capture the new socket in a local `socket` and guard every handler
with `if (ws !== socket) return;` so a replaced or parked socket's late
open/message/close/error events are no-ops. One terminal now has exactly one
live socket at a time. Only the racy leftover path closes a socket here; healthy
sockets are untouched (all `connect()` callers already gate on a
closed/absent socket or a prior park). PWA typecheck and the 817 web tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
#466)

Non-selected terminal sessions were force-dropped and then flooded with a
replay on switch. When a subscriber fell >1024 broadcast chunks behind, the
outbound task sent a `Lag` frame and closed the socket; the client tore down
and reattached, and the server replayed scrollback — a large/full replay that
is heavy main-thread work, which stalled the neighbouring panes' sockets and
made them lag too. The loop fed itself.

Two changes to the outbound task, server-side only (the client already handles
mid-stream snapshots and tracks its resume cursor):

- Coalesce: drain the broadcast chunks already queued (`try_recv`) into one
  WebSocket frame, bounded at 256 KiB. A chatty session now wakes the client's
  single message-draining thread far fewer times, which is what was starving
  the other panes.
- In-band lag recovery: on `Lagged`, re-snapshot from the client's exact
  last-delivered position and stream it as the normal SnapshotStart → payload
  → SnapshotDone sequence on the *same* socket — a `reset:false` delta when the
  cursor is still in the scrollback window, a `reset:true` reload when it has
  aged out. The socket never closes, so there is no reconnect/replay cascade. A
  circuit breaker (5 consecutive resyncs with no live send) falls back to the
  old Lag-and-close so a hopelessly-behind client can't drive unbounded
  resnapshotting.

Pure `coalesce` helper is unit-tested; fmt, clippy (-D warnings) and the ws
tests pass. Deliberately out of scope (follow-ups): decoupling client socket
draining from the render thread (Web Worker) and pausing hidden panes'
subscriptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
…es (#459)

Relax the ureq/url version floors in voice/tts/Cargo.toml and update the
shared voice/Cargo.lock so the sidecar no longer resolves the vulnerable
transitive crates:

- rustls-webpki 0.102.8 -> 0.103.15  (clears RUSTSEC-2026-0104 high panic-on-CRL,
  RUSTSEC-2026-0049, RUSTSEC-2026-0098, RUSTSEC-2026-0099)
- idna 0.4.0 -> 1.1.0                 (clears RUSTSEC-2024-0421)
- ureq 2.9.7 -> 2.12.1               (kept on the 2.x line; no ureq-3 API use)
- url 2.4.1 -> 2.5.8

ureq/url are not referenced from tts/src; they exist only to steer the shared
lock. cargo-audit against voice/Cargo.lock goes from 5 vulnerabilities to 0
(only the pre-existing, unrelated audiopus_sys "unmaintained" notice remains).
Voice CI (fmt, clippy -D warnings, test) passes locally with the updated lock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
promote.yml has never succeeded: the self-hosted [self-hosted] runner has no
gh CLI, so `gh api` / `gh run list` / `gh pr list` / `gh pr create` all die
with `gh: command not found` (exit 127) before any gate runs.

Replace every gh call with a small curl+jq `api()` helper hitting
$GITHUB_API_URL (curl and jq are both present on the runner and already used
by deploy-production.yml). PR creation still authenticates as
VOGT_PROMOTION_TOKEN so the opened PR receives its normal checks.

Also add `actions: read` to the job permissions: the dev deployment-receipt
gate reads actions/workflows runs and run artifacts, which the previous
permission block (contents/checks/pull-requests only) did not grant. That gap
was never reached before because the run failed earlier on the missing gh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
…ls (#460)

test_promotion_is_fast_forward_only_and_never_pushes_a_branch pinned the old
`gh pr create` string; #460 replaced the gh CLI with curl+REST. Update the
assertions to the new contract — the PR is opened via POST .../pulls with the
promotion token, `gh pr create`/`gh api`/`gh run list` are gone, and
`actions: read` is granted for the receipt lookups — while keeping the
never-pushes-a-branch and fast-forward-only invariants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4VhQKnLKBA847LSZpPPds
@thedancingdeveloper
thedancingdeveloper merged commit 26ff925 into prod Aug 29, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant