Called by upgrade-microbus. Upgrades the project from v1.31.x to v1.32.0. Deletes the removed workflow.Graph.SetTimeBudget (per-task time budgets are now declared on the task endpoint via the new sub.TimeBudget option), renames the foreman DefaultTimeBudget config to TimeBudget, adds a trailing *workflow.FlowOptions argument to foreman Create and Run (priority/fairness), and regenerates manifests and mocks (which now carry a "DO NOT EDIT" marker).
Scanned 6/6/2026
Install via CLI
openskills install microbus-io/fabric---
name: upgrade-v1-32-0
user-invocable: false
description: Called by upgrade-microbus. Upgrades the project from v1.31.x to v1.32.0. Deletes the removed workflow.Graph.SetTimeBudget (per-task time budgets are now declared on the task endpoint via the new sub.TimeBudget option), renames the foreman DefaultTimeBudget config to TimeBudget, adds a trailing *workflow.FlowOptions argument to foreman Create and Run (priority/fairness), and regenerates manifests and mocks (which now carry a "DO NOT EDIT" marker).
---
## Background
- **`workflow.Graph.SetTimeBudget` deleted outright.** `Graph.SetTimeBudget`, `Graph.TimeBudget`, and the
`Node.TimeBudget` field are gone. The workflow graph no longer encodes any timing. Breaking change, no
deprecation shim - acceptable at this early stage.
- **`sub.TimeBudget(duration)` is the replacement.** A per-task budget is now declared by the *task endpoint*
on its subscription. The connector shortens that handler's inbound context deadline to
`min(caller-provided budget, declared budget)`, so a task that exceeds its declared budget is cancelled via
its context. The budget is keyed by task endpoint, not by graph position: one `sub.TimeBudget` on the
endpoint governs every graph that dispatches to it.
- **Foreman `DefaultTimeBudget` config renamed to `TimeBudget`.** Same default (`2m`) and validation
(`dur [1s,15m]`), but it is now documented as the hard *ceiling* applied to every task dispatch call,
analogous to the ingress proxy's request-timeout ceiling. A task declaring a shorter `sub.TimeBudget` is
cancelled at that shorter bound.
- **Manifest gains an optional `timeBudget`.** `cmd/genmanifest` AST-generates a declared-only `timeBudget`
on `tasks`/`functions`/`webs` entries from `sub.TimeBudget`. It records the declared value only, never a
deployment-resolved effective one. Regenerate manifests; never hand-edit the field.
- **Foreman `Create` and `Run` take a trailing `opts *workflow.FlowOptions`.** `FlowOptions` (priority,
fairness key, fairness weight) is a new flow-level type in the `workflow` package. The signatures are now
`Create(ctx, workflowName, initialState, opts)` and `Run(ctx, workflowName, initialState, opts)`; `nil`
means all defaults. `CreateIn`/`RunIn`, the `foremanapi` `Client`/`MulticastClient` `Create`/`Run`, the
generated `WorkflowRunner` interface, and `marshalWorkflow` all gain the argument. The per-workflow
`Executor` method signatures are **unchanged**; an `Executor.WithFlowOptions(*workflow.FlowOptions)`
builder threads options through, so executor call sites need no edit.
- **New foreman `DefaultPriority` config** (default `5`, `int [0,]`): the priority assigned when a caller
passes no `FlowOptions.Priority`. Lower numbers run first.
- **`mock.go`, `mock_test.go`, and `manifest.yaml` now carry a generated `DO NOT EDIT` marker.** They are
regenerated by `cmd/genmock` / `cmd/genmanifest`; never hand-edit them.
This skill does not auto-migrate the *value* of a removed budget. Whether a removed `graph.SetTimeBudget` was
load-bearing is a developer judgement; it is enough to know that to restore a tight per-task bound you add
`sub.TimeBudget` on that task endpoint's subscription.
## Workflow
```
Upgrade a Microbus project to v1.32.0:
- [ ] Step 1: Remove graph.SetTimeBudget calls; re-express load-bearing budgets via sub.TimeBudget
- [ ] Step 2: Rename the foreman DefaultTimeBudget config to TimeBudget
- [ ] Step 3: Insert a nil argument into foreman Create / Run callers
- [ ] Step 4: Regenerate manifests and mocks with cmd/genmanifest and cmd/genmock
```
#### Step 1: Remove `graph.SetTimeBudget` Calls
```bash
grep -rn "SetTimeBudget" --include="*.go" .
```
Only calls on a `*workflow.Graph` receiver are affected. `coreservices/httpingress`'s `SetTimeBudget` is a
config setter and `frame.SetTimeBudget` is a header writer - both are unrelated and must be left alone.
Disambiguate by the receiver: a `graph.SetTimeBudget("nodeName", duration)` inside a workflow-graph builder
is the one to remove.
For each `graph.SetTimeBudget(name, duration)`:
1. Delete the call (and any now-orphaned `// per-task time budget` comment).
2. If the budget was load-bearing (a task that genuinely must be cancelled before the foreman ceiling),
re-express it on that task's endpoint. Find the task-hosting microservice and add
`sub.TimeBudget(duration)` to that task's `svc.Subscribe( // MARKER: <TaskName> ...)` block in its
`intermediate.go`, after the `sub.Task(...)` option. Use the same duration the graph call used.
3. If removing the call leaves the `time` import unused in that file, drop it (`goimports -w`).
#### Step 2: Rename the Foreman `DefaultTimeBudget` Config to `TimeBudget`
```bash
grep -rn "DefaultTimeBudget" --include="*.go" --include="*.yaml" --include="*.txt" --include="*.md" .
```
- In `config.yaml` / `config.local.yaml` (and `env.yaml` / `env.local.yaml` if present), under the foreman's
hostname section (e.g. `foreman.core:`), rename the `DefaultTimeBudget:` key to `TimeBudget:`. Keep the
value. Do not touch a `TimeBudget` under `http.ingress.core:` - that is the unrelated ingress ceiling.
- Replace any `svc.SetDefaultTimeBudget(` / `svc.DefaultTimeBudget()` calls targeting the foreman with
`svc.SetTimeBudget(` / `svc.TimeBudget()`. These are rare in downstream code (the foreman is reached via
its client, not its config getters).
#### Step 3: Insert a `nil` Argument Into Foreman `Create` / `Run` Callers
```bash
grep -rn "\.Create(\|\.Run(\|\.RunAndParse(" --include="*.go" . | grep -i foreman
```
Only direct calls to a `foremanapi` client/mock are affected: `foremanapi.NewClient(svc).Create(...)`,
`.Run(...)`, `.RunAndParse(...)`, and any `WorkflowRunner` whose concrete type is `foremanapi.Client`.
Append a final `nil` argument (the `*workflow.FlowOptions`):
- `foremanClient.Create(ctx, url, state)` -> `foremanClient.Create(ctx, url, state, nil)`
- `foremanClient.Run(ctx, url, state)` -> `foremanClient.Run(ctx, url, state, nil)`
- `RunAndParse(ctx, url, state, &out)` -> `RunAndParse(ctx, url, state, nil, &out)` (opts goes before the result)
An AST-safe `gofmt -r` per file avoids touching multiline arguments by hand, e.g.
`gofmt -r 'c.Create(a, b, d) -> c.Create(a, b, d, nil)' -w <file>` (and the same for `Run`). Scope it to
files that call the foreman client so an unrelated 3-argument `.Create` elsewhere is not rewritten.
Do **not** edit generated per-workflow `Executor` methods or their call sites - their signatures did not
change. To pass options through the typed executor in a test, use
`exec.WithFlowOptions(&workflow.FlowOptions{...})`. `CreateTask` is unchanged.
#### Step 4: Regenerate Manifests and Mocks
From inside each microservice directory:
```bash
go run github.com/microbus-io/fabric/cmd/genmanifest --path .
go run github.com/microbus-io/fabric/cmd/genmock --path .
```
`genmanifest` bumps `frameworkVersion` to `1.32.0`, refreshes `modifiedAt`, emits the declared-only
`timeBudget` field where `sub.TimeBudget` is declared, and stamps the `# Code generated ... DO NOT EDIT.`
marker. `genmock` regenerates `mock.go`/`mock_test.go` with the updated `Create`/`Run` signatures and the
`// Code generated ... DO NOT EDIT.` marker. Both are idempotent; run them at housekeeping time too.
No comments yet. Be the first to comment!