Skip to content

[epic] Finish the resource-versioning generalization — orchestrations, workflows, releases #883

Description

@arantespp

Tracks the remaining work from #877, whose layer 1 shipped in #880.

Why this exists separately

#877 scoped four steps and was auto-closed by #880's merge, which delivered only step 2. Rather than reopen it with one step struck through, this epic carries the unshipped remainder. #877 stays closed and accurate: the shared archive engine exists.

What already landed (#880):

  • packages/server/src/lib/resourceVersions.ts — the shared archive: makeVersionStore (write side) + makeVersionArchive (list/get/restore), with per-resource adapters for the config projection, applyConfig, mapVersion, and loadResource.
  • Agents and guardrails migrated onto it, behaviour-preserving for agents; guardrails levelled up to the same surface (id, label, created_by, list + restore, no-op dedup).
  • Layer 2's pure helpers extracted to releaseAssignment.ts (bucketForKey, assignReleaseVersion, parseActiveRelease).

What landed for step 1 (#892, closing #872):

  • OrchestrationVersion as the third adapter on the shared engine — orchestrationVersionSnapshot.ts (projection + write side) and orchestrationVersions.ts (archive adapters, list/get/restore).
  • orchestrationRunGraph.ts — a single resolution seam. All four entry points (driveQueuedRun, wakeRun, resumeOrchestrationRunExecution, redriveRun) previously did orch.nodes as OrchestrationNode[] independently, so fixing three of them would have looked correct in review and left the bug in the path a run actually parks in.
  • OrchestrationRun.orchestrationVersion records the pin, stamped at start-orchestration-run and exposed as orchestration_version.
  • Three IAM actions + three REST routes; MCP derives its tools from the spec automatically.

Agents, guardrails and now orchestrations are the three worked examples for everything below. Orchestrations is the closest model for #882: it is the first adapter where the consumption moment is not per-request.

Steps

What #892 makes cheaper for #882

The two are the same bug class, and #892 settled the design questions #882 would otherwise have to re-litigate:

Decision Answer, and why it should carry over
Store the pin as a version number or an FK to the version table? Number. It is what the wire exposes and what an audit reader cites, it matches Generation.agentVersion, and the archive row is reachable with no join.
Is name part of the versioned config? No, metadata. Bumping a version on a rename makes two version numbers denote the same state machine — the one thing the number must not do.
Guard edits while work is in flight? No. Pinning is the complete fix; a guard would remove the capability the design keeps, where the live columns become a draft for work started from now on.
Runs/tasks that predate the pin? Null pin → live definition, logged. Refusing to drive them would strand everything in flight across the deploy.
One resolution seam, or resolve at each read site? One seam. #882 has three read sites; the same "fix two of three and it still looks right" hazard applies.

The one place #882 differs: a workflow's consumption moment is task creation, not per-request, and #877's own table notes a workflow release would mean "the version new tasks are created into" with no mid-life reassignment.

A sequencing decision worth recording

#877 listed as its step 1 a stopgap for #872: a run-level JSONB snapshot on OrchestrationRun, justified as "small, ships fast, no new tables". That justification no longer held — it existed because the shared engine didn't. Building the stopgap would have been write-then-replace work, since #877's own step 3 already says the snapshot "becomes a version reference".

Recommendation was to skip the stopgap and take #872 straight onto OrchestrationVersion. That is what #892 did, and the same reasoning applies to #882: there is no stopgap worth building for it either.

Operational follow-ups

None of these is repo work — they are per-deployment calls and release bookkeeping, listed here so they are not lost.

  • Guardrail version backfill. feat(server): extract a shared resource-versioning engine #880 renamed guardrail_versions.documentconfig and rewrapped it as { document }. The repo has no migration mechanism — schema is managed by sync --alter — so existing archived guardrail documents are not carried across. New versions archive correctly from the first write. Where that history matters:
    UPDATE guardrail_versions SET config = jsonb_build_object('document', document);
  • Verify the version bump before the next release. feat(server): extract a shared resource-versioning engine #880's merge commit carries a BREAKING CHANGE: footer (the GuardrailVersion response moved documentconfig.document). Packages are at 0.19.2, and tools differ on whether a breaking change at 0.x bumps to 1.0.0 or 0.20.0. Confirm with pnpm lerna version --dry-run rather than assuming — .claude/rules/release.md does not cover the 0.x case. fix(orchestrations): pin a run to the graph it started on #892 is additive (fix:, no breaking footer), so it does not change this question — but it is unreleased alongside feat(server): extract a shared resource-versioning engine #880, so both land in the same release.
  • fix(orchestrations): pin a run to the graph it started on #892 needs no backfill. orchestration_versions is a new table and orchestrations.version / orchestration_runs.orchestration_version are new columns, all created by sync --alter. Existing orchestrations get version = 1 with no archived row and existing runs get a null pin — together exactly the documented degradation path, so in-flight runs continue on the live graph and the first graph edit after deploy archives normally. No index was renamed or removed. Recorded here so nobody goes looking for a migration.

Related: #877, #880, #892

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions