feat(vloer): warn when Runs hold budget Ploeg cannot release #211

Merged
ryangr0 merged 2 commits from ryangr0/replace/201-unsettled-accounts into development 2026-10-04 08:39:04 +00:00 AGit
Owner

Replaces #201 (feat: list and warn about Runs whose spend Ploeg cannot settle) after the Ploeg separation in #206. Its Ploeg part is ploeg-hq/ploeg#50; this PR moves Unfold's pin to that branch and carries the rest.

Commits

  • d0058978 build(ploeg): pin ploeg-hq/ploeg#50 to list Runs whose spend cannot settle
  • eff0d28f feat(vloer): warn when Runs hold budget Ploeg cannot release (from 8de8151274)
  • 61737d66 chore(site): re-record the demo replay for the unsettled summary fields (from 857397edb0)

Merge order

  1. #206, the cutover. Until it merges, this PR's diff also shows the cutover commits it is stacked on.
  2. ploeg-hq/ploeg#50.
  3. Move the pin in this PR's build(ploeg) commit to the merge commit on Ploeg's main, then merge. Until then the ploeg-pin check is red by design: it requires the pinned commit on Ploeg's main.

Verification (local, at 61737d66 with Ploeg e683eba0)

  • mise run verify with the result cache, as a pull request runs it: all gates passed. That includes the Ploeg group (its own scripts/verify.sh at the pin), Vloer, the extension, the demo replay check, integration (managed qualification) and docs.
  • Ploeg side: GitHub CI passed on ploeg-hq/ploeg#50.

What happened to each commit of #201

Commit Subject Here
5cb8ee814f feat(ploeg): list finished Runs whose spend Ploeg cannot settle Ploeg part ported to ploeg-hq/ploeg#50
8de8151274 feat(vloer): warn when Runs hold budget Ploeg cannot release Ploeg part ported to ploeg-hq/ploeg#50; Unfold part re-applied as eff0d28f
857397edb0 chore(site): re-record the demo replay for the unsettled summary fields re-recorded on the new base with mise run demo-record as 61737d66

The original had no reviews or comments. Its checks were red because development itself failed at mise install --locked; #206 fixes that lock. No earlier check result carries over.

Original description of #201

Summary

Ploeg, VIK-1634 (https://vikunja.webgrip.dev/tasks/1634): new read-only GET /api/v1/operator/unsettled-accounts. It lists finished Runs whose account is still minting, issued or unknown. These are the Runs the block sweep retries and logs as managed key block retry unresolved.

  • Each row has runId, workItemId, team, accountState, heldUsd (from run_budget_holds.reserved) and since (run_llm_accounts.updated_at). Rows are oldest first, at most 200.
  • Totals {count, heldUsd} come from a window sum, so they also count matching Runs beyond the 200 on the page.
  • Rows are limited to the consumer's Teams. Any query parameter gets 400 invalid_request. No run token, alias or key is returned.
  • PendingLLMBlocks and the new query share one selection constant, so the two cannot drift.
  • The operator-api.v1 schema gains unsettledAccountsResponse, and step 6 of investigate-a-runs-spend.md names the endpoint.
  • No migration, sweep or settlement change, so this stays clear of VIK-1755.

Vloer, VIK-1635 (https://vikunja.webgrip.dev/tasks/1635): PloegClient.summary reads unsettled-accounts in its own try. It filters rows by the user's Teams, sums the count and total from those rows, and adds unsettled and unsettledError to the summary.

  • Now shows a severe callout, "3 Runs hold budget Ploeg cannot release", with the format.money total, a "Show the 3 Runs" disclosure linking #work/<id>, and the runbook apps/ploeg/docs/ops/managed-workers.md, "Reconcile uncertainty".
  • Insights gets a "Cannot release" tile.
  • A failed read shows "Could not be loaded". A 404 from an older Ploeg shows "Not reported by this Ploeg".
  • The demo reports 0 without calling Ploeg. There is no settle, release or retry action.
  • The site replay is re-recorded in a separate chore(site) commit, because the demo summary gained the two fields.

Billing-adjacent: a human should review this.

What was reused from the 2026-10-01 branches

Nothing was carried over. I read both branches:

  • fix/ploeg-unsettled-holds-park: the floor sweep waits for unsettled holds, a new close reason, the Vikunja link and ADR-0048.
  • fix/vloer-work-item-clarity: the "Budget held, not spent" wording and Run counts.

Both change settlement behaviour, close reasons or Work Item page wording. That work is outside these tickets and overlaps the VIK-1755 settlement logic. Only their diagnosis was used: finished Runs keep holding budget. No ADR or ADR status change is included.

Tests

  • New pkg/store/llm_unsettled_test.go: the selection equals PendingLLMBlocks, holds equal run_budget_holds, rows are oldest first, totals include Runs beyond the page, plus scope, empty and limit cases.
  • New pkg/httpapi/operator_unsettled_test.go: schema validation, silver scope, no token, alias or key in the body, empty 200, 400 for a query, 405 for POST.
  • New Vloer cases in test/ploeg.test.ts, test/now-view.test.mjs and test/ploeg-activity.test.mjs.
  • cd apps/ploeg && go test -count=1 ./... && go vet ./...: all ok.
  • apps/vloer: npm test 704/704 pass, npm run check ok.
  • mise run verify: all gates passed (vloer, demo-replay, vloer-extension, ploeg, brand, site, site-demo, helm, release, integration, docs).

Checks left to CI

None. Live-provider tests stay opt-in.

🤖 Generated with Claude Code

🤖 Generated with Claude Code

Replaces #201 (feat: list and warn about Runs whose spend Ploeg cannot settle) after the Ploeg separation in #206. Its Ploeg part is [ploeg-hq/ploeg#50](https://github.com/ploeg-hq/ploeg/pull/50); this PR moves Unfold's pin to that branch and carries the rest. ## Commits - `d0058978` build(ploeg): pin ploeg-hq/ploeg#50 to list Runs whose spend cannot settle - `eff0d28f` feat(vloer): warn when Runs hold budget Ploeg cannot release (from `8de8151274`) - `61737d66` chore(site): re-record the demo replay for the unsettled summary fields (from `857397edb0`) ## Merge order 1. #206, the cutover. Until it merges, this PR's diff also shows the cutover commits it is stacked on. 2. [ploeg-hq/ploeg#50](https://github.com/ploeg-hq/ploeg/pull/50). 3. Move the pin in this PR's `build(ploeg)` commit to the merge commit on Ploeg's `main`, then merge. Until then the `ploeg-pin` check is red by design: it requires the pinned commit on Ploeg's `main`. ## Verification (local, at `61737d66` with Ploeg `e683eba0`) - `mise run verify` with the result cache, as a pull request runs it: all gates passed. That includes the Ploeg group (its own `scripts/verify.sh` at the pin), Vloer, the extension, the demo replay check, integration (managed qualification) and docs. - Ploeg side: GitHub CI passed on [ploeg-hq/ploeg#50](https://github.com/ploeg-hq/ploeg/pull/50). ## What happened to each commit of #201 | Commit | Subject | Here | | --- | --- | --- | | `5cb8ee814f` | feat(ploeg): list finished Runs whose spend Ploeg cannot settle | Ploeg part ported to ploeg-hq/ploeg#50 | | `8de8151274` | feat(vloer): warn when Runs hold budget Ploeg cannot release | Ploeg part ported to ploeg-hq/ploeg#50; Unfold part re-applied as `eff0d28f` | | `857397edb0` | chore(site): re-record the demo replay for the unsettled summary fields | re-recorded on the new base with `mise run demo-record` as `61737d66` | The original had no reviews or comments. Its checks were red because `development` itself failed at `mise install --locked`; #206 fixes that lock. No earlier check result carries over. <details><summary>Original description of #201</summary> ## Summary **Ploeg, VIK-1634** (https://vikunja.webgrip.dev/tasks/1634): new read-only `GET /api/v1/operator/unsettled-accounts`. It lists finished Runs whose account is still `minting`, `issued` or `unknown`. These are the Runs the block sweep retries and logs as `managed key block retry unresolved`. - Each row has `runId, workItemId, team, accountState, heldUsd` (from `run_budget_holds.reserved`) and `since` (`run_llm_accounts.updated_at`). Rows are oldest first, at most 200. - Totals `{count, heldUsd}` come from a window sum, so they also count matching Runs beyond the 200 on the page. - Rows are limited to the consumer's Teams. Any query parameter gets 400 `invalid_request`. No run token, alias or key is returned. - `PendingLLMBlocks` and the new query share one selection constant, so the two cannot drift. - The `operator-api.v1` schema gains `unsettledAccountsResponse`, and step 6 of `investigate-a-runs-spend.md` names the endpoint. - No migration, sweep or settlement change, so this stays clear of VIK-1755. **Vloer, VIK-1635** (https://vikunja.webgrip.dev/tasks/1635): `PloegClient.summary` reads `unsettled-accounts` in its own try. It filters rows by the user's Teams, sums the count and total from those rows, and adds `unsettled` and `unsettledError` to the summary. - Now shows a severe callout, "3 Runs hold budget Ploeg cannot release", with the `format.money` total, a "Show the 3 Runs" disclosure linking `#work/<id>`, and the runbook `apps/ploeg/docs/ops/managed-workers.md`, "Reconcile uncertainty". - Insights gets a "Cannot release" tile. - A failed read shows "Could not be loaded". A 404 from an older Ploeg shows "Not reported by this Ploeg". - The demo reports 0 without calling Ploeg. There is no settle, release or retry action. - The site replay is re-recorded in a separate `chore(site)` commit, because the demo summary gained the two fields. Billing-adjacent: a human should review this. ## What was reused from the 2026-10-01 branches Nothing was carried over. I read both branches: - `fix/ploeg-unsettled-holds-park`: the floor sweep waits for unsettled holds, a new close reason, the Vikunja link and ADR-0048. - `fix/vloer-work-item-clarity`: the "Budget held, not spent" wording and Run counts. Both change settlement behaviour, close reasons or Work Item page wording. That work is outside these tickets and overlaps the VIK-1755 settlement logic. Only their diagnosis was used: finished Runs keep holding budget. No ADR or ADR status change is included. ## Tests - New `pkg/store/llm_unsettled_test.go`: the selection equals `PendingLLMBlocks`, holds equal `run_budget_holds`, rows are oldest first, totals include Runs beyond the page, plus scope, empty and limit cases. - New `pkg/httpapi/operator_unsettled_test.go`: schema validation, silver scope, no token, alias or key in the body, empty 200, 400 for a query, 405 for POST. - New Vloer cases in `test/ploeg.test.ts`, `test/now-view.test.mjs` and `test/ploeg-activity.test.mjs`. - `cd apps/ploeg && go test -count=1 ./... && go vet ./...`: all ok. - `apps/vloer`: `npm test` 704/704 pass, `npm run check` ok. - `mise run verify`: all gates passed (vloer, demo-replay, vloer-extension, ploeg, brand, site, site-demo, helm, release, integration, docs). ## Checks left to CI None. Live-provider tests stay opt-in. 🤖 Generated with [Claude Code](https://claude.com/claude-code) </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
711a4814 moved uv to 0.12.22 in mise.toml but left the lockfile at
0.12.21. mise-action runs `mise install --locked`, which refuses a
version the lockfile does not hold, so the checks job has failed before
any gate since that commit, on development and on every pull request.

`mise lock uv` (mise 2026.9.18) records 0.12.22 for all seven platforms.
The openspec lock files it deletes under .mise/locks are kept.

Refs: https://github.com/webgrip/unfold/issues/2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the vendored apps/ploeg tree with a gitlink to
https://github.com/ploeg-hq/ploeg.git at v0.1.0
(87f8dc45a0ea768c6ab95196d8b99d481df10c65), which was extracted from
this repository at 9c1d53f.

- mise run setup, the verify, docs and demo checkouts and the TechDocs
  prepare commands initialise the submodule.
- scripts/ploeg-pin.mjs refuses vendored source, another repository and
  an uninitialised or modified checkout. The ploeg-pin job also requires
  the pinned commit on Ploeg's main, and the release waits for it.
- verify runs Ploeg's own scripts/verify.sh at the pin and compiles the
  unified demo helper, which now imports github.com/ploeg-hq/ploeg.
- The docs build still renders Ploeg's pinned pages, but no longer
  regenerates or validates Ploeg's configuration reference, domain pages
  or decision ledger, and it links Ploeg's source files on GitHub at the
  pinned commit. The combined glossary keeps a decision that a pinned
  model cites by URL instead of mangling it into a relative path.
- Renovate ignores apps/ploeg, drops the Go overlay and leaves the pin
  to people. CI no longer builds the ploegd image context.

Refs: https://github.com/webgrip/unfold/issues/2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unfold's train now versions Vloer only; github.com/ploeg-hq/ploeg
versions and publishes Ploeg with GitHub Actions.

- on_release_published.yml drops the Ploeg chart, image, signing and
  distribution jobs. The Vloer publisher is the only one, so it takes
  the GitHub release out of draft itself.
- publish_release.py and publish_chart.py refuse ploeg before any Git,
  network or file access. The Go module export to github.com/webgrip/ploeg
  is gone, including the call that disabled GitHub Actions there.
- release-prepare.mjs and apps/.releaserc.cjs touch only Vloer's chart,
  and a commit scoped ploeg never releases Unfold.
- release-floors.json keeps Ploeg's floor and withdrawn 1.0.0-rc.1, marks
  the component retired after 0.4.0-rc.35, and both loaders refuse a
  train that versions a retired component.
- The release preflight no longer checks registry access for ploegd or
  charts/ploeg.

Refs: https://github.com/webgrip/unfold/issues/2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: record that Unfold pins Ploeg and releases only Vloer
All checks were successful
[Workflow] On Pull Request / ploeg-pin (pull_request) Successful in 42s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 16s
[Workflow] On Pull Request / checks (pull_request) Successful in 7m8s
[Workflow] On Pull Request / warnings (pull_request) Successful in 0s
1bca2ac69b
ADR-0019 records the consumer side of the separation approved in
webgrip/unfold#1: Ploeg lives in github.com/ploeg-hq/ploeg, Unfold pins
it as a submodule, and the unfold-v train versions Vloer only. It
supersedes ADR-0004; ADR-0001 and ADR-0018 get dated notes.

README, AGENTS.md, NOTICE, the team-silver skill and the current pages
now say where Ploeg lives, how the pin moves, what Unfold releases and
where Ploeg's artifacts come from.

Refs: https://github.com/webgrip/unfold/issues/2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build(docs): treat Ploeg's archived history pages as records
All checks were successful
[Workflow] On Pull Request / ploeg-pin (pull_request) Successful in 24s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 29s
[Workflow] On Pull Request / checks (pull_request) Successful in 2m22s
[Workflow] On Pull Request / warnings (pull_request) Successful in 0s
3870a8b560
Ploeg's main keeps its pre-separation release history in
docs/history/legacy-changelog.md, a record no current page links. Unfold
renders Ploeg's docs from the pinned commit, so any pin past v0.1.0 failed
the docs build with that page as an orphan. ploeg/history now joins
ploeg/backlog as a record path: kept, marked "not current guidance" and
left out of the nav and search.

Refs: https://github.com/webgrip/unfold/issues/2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves apps/ploeg to e683eba0, the head of ploeg-hq/ploeg#50, which is
the Ploeg side of Unfold PR 201. Once that pull request merges, move
the pin to its merge commit on Ploeg's main. Until then the ploeg-pin check
stays red, as it should.

Refs: https://github.com/ploeg-hq/ploeg/pull/50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The summary now also reads Ploeg's unsettled-accounts list in its own
try, keeps only the rows of Teams the user may read and sums the count
and held total from those rows. A failure never fails the summary: it
reports "Could not be loaded", and an older Ploeg that answers 404
reports "Not reported by this Ploeg". The demo reports zero without
asking Ploeg.

Now shows a severe callout, "3 Runs hold budget Ploeg cannot release",
with the held total, a "Show the 3 Runs" disclosure linking each Work
Item and Ploeg's "Reconcile uncertainty" runbook. Insights shows the
same count and total in a "Cannot release" tile. Vloer offers no settle,
release or retry action.

VIK-1635

Replaces-commit: 8de8151274 (#201)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
chore(site): re-record the demo replay for the unsettled summary fields
Some checks failed
[Workflow] On Pull Request / ploeg-pin (pull_request) Failing after 28s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 32s
[Workflow] On Pull Request / checks (pull_request) Successful in 10m5s
[Workflow] On Pull Request / warnings (pull_request) Successful in 0s
61737d668a
The demo summary now carries unsettled and unsettledError, so the hosted
/demo replay is recorded again from Vloer's deterministic demo.

VIK-1635

Re-recorded with `mise run demo-record` on top of the Ploeg separation
(webgrip/unfold#206) instead of merging the recorded JSON.

Replaces-commit: 857397edb0 (#201)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ryangr0 force-pushed ryangr0/replace/201-unsettled-accounts from 61737d668a
Some checks failed
[Workflow] On Pull Request / ploeg-pin (pull_request) Failing after 28s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 32s
[Workflow] On Pull Request / checks (pull_request) Successful in 10m5s
[Workflow] On Pull Request / warnings (pull_request) Successful in 0s
to b6d2f25fa1
Some checks failed
[Workflow] On Pull Request / checks (pull_request) Has been cancelled
[Workflow] On Pull Request / warnings (pull_request) Has been cancelled
[Workflow] On Pull Request / ploeg-pin (pull_request) Has been cancelled
[Workflow] On Pull Request / release-policy (pull_request) Has been cancelled
2026-10-04 07:08:57 +00:00
Compare
ryangr0 force-pushed ryangr0/replace/201-unsettled-accounts from b6d2f25fa1
Some checks failed
[Workflow] On Pull Request / checks (pull_request) Has been cancelled
[Workflow] On Pull Request / warnings (pull_request) Has been cancelled
[Workflow] On Pull Request / ploeg-pin (pull_request) Has been cancelled
[Workflow] On Pull Request / release-policy (pull_request) Has been cancelled
to 46819e9723
All checks were successful
[Workflow] On Pull Request / ploeg-pin (pull_request) Successful in 33s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 40s
[Workflow] On Pull Request / checks (pull_request) Successful in 6m29s
[Workflow] On Pull Request / warnings (pull_request) Successful in 0s
2026-10-04 07:12:38 +00:00
Compare
ryangr0 merged commit 27d61c4e01 into development 2026-10-04 08:39:04 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/unfold!211
No description provided.