Skip to content

fountain v0.7.0 -> v0.16.0: the pin catches up, and gets something watching it - #120

Open
lex00 wants to merge 1 commit into
mainfrom
issue-119-image-pin
Open

fountain v0.7.0 -> v0.16.0: the pin catches up, and gets something watching it#120
lex00 wants to merge 1 commit into
mainfrom
issue-119-image-pin

Conversation

@lex00

@lex00 lex00 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

chant.config.ts and src/params.ts both spelled out ghcr.io/binarybourbon/fountain:v0.7.0, nine releases behind, so just up stood up an instance without the team roster, teammate schedules, webhook endpoints or the sandboxes routes.

  • Move the pin to v0.16.0, confirmed as the current release and confirmed present on ghcr before pinning.
  • Put the version in one place, src/lib/fountain-image.ts, which both files now import. Two literals with nothing watching them is how this drifted; workflows/shared.ts already does the same for K3D_VERSION.
  • Add an image-pin workflow, declared in TypeScript like the others and rendered by just ci, that runs weekly, asks for the latest release, checks the tag actually exists on ghcr, and opens a single-line pull request when the pin is behind. It does not merge, does not rewrite prose, and does not reopen a bump whose branch already exists.
  • Update the docs that named the old version, and the database table count in status.md, which this run measured at 41 rather than the 25 the page claimed.

Read this before merging: the bump changes what the conversation gate can assert. Fountain has spoken ACP to its runtimes since v0.9.0, and spritzer 0.5.0 answers initialize with -32601, so a turn on the emulated plane can no longer reach a completed ending. verify-conversation now asserts everything the deployment owns, which is the sandbox, the dispatch and the streamed output, and recognises that one refusal by name. Any other ending still fails, including the two regressions the gate was written for, and a real data plane is still held to exit 0. status.md moves "Completing a turn" from Verified to Does not work and says why, and the earlier "34 of 34" evidence stays attributed to v0.6.1. The alternative was to file a spritzer issue and leave the gate red; that choice is yours to reverse.

Closes #119

Validation:

  • just check passes: typecheck, chant lint, 35 tests, build.
  • just ci then just ci-check pass, so all four workflows match their declarations. actionlint is clean.
  • just e2e passes from nothing on the new pin: readiness reports the database ok, the master key is byte-identical across a redeploy, the restore matched live across 41 tables, registration and first-account admin work, and the conversation plumbing is verified as described above.
  • The new workflow has never run on GitHub. Its shell was dry-run in a container with the push and PR creation stubbed, and actionlint checks the rest, but the schedule trigger and the token's push permission are unverified until it fires.
  • tier=ha and target=kubernetes were not re-run at the new pin.

…tching it

The declared default sat nine releases behind. `just up` stood up an instance
with no team roster, no teammate schedules, no webhook endpoints and no
sandboxes routes, against docs on this same site that describe all four, and a
client generated from the current OpenAPI spec failed against it in ways that
read like client bugs. Closes #119.

Two literals held the pin: chant.config.ts's declared default and src/params.ts's
fallback behind it, and only the first was visible from the docs. Both now read
FOUNTAIN_VERSION from src/lib/fountain-image.ts, so they cannot say different
things and a bump is one line. Same rule as workflows/shared.ts's K3D_VERSION —
a pin stays honest only while there is exactly one place it lives.

The recurrence gets a reader rather than a line in a checklist, because every
other pin here already has one and this was the one with none.
image-pin/pipeline.ts renders .github/workflows/image-pin.yml: weekly and on
demand, read the pin, ask GitHub for fountain's latest release, confirm the tag
is actually on ghcr (a release is not an image — the publish lands minutes
later), and open one PR moving that line. It does not merge, does not touch the
prose the pin is quoted in — the body lists those files instead — and will not
raise the same bump twice, so a bump somebody declined stays declined. `just ci`
and `just ci-check` cover it like the other three.

What nine releases changed for us, found by standing it up rather than by
reading the notes:

- The database is 41 tables, not the 25 status.md claimed. This run measured it.
- The conversation turn no longer completes against the emulator, and that one
  is not ours to fix. fountain speaks the Agent Client Protocol to its runtimes
  from v0.9.0 (fountain#671), and #674 deleted the spawn path it replaced — an
  agent comes back `acp: true` whatever you post to /api/agents, so there is no
  way back to the old path. A turn now runs `claude-agent-acp` and sends
  `initialize`; spritzer 0.5.0 echoes command lines, speaks no JSON-RPC, and
  answers -32601. The turn stage ends `failed`, deterministically, every run.
  spritzer's newest release is the 0.5.0 this already pins, so nothing here
  closes it.

So the gate stops claiming a completed turn. It asserts what this deployment
owns — the sandbox is provisioned, the turn is dispatched into it, the runtime's
output streams back — and then recognises exactly that one refusal on the
emulated plane, by name. Any other ending still fails, the two old regressions
included, and a real data plane is still held to a turn that exits 0.

The "34 of 34" evidence stays where it was measured, at v0.6.1 over a path that
no longer exists. status.md moves the row to Does not work rather than
re-attributing it, data-plane.md replaces the mechanism, ci.md says what the
gate now does. That row has been described wrongly four times now; the fourth is
written down with the other three.

Full `just e2e` from nothing on the new pin, green: readiness through to the
database, MASTER_SECRETS_KEY byte-identical across a re-run, restartCount 0, the
backup taken and restored into a throwaway and table-matched against live,
register and self-verify over the API, the first account admin and
audit-recorded, the conversation plumbing to the handshake, every seam accepted
by a real API server.
@lex00 lex00 added the enhancement New feature or request label Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The default image pin is v0.7.0, nine releases behind, so just up stands up an instance without team, schedules or webhooks

1 participant