Skip to content

feat(taskflow)!: resolve a task's templates through its manifest version - #306

Open
lokewate wants to merge 1 commit into
feat/manifest-version-enginefrom
feat/manifest-pinned-flows
Open

lokewate wants to merge 1 commit into
feat/manifest-version-enginefrom
feat/manifest-pinned-flows

Conversation

@lokewate

@lokewate lokewate commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Each task records the manifest version its parent workflow carries, so a task's steps, submissions, drafts and renders all resolve their templates through the version the flow started on.

Stack

Manifest versioning, in five PRs to review and merge in order. Each one builds and passes its tests on its own.

  1. feat(artifact)!: load versioned manifests as a set #305 artifact: load versioned manifests as a set
  2. feat(artifact)!: resolve explicitly versioned rows across manifests #346 artifact: resolve explicitly versioned rows across manifests
  3. feat(artifact): load newer manifests on demand #319 artifact: load newer manifests on demand
  4. feat(workflow)!: carry the manifest version through every workflow #322 workflow: carry the manifest version through every workflow
  5. feat(taskflow)!: resolve a task's templates through its manifest version #306 taskflow: resolve a task's templates through its manifest version ← this PR

#320 (validate every artifact a manifest lists) and #321 (manifestcheck) are parked: pinning doesn't need them, and a release job can bring validation back later.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Other (please describe):

Changes Made

  • NewTaskManager takes an *artifact.Set.
  • StartTask resolves the task template, its workflow and render config through the payload's version. It stores the version on the task record and seeds it into the task's own workflow.
  • StartTaskStep, CompleteTaskStep and SaveDraft resolve the step template through the record's version, not the payload's. GetTaskRenderInfo and ZoneViewAssembler.Assemble (a host's task view) do the same for rendering.
  • artifact.WithManifestVersion / ManifestVersionFrom. Each of those puts the version on the context, so plugins, extensions and the renderer's template provider can resolve through it too.
  • A task without a version fails before anything is written. Falling back to the current version would change templates under a running task. The error is a non-retryable ApplicationError, returned unwrapped because Temporal reads retryability off the top-level error only. A version this replica has not loaded stays retryable, since Set.Registry loads newer manifests on demand.
  • Store. TaskRecord.ManifestVersion is stored in column manifest_version.
  • Breaking:
    • NewTaskManager takes an *artifact.Set;
    • hosts add manifest_version INTEGER NOT NULL DEFAULT 0 to the task records table;
    • workflows already running fail their next task.
  • Logs: the task and step start logs name the manifest version, so a step's log says which templates it ran.

Testing

  • I have tested this change locally
  • I have added tests that prove my fix is effective or that my feature works
  • I have tested edge cases
  • All existing tests pass

Run with go test -race ./taskflow/.... The tests cover a task that keeps its starting version after the current version moves (through start, step, submit and extensions), SaveDraft on a versioned task, missing-version refusal as an unwrapped non-retryable error with nothing written and the step never claimed, an unpublished version staying retryable, and the renderer receiving the task's version.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have checked that there are no merge conflicts

Related Issues

The host side is OpenNSW/nsw-srilanka#660. It records the version on each flow row, passes it at start, and adds the migration. Tracked in OpenNSW/one-trade-artifacts#243.

Screenshots/Demo

N/A

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6fe5d0f5-3320-4a6c-8a7d-577d56f4e7bd
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 12, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lokewate
lokewate force-pushed the feat/manifest-pinned-flows branch from 9bf9682 to ba293b8 Compare October 20, 2026 08:13
lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 20, 2026
Artifacts are published as manifest_v1.json, manifest_v2.json, …
(core artifact.Set). A published manifest and its files never change; a
change is a new file and a new manifest. Each flow records the version
current when it started and resolves every template through it, so a
deploy never changes the templates of a running flow.

- Bootstrap loads every manifest and makes the highest current, or the
  one artifacts.manifestVersion pins. Unless
  artifacts.skipStartupValidation is set, it fetches every artifact of
  the current manifest and checks it parses and points only at listed
  artifacts: core's kinds (including render configs' section
  templates), task_config, static_data, and the templates step
  templates' notification extensions name. A broken manifest stops the
  deploy.
- caseflow records the current version on the case_flows row and starts
  the flow with workflow.VarManifestVersion. A retry of a recorded flow
  resumes on its recorded version before the current manifest is
  consulted, so it still starts when the current manifest dropped its
  template. Only a new flow is checked against the current manifest.
- Agency inject does the same on agency_workflow, and the officer case
  view reads each workflow's task_config from its own version.
- The definition handler resolves a split's branch workflows in the
  calling workflow's version, and the template provider (form
  rendering, notification templates) resolves through the version core
  puts on the context. Without one it fails rather than fall back to
  the current version.
- Static data is fetched by explicit version, from whichever manifest
  lists it.
- Consignment flow listings name each flow from its own version.
- The example configs and helm values read GitHub through the
  raw-content host: startup validation fetches every artifact, more
  than the unauthenticated REST limit allows. The replay harness skips
  startup validation, since every replay builds its own app.
- go.mod pins core to the head of OpenNSW/core#306 until it merges.

BREAKING CHANGE: the artifact source must publish manifest_v1.json in
place of manifest.json. Migration 000025 adds manifest_version to
case_flows, agency_workflow and task_records_v2. Flows started before it
have no version and fail their next task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 20, 2026
Artifacts are published as manifest_v1.json, manifest_v2.json, …
(core artifact.Set). A published manifest and its files never change; a
change is a new file and a new manifest. Each flow records the version
current when it started and resolves every template through it, so a
deploy never changes the templates of a running flow.

- Bootstrap loads every manifest and makes the highest current, or the
  one artifacts.manifestVersion pins. Unless
  artifacts.skipStartupValidation is set, it fetches every artifact of
  the current manifest and checks it parses and points only at listed
  artifacts: core's kinds (including render configs' section
  templates), task_config, static_data, and the templates step
  templates' notification extensions name. A broken manifest stops the
  deploy.
- caseflow records the current version on the case_flows row and starts
  the flow with workflow.VarManifestVersion. A retry of a recorded flow
  resumes on its recorded version before the current manifest is
  consulted, so it still starts when the current manifest dropped its
  template. Only a new flow is checked against the current manifest.
- Agency inject does the same on agency_workflow, and the officer case
  view reads each workflow's task_config from its own version.
- The definition handler resolves a split's branch workflows in the
  calling workflow's version, and the template provider (form
  rendering, notification templates) resolves through the version core
  puts on the context. Without one it fails rather than fall back to
  the current version.
- Static data is fetched by explicit version, from whichever manifest
  lists it.
- Consignment flow listings name each flow from its own version.
- The example configs and helm values read GitHub through the
  raw-content host: startup validation fetches every artifact, more
  than the unauthenticated REST limit allows. The replay harness skips
  startup validation, since every replay builds its own app.
- go.mod pins core to the head of OpenNSW/core#306 until it merges.

BREAKING CHANGE: the artifact source must publish manifest_v1.json in
place of manifest.json. Migration 000025 adds manifest_version to
case_flows, agency_workflow and task_records_v2. Flows started before it
have no version and fail their next task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lokewate
lokewate changed the base branch from feat/manifest-set to feat/manifest-version-engine October 20, 2026 15:15
@lokewate
lokewate force-pushed the feat/manifest-pinned-flows branch from ba293b8 to 09f462c Compare October 20, 2026 15:15
@lokewate lokewate changed the title feat(workflow,taskflow)!: resolve a flow's templates through its manifest version feat(taskflow)!: resolve a task's templates through its manifest version Oct 20, 2026
lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 20, 2026
Artifacts are published as manifest_v1.json, manifest_v2.json, …
(core artifact.Set). A published manifest and its files never change; a
change is a new file and a new manifest. Each flow records the version
current when it started and resolves every template through it, so a
deploy never changes the templates of a running flow.

- Bootstrap loads every manifest and makes the highest current, or the
  one artifacts.manifestVersion pins. Unless
  artifacts.skipStartupValidation is set, it fetches every artifact of
  the current manifest and checks it parses and points only at listed
  artifacts: core's kinds (including render configs' section
  templates), task_config, static_data, and the templates step
  templates' notification extensions name. A broken manifest stops the
  deploy.
- caseflow records the current version on the case_flows row and starts
  the flow with workflow.VarManifestVersion. A retry of a recorded flow
  resumes on its recorded version before the current manifest is
  consulted, so it still starts when the current manifest dropped its
  template. Only a new flow is checked against the current manifest.
- Agency inject does the same on agency_workflow, and the officer case
  view reads each workflow's task_config from its own version.
- The definition handler resolves a split's branch workflows in the
  calling workflow's version, and the template provider (form
  rendering, notification templates) resolves through the version core
  puts on the context. Without one it fails rather than fall back to
  the current version.
- Static data is fetched by explicit version, from whichever manifest
  lists it.
- Consignment flow listings name each flow from its own version.
- The example configs and helm values read GitHub through the
  raw-content host: startup validation fetches every artifact, more
  than the unauthenticated REST limit allows. The replay harness skips
  startup validation, since every replay builds its own app.
- go.mod pins core to the head of OpenNSW/core#306 until it merges.

BREAKING CHANGE: the artifact source must publish manifest_v1.json in
place of manifest.json. Migration 000025 adds manifest_version to
case_flows, agency_workflow and task_records_v2. Flows started before it
have no version and fail their next task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lokewate
lokewate force-pushed the feat/manifest-pinned-flows branch from 09f462c to 8a0f0a7 Compare October 23, 2026 09:22
@lokewate
lokewate marked this pull request as ready for review October 23, 2026 09:57
@lokewate
lokewate added this pull request to stack #347 October 23, 2026 11:38
@lokewate
lokewate force-pushed the feat/manifest-pinned-flows branch from 8a0f0a7 to 7f22780 Compare October 23, 2026 16:09
lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 23, 2026
…version

Pin core to the manifest versioning stack (OpenNSW/core#306): every
workflow carries the artifact manifest version it started on, and every
task, step, render and notification in it resolves its templates through
that version.

- TNSW case flows and agency injects start on the version current when
  they start, and load their definition from it. Recording the version on
  their rows, so a retried start keeps it, comes in the next change.
- Template providers, step permissions, file fields and the payload
  schema extension resolve through the task's version.
- task_records_v2 gets a manifest_version column (migration 000028).

BREAKING CHANGE: migration 000028 adds task_records_v2.manifest_version.
A task created before it has version 0 and fails its next step, so let
open tasks finish, or cancel them, before upgrading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lokewate added a commit to OpenNSW/nsw-srilanka that referenced this pull request Oct 24, 2026
…version

Pin core to the manifest versioning stack (OpenNSW/core#306): every
workflow carries the artifact manifest version it started on, and every
task, step, render and notification in it resolves its templates through
that version.

- TNSW case flows and agency injects start on the version current when
  they start, and load their definition from it. Recording the version on
  their rows, so a retried start keeps it, comes in the next change.
- Template providers, step permissions, file fields and the payload
  schema extension resolve through the task's version.
- task_records_v2 gets a manifest_version column (migration 000028).

BREAKING CHANGE: migration 000028 adds task_records_v2.manifest_version.
A task created before it has version 0 and fails its next step, so let
open tasks finish, or cancel them, before upgrading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mushrafmim
mushrafmim force-pushed the feat/manifest-pinned-flows branch from 7f22780 to 7786216 Compare October 24, 2026 19:20
Each task records the manifest version its parent workflow carries
(TaskPayload.ManifestVersion), so a task's steps, submissions, drafts
and renders all resolve their templates through the version the flow
started on.

- NewTaskManager takes an *artifact.Set.
- StartTask resolves the task template, its workflow and render config
  through the payload's version, stores it on the task record and seeds
  it into the task's own workflow.
- StartTaskStep, CompleteTaskStep and SaveDraft resolve the step
  template through the record's version, not the payload's.
  GetTaskRenderInfo and ZoneViewAssembler.Assemble do the same for
  rendering.
- Each puts the version on the context (artifact.WithManifestVersion),
  so plugins, extensions and the renderer's template provider resolve
  through it too.
- A task without a version fails before anything is written, with a
  non-retryable error returned unwrapped: falling back to the current
  version would change templates under a running task. A version this
  replica has not loaded stays retryable.
- TaskRecord.ManifestVersion is stored in column manifest_version.

The task and step start logs name the manifest version, so a step's
log says which templates it ran.

BREAKING CHANGE: NewTaskManager takes an *artifact.Set, hosts add
manifest_version INTEGER NOT NULL DEFAULT 0 to the task records table,
and workflows already running fail their next task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lokewate
lokewate force-pushed the feat/manifest-pinned-flows branch from 7786216 to 28fd51e Compare October 25, 2026 05:00
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