Skip to content

feat(taskrunner): replacement, cancellation, batching and retries - #331

Merged
alanshaw merged 12 commits into
ash/feat/taskrunner-corefrom
ash/feat/taskrunner-scheduling
Nov 10, 2026
Merged

alanshaw merged 12 commits into
ash/feat/taskrunner-corefrom
ash/feat/taskrunner-scheduling

Conversation

@alanshaw

@alanshaw alanshaw commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

The second step of #328, stacked on #330: the scheduling features the task runner RFC describes for async tasks. Like #330, nothing in the server uses them yet.

  • Replacing. A delayed task can be submitted as a replacement. If one with the same subject and action is waiting and has not started, whichever runs later is kept and the other is dropped. Ingot will use this to push a cached file's eviction back each time it is read. A replacement that has already started is left alone, and the new one is added beside it.
  • Cancelling. A task that has not started can be cancelled, from a handler or in a caller's own transaction, so the cancel commits with the writes that make it necessary. Ingot will use this to cancel an upload's cleanup once the write that claims its shards commits.
  • Batching. An action can take a run of consecutive tasks on one subject in a single go, up to a limit, stopping at the first task of a different action so the order still holds. Ingot will use this to register a space's queued objects with the network in one request.
  • Retries. An action can register a backoff and an attempt limit for its async tasks, and a handler can mark an error as one no retry will fix. Sync tasks are never retried: their error goes to the caller.

The package also gets a README with an example of every feature, including the earlier ones.

🤖 Generated with Claude Code

@alanshaw alanshaw added the ingot label Nov 2, 2026
@alanshaw alanshaw self-assigned this Nov 2, 2026
@alanshaw
alanshaw added this pull request to stack #332 November 2, 2026 18:10
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch 3 times, most recently from 93d67eb to 611d661 Compare November 2, 2026 18:49
@alanshaw
alanshaw requested a balanced review from Copilot November 2, 2026 18:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Retry scheduling breaks cancellation guarantees and same-subject ordering, while non-atomic batch failure updates violate the fail-together contract.

3 open issues
What changed in this PR

Adds scheduling features to the standalone Postgres task runner, building on #330 toward #328’s replacement of specialized locks and sweepers. The runner remains unwired from the server.

Changes:

  • Adds pending-task replacement and cancellation, async batching, and configurable retries.
  • Introduces a second schema migration for scheduling state.
  • Adds live database tests and usage documentation.
File Description
taskrunner/​worker.go Claims individual tasks or batches before execution.
taskrunner/​tree.go Executes batches and integrates retries and cancellation notifications.
taskrunner/​schema.go Adds scheduling columns, cancellation state, and a replacement index.
taskrunner/​scheduling.go Implements replacement, cancellation, batch claiming, and retries.
taskrunner/​scheduling_live_test.go Tests scheduling features against Postgres.
taskrunner/​runner.go Exposes scheduling options and validates async-only batching.
taskrunner/​README.md Documents runner behavior and feature usage.
CLAUDE.md Updates the task runner package description.

Review effort: Balanced

Comment thread taskrunner/scheduling.go Resolved
Comment thread taskrunner/scheduling.go Resolved
Comment thread taskrunner/scheduling.go Resolved
alanshaw added a commit that referenced this pull request Nov 2, 2026
…old their subject

From review on #331:
- a batch's failures and retries are recorded in one transaction under its
  lease, and waiters are woken only after it commits
- RetryPolicy.HoldsSubject keeps the tasks behind a retry waiting, so an
  add that fails and retries still runs before a remove submitted after it;
  it is opt-in because it also holds up the subject's sync tasks
- a task waiting to retry stays cancellable, which the docs now say

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from 611d661 to 89176f7 Compare November 2, 2026 19:12
@alanshaw
alanshaw requested a lite review from Copilot November 2, 2026 19:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical finding remains in taskrunner/tree.go: handlerless batch failures can commit task outcomes individually, leaving the batch partially failed instead of enforcing the documented fail-together behavior.

1 open issue
3 resolved since last review

Review effort: Lite

Comment thread taskrunner/tree.go Resolved
alanshaw added a commit that referenced this pull request Nov 2, 2026
…old their subject

From review on #331:
- a batch's failures and retries are recorded in one transaction under its
  lease, and waiters are woken only after it commits
- RetryPolicy.HoldsSubject keeps the tasks behind a retry waiting, so an
  add that fails and retries still runs before a remove submitted after it;
  it is opt-in because it also holds up the subject's sync tasks
- a task waiting to retry stays cancellable, which the docs now say

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from 89176f7 to 880fc71 Compare November 2, 2026 20:07
@alanshaw
alanshaw requested a lite review from Copilot November 2, 2026 20:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical active issue remains because batch expiry is not atomic, allowing some tasks to be committed as expired while others remain pending and later run independently, violating the fail-together contract.

1 open issue
1 resolved since last review

Review effort: Lite

Comment thread taskrunner/tree.go Resolved
alanshaw added a commit that referenced this pull request Nov 3, 2026
From review on #331: finish takes the tasks to record and writes them in one
lease-checked transaction, so a batch's expiries land together or not at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alanshaw added a commit that referenced this pull request Nov 3, 2026
…old their subject

From review on #331:
- a batch's failures and retries are recorded in one transaction under its
  lease, and waiters are woken only after it commits
- RetryPolicy.HoldsSubject keeps the tasks behind a retry waiting, so an
  add that fails and retries still runs before a remove submitted after it;
  it is opt-in because it also holds up the subject's sync tasks
- a task waiting to retry stays cancellable, which the docs now say

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from e7f6453 to e6e230b Compare November 3, 2026 15:02
alanshaw added a commit that referenced this pull request Nov 3, 2026
From review on #331: finish takes the tasks to record and writes them in one
lease-checked transaction, so a batch's expiries land together or not at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw requested a lite review from Copilot November 3, 2026 15:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Active critical and moderate findings remain: mixed batch outcomes can violate the fail-together contract when expiry is handled separately, and DrainLimit can be exceeded by batched claims.

2 open issues
1 resolved since last review

Review effort: Lite

Comment thread taskrunner/tree.go Resolved
Comment thread taskrunner/worker.go Resolved
alanshaw added a commit that referenced this pull request Nov 3, 2026
…r-task expiry in batches

From review on #331:
- a drain counts every task a batch claims toward DrainLimit, and cuts the
  next batch short to fit, so a worker never runs more than DrainLimit tasks
  on one subject before releasing its lease
- a task already past its Expires is recorded as expired and left out of
  its batch; HandleBatch and BatchHandler now say so, and a test pins it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw requested a lite review from Copilot November 3, 2026 17:12
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from 4224daa to 8de6457 Compare November 6, 2026 15:20
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from 8de6457 to 995d77e Compare November 9, 2026 16:14
alanshaw and others added 12 commits November 10, 2026 12:43
Async tasks can replace a pending one (the later NotBefore wins), be
cancelled before they start, run in batches of one action, and be retried
under a per-action policy. Adds a README with usage examples.

Refs #328

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…old their subject

From review on #331:
- a batch's failures and retries are recorded in one transaction under its
  lease, and waiters are woken only after it commits
- RetryPolicy.HoldsSubject keeps the tasks behind a retry waiting, so an
  add that fails and retries still runs before a remove submitted after it;
  it is opt-in because it also holds up the subject's sync tasks
- a task waiting to retry stays cancellable, which the docs now say

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From review on #331: finish takes the tasks to record and writes them in one
lease-checked transaction, so a batch's expiries land together or not at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ase lost

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r-task expiry in batches

From review on #331:
- a drain counts every task a batch claims toward DrainLimit, and cuts the
  next batch short to fit, so a worker never runs more than DrainLimit tasks
  on one subject before releasing its lease
- a task already past its Expires is recorded as expired and left out of
  its batch; HandleBatch and BatchHandler now say so, and a test pins it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There is no deployment to carry forward, so the columns for replacement,
cancellation and retries are part of the one step that creates the table
rather than a step of their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A retry marked its task not replaceable, so a replacement submitted during
the backoff was added beside it and the stale retry still ran: a cached
copy renewed while its eviction backed off was removed before its new time.
A handler fails before acting, so a task waiting to retry has done nothing,
as Cancel already assumes. A replacement now takes its place, starting its
attempts afresh and clearing any hold on the subject.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DrainLimit bounded a lease's work by counting a batch's tasks and cutting the
next batch short to fit, so it also capped every batch at DrainLimit: queued
registrations went out 100 at a time against their own limit of 256. It now
counts handler runs, so a batch's size is only its action's limit, and a
lease still gives way after DrainLimit runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alanshaw
alanshaw force-pushed the ash/feat/taskrunner-scheduling branch from 995d77e to 033db23 Compare November 10, 2026 12:46
@alanshaw
alanshaw merged commit ce9e35c into main Nov 10, 2026
21 checks passed
@alanshaw
alanshaw deleted the ash/feat/taskrunner-scheduling branch November 10, 2026 16:00
alanshaw added a commit that referenced this pull request Nov 10, 2026
…#334)

The third and last runner step of #328, stacked on #331: placing tasks
on one instance, and metrics, as the [task runner
RFC](fil-one/RFC#40) describes. Nothing in the
server uses the runner yet; the next pull requests start moving Ingot
onto it.

**Placement.** A task can be tied to one instance, for work on that
instance's local state such as a cached file. Only that instance runs
it, and other instances also leave the tasks queued behind it on the
same subject until it has run, so the subject's order holds. A sync task
tied to another instance is refused, since it can only run in its
caller. Ingot runs as one instance today, so this changes nothing yet;
it is there so a multi-instance design can place work without changing
the runner again.

**Metrics.** The runner reports through OpenTelemetry, labelled by queue
and action, so the deployment's pool sizes can be tuned from real
numbers: how many tasks are ready and how many are delayed, how long the
oldest ready task has waited, how long callers wait for a subject, how
long tasks take by outcome, how busy each queue's workers are, and how
often a lease is taken over from a consumer that stopped renewing it.
The queue counts are read from Postgres each time the exporter collects.
With no meter provider configured, every instrument does nothing.

The README gains a section on each.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 10, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:e83df92`](https://github.com/orgs/fil-forge/packages/container/piri/1535715713)

What fil-forge/piri brings over staging's pin, [`029d86d..f62b605`](fil-forge/piri@029d86d...f62b605):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:8fc72f3`](https://github.com/orgs/fil-forge/packages/container/ingot/1537185609)

What fil-forge/ingot brings over staging's pin, [`af89169..7bfca91`](fil-forge/ingot@af89169...7bfca91):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 10, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:e83df92`](https://github.com/orgs/fil-forge/packages/container/piri/1535715713)

What fil-forge/piri brings over staging's pin, [`029d86d..f62b605`](fil-forge/piri@029d86d...f62b605):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:e83df92`](https://github.com/orgs/fil-forge/packages/container/piri/1535715713)

What fil-forge/piri brings over staging's pin, [`029d86d..f62b605`](fil-forge/piri@029d86d...f62b605):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:e83df92`](https://github.com/orgs/fil-forge/packages/container/piri/1535715713)

What fil-forge/piri brings over staging's pin, [`029d86d..f62b605`](fil-forge/piri@029d86d...f62b605):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:e83df92`](https://github.com/orgs/fil-forge/packages/container/piri/1535715713)

What fil-forge/piri brings over staging's pin, [`029d86d..f62b605`](fil-forge/piri@029d86d...f62b605):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:d5b2a71`](https://github.com/orgs/fil-forge/packages/container/piri/1542539277)

What fil-forge/piri brings over staging's pin, [`029d86d..5e80c10`](fil-forge/piri@029d86d...5e80c10):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))
- fil-forge/piri#220 ([5e80c10](fil-forge/piri@5e80c10))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:cf21468`](https://github.com/orgs/fil-forge/packages/container/piri/1542621852)

What fil-forge/piri brings over staging's pin, [`029d86d..e663f38`](fil-forge/piri@029d86d...e663f38):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))
- fil-forge/piri#220 ([5e80c10](fil-forge/piri@5e80c10))
- fil-forge/piri#225 ([e663f38](fil-forge/piri@e663f38))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:86a32ca`](https://github.com/orgs/fil-forge/packages/container/ingot/1538499073)

What fil-forge/ingot brings over staging's pin, [`af89169..82a70c9`](fil-forge/ingot@af89169...82a70c9):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
fil-forge-bot Bot added a commit to fil-forge/infra-nodes that referenced this pull request Nov 11, 2026
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what
`nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks
it up on its next reconcile pass, waits for a safe proving window and restarts each
service that changed.

This pull request is rebuilt on every push to `main` and closed once staging matches
dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand.

## piri

[`sha256:d0e88a9`](https://github.com/orgs/fil-forge/packages/container/piri/1506614110) → [`sha256:cf21468`](https://github.com/orgs/fil-forge/packages/container/piri/1542621852)

What fil-forge/piri brings over staging's pin, [`029d86d..e663f38`](fil-forge/piri@029d86d...e663f38):

- fil-forge/piri#215 ([3d07e1a](fil-forge/piri@3d07e1a))
- fil-forge/piri#214 ([6c47937](fil-forge/piri@6c47937))
- fil-forge/piri#216 ([6f2be19](fil-forge/piri@6f2be19))
- fil-forge/piri#218 ([67efb3b](fil-forge/piri@67efb3b))
- fil-forge/piri#217 ([a53055c](fil-forge/piri@a53055c))
- fil-forge/piri#228 ([f62b605](fil-forge/piri@f62b605))
- fil-forge/piri#220 ([5e80c10](fil-forge/piri@5e80c10))
- fil-forge/piri#225 ([e663f38](fil-forge/piri@e663f38))

## ingot

[`sha256:ac6dc48`](https://github.com/orgs/fil-forge/packages/container/ingot/1530171943) → [`sha256:70a32a8`](https://github.com/orgs/fil-forge/packages/container/ingot/1542745662)

What fil-forge/ingot brings over staging's pin, [`af89169..2a460d7`](fil-forge/ingot@af89169...2a460d7):

- fil-forge/ingot#361 ([7d9f32b](fil-forge/ingot@7d9f32b))
- fil-forge/ingot#330 ([cddc726](fil-forge/ingot@cddc726))
- fil-forge/ingot#331 ([ce9e35c](fil-forge/ingot@ce9e35c))
- fil-forge/ingot#334 ([b9fd991](fil-forge/ingot@b9fd991))
- fil-forge/ingot#336 ([c054d14](fil-forge/ingot@c054d14))
- fil-forge/ingot#337 ([26ee5e9](fil-forge/ingot@26ee5e9))
- fil-forge/ingot#340 ([155d969](fil-forge/ingot@155d969))
- fil-forge/ingot#341 ([0aff04e](fil-forge/ingot@0aff04e))
- fil-forge/ingot#342 ([24d752a](fil-forge/ingot@24d752a))
- fil-forge/ingot#343 ([e04c724](fil-forge/ingot@e04c724))
- fil-forge/ingot#344 ([8c0cd13](fil-forge/ingot@8c0cd13))
- fil-forge/ingot#345 ([c8cb626](fil-forge/ingot@c8cb626))
- fil-forge/ingot#346 ([ff68601](fil-forge/ingot@ff68601))
- fil-forge/ingot#347 ([c7f3d8d](fil-forge/ingot@c7f3d8d))
- fil-forge/ingot#348 ([e0bd021](fil-forge/ingot@e0bd021))
- fil-forge/ingot#349 ([a4b8dd7](fil-forge/ingot@a4b8dd7))
- fil-forge/ingot#350 ([2ea37b7](fil-forge/ingot@2ea37b7))
- fil-forge/ingot#351 ([d330171](fil-forge/ingot@d330171))
- fil-forge/ingot#352 ([6172383](fil-forge/ingot@6172383))
- fil-forge/ingot#362 ([bfd2961](fil-forge/ingot@bfd2961))
- fil-forge/ingot#363 ([a2e9841](fil-forge/ingot@a2e9841))
- fil-forge/ingot#365 ([7bfca91](fil-forge/ingot@7bfca91))
- fil-forge/ingot#319 ([3d5520b](fil-forge/ingot@3d5520b))
- fil-forge/ingot#353 ([82a70c9](fil-forge/ingot@82a70c9))
- fil-forge/ingot#378 ([2a460d7](fil-forge/ingot@2a460d7))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants