Skip to content

feat(artifact): validate every artifact a manifest lists - #320

Closed
lokewate wants to merge 3 commits into
feat/manifest-on-demandfrom
feat/manifest-validate
Closed

lokewate wants to merge 3 commits into
feat/manifest-on-demandfrom
feat/manifest-validate

Conversation

@lokewate

@lokewate lokewate commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Set.Validate fetches every artifact a manifest version lists and runs the Check for its kind. It reports every missing file, malformed artifact and dangling reference at once. A host runs it on the current version at startup, so a broken manifest stops the deploy instead of the first flow that reaches the broken artifact.

Stack

Manifest versioning, split into six 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): load newer manifests on demand #319 artifact: load newer manifests on demand
  3. feat(artifact): validate every artifact a manifest lists #320 artifact: validate every artifact a manifest lists ← this PR
  4. feat(artifact): add manifestcheck for artifacts repositories' CI #321 artifact: add manifestcheck for artifacts repositories' CI
  5. feat(workflow)!: carry the manifest version through every workflow #322 workflow: carry the manifest version through every workflow
  6. feat(taskflow)!: resolve a task's templates through its manifest version #306 taskflow: resolve a task's templates through its manifest version

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

  • Set.Validate(ctx, v, checks) and the Check/Checks/Ref types.
  • A Check for each core adapter, collected in corechecks.Checks():
    • a task template must name a listed workflow and render config;
    • a workflow's TASK nodes must name a listed task or step template;
    • a split that runs one template for every branch must name a listed workflow;
    • a render config's sections must name listed templates.
  • adapter/kinds holds the core kind constants, so adapters that point at each other share them. Each adapter's Kind is now an alias of its constant; the values are unchanged.
  • A host adds checks for its own kinds to the map corechecks.Checks() returns.

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 ./artifact/.... The tests cover Validate passing, reporting several problems together and refusing an unknown version, and each adapter's Check, including a DIFFERENT_TEMPLATES split being skipped and render-config sections.

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

Hosts: OpenNSW/nsw-srilanka#660. Artifacts: OpenNSW/one-trade-artifacts#156.

Screenshots/Demo

N/A

Additional Notes

  • Reference checks cover what core can see. Plugin and extension properties can also name artifacts. Only the host knows those shapes, so the host adds checks: feat(artifacts): load the artifact manifests as a set nsw-srilanka#660 checks notification template_ids.
  • A TASK node may name either a task template or a step template. A single workflow file does not say whether it is a case's top-level workflow or a task's own workflow.

🤖 Generated with Claude Code

lokewate and others added 3 commits October 20, 2026 20:41
An artifact source publishes manifest_v1.json, manifest_v2.json, … and
artifact.Set keeps one Registry per version. A published manifest and
the files it lists never change: a change is a new file plus a new
manifest. A flow records Set.Current() when it starts and resolves
every artifact through Set.Registry(ctx, version), so a deploy never
changes the templates of a running flow.

- LoadSet probes manifest_v1.json upward until the loader reports one
  missing; any other error fails it. The highest version is current
  unless WithPinnedVersion names an older one, and with a pin only
  manifests up to it are read.
- RegisterFromConfig rejects two rows with the same id, kind and
  version. LoadSet rejects an explicitly versioned row whose path
  differs between manifests, and GetExplicit serves such a row outside
  any flow.
- Latest returns a manifest's unversioned row even when the manifest
  also lists explicit versions.

BREAKING CHANGE: ManifestFilename gives way to ManifestPath(v),
LoadManifest takes a version, and workflowdef.LoadVersion is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replicas load their sets at different times, so a flow pinned by a
replica that started after a manifest was published can run its next
step on a replica that started before it. Set.Registry now loads a
version above the highest it has, and GetExplicit probes for a newer
manifest when no loaded one lists the version asked for.

A probe fetches only up to the version asked for, so a broken later
manifest cannot fail it, and holds no lock that lookups of loaded
versions wait on. After a probe that fails or finds nothing it waits
10s before probing again, so retries of a pinned flow do not each cost
a loader round trip. Current does not move until the set is reloaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Set.Validate fetches every artifact a manifest version lists and runs
the Check for its kind, reporting every missing file, malformed
artifact and dangling reference at once. Run on the current version at
startup, it stops a deploy on a broken manifest instead of the first
flow that reaches the broken artifact.

Each core adapter gains a Check, collected in corechecks.Checks():

- a task template must name a listed workflow and render config;
- a workflow's TASK nodes must name a listed task or step template;
- a split that runs one template for every branch must name a listed
  workflow;
- a render config's sections must name listed templates.

adapter/kinds holds the core kind constants, so adapters that point at
each other share them.

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

coderabbitai Bot commented Oct 20, 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

Copy link
Copy Markdown
Contributor Author

Parked: pinning flows to a manifest version doesn't need this, so it's out of the manifest versioning stack (#305 → #346 → #319 → #322 → #306) to keep each PR small. Validation can come back with the release job (OpenNSW/one-trade-artifacts#243); the branch is kept.

@lokewate lokewate closed this Oct 23, 2026
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