Skip to content

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

Draft
lokewate wants to merge 5 commits into
feat/manifest-setfrom
feat/manifest-pinned-flows
Draft

lokewate wants to merge 5 commits into
feat/manifest-setfrom
feat/manifest-pinned-flows

Conversation

@lokewate

@lokewate lokewate commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A host starts each top-level workflow with VarManifestVersion (_manifest_version) set to the current artifact manifest version. The engine carries it to every child workflow and reports it on every task activation, definition fetch and admin park. Each task records it, so a task's steps, submissions and renders all resolve their templates through the version the flow started on.

Stacked on #305: this PR targets feat/manifest-set, so its diff is this change alone. Review #305 first.

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

  • Engine.
    • ActivationRef and TaskPayload carry ManifestVersion.
    • FetchWorkflowDefinitionActivity takes the version as a second argument, and RegisterDefinitionHandler takes a DefinitionHandler(manifestVersion, templateID).
    • AdminParkPayload carries the version, so a park handler can show the parked node's template from the flow's own manifest.
    • Dynamic-split children get the variable explicitly. Batch and parallel children already copy the engine's underscore variables.
    • A whole number that came back through Temporal's JSON as a float64 is read as the integer. Anything that is not a positive whole number counts as no version.
  • Taskflow.
    • 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 and CompleteTaskStep resolve the step template through the record's version, not the payload's. They put the version on the context, so plugins, extensions and the renderer's template provider can resolve through it too.
    • GetTaskRenderInfo and ZoneViewAssembler.Assemble (a host's task view) do the same for rendering.
  • 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:
    • hosts set VarManifestVersion when they start a top-level workflow;
    • hosts add manifest_version INTEGER NOT NULL DEFAULT 0 to the task records table;
    • workflows already running fail their next task.

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 ./workflow/ ./taskflow/... ./artifact/.... The tests cover:

  • the version reaching every task activation in batch and parallel children, and the dynamic split's fetch and branches. A mutation check confirmed that dropping it from the split's children fails the test.
  • manifestVersion() value forms: int, float64, json.Number, negative, fractional and string;
  • the admin park payload;
  • a task that keeps its starting version after the current version moves, through start, step, submit and extensions;
  • missing-version refusal as an unwrapped non-retryable error, with nothing written and the step never claimed;
  • an unpublished version staying retryable;
  • 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

Related to #305

Screenshots/Demo

N/A

Additional Notes

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

🤖 Generated with Claude Code

…fest version

A host starts each top-level workflow with VarManifestVersion
("_manifest_version") set to the current artifact manifest version. The
engine carries it to every child workflow and reports it to the host on
every task activation and definition fetch, and each task records it,
so a deploy never changes the templates of a flow already running.

- The engine passes the version on ActivationRef (and so
  TaskPayload.ManifestVersion) and as a second argument to
  FetchWorkflowDefinitionActivity. Dynamic-split children get it
  explicitly; batch and parallel children already copy the engine's
  underscore variables. A whole number that came back through JSON as a
  float64 is read as the integer.
- RegisterDefinitionHandler takes a DefinitionHandler, which receives
  the manifest version with the template ID. AdminParkPayload carries
  the version too, so a park handler can show the parked node's
  template from the flow's own manifest.
- TaskManager 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 and CompleteTaskStep resolve the step
  template through the record's version, and put it on the context so
  plugins, extensions and the renderer's template provider can resolve
  through it too (artifact.ManifestVersionFrom).
- A task without a version fails with a non-retryable error before
  anything is written: falling back to the current version would change
  templates under it. A version this replica has not loaded stays
  retryable, since Set.Registry loads newer manifests on demand.
- TaskRecord gains ManifestVersion, stored in column manifest_version.

BREAKING CHANGE: NewTaskManager takes an *artifact.Set instead of an
*artifact.Registry, RegisterDefinitionHandler takes a DefinitionHandler,
and FetchWorkflowDefinitionActivity takes the manifest version. Hosts
must set VarManifestVersion when starting a top-level workflow and add
a manifest_version INTEGER NOT NULL DEFAULT 0 column to the task
records table. Workflows already running fail their next task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 12, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • 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 and others added 3 commits October 13, 2026 00:19
ZoneViewAssembler.Assemble, which a host's GET /api/v1/tasks/{id}
serves, rendered on the caller's context. A host template provider that
resolves through the task's manifest version found none there, so every
template-backed section failed. Assemble puts record.ManifestVersion on
the context, as GetTaskRenderInfo does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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