Skip to content

Add workflow drift safety (orphaned-state handling)#16

Merged
dereuromark merged 7 commits into
masterfrom
feature/versioning-drift-safety
May 22, 2026
Merged

Add workflow drift safety (orphaned-state handling)#16
dereuromark merged 7 commits into
masterfrom
feature/versioning-drift-safety

Conversation

@dereuromark

@dereuromark dereuromark commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the plugin safe when a workflow definition changes while records already exist — the "100 production records in different states, then we change the workflow" problem. It works out of the box: no configuration, no schema change, nothing to enable.

Graceful degradation (always on)

Records left in a state that no longer exists ("orphaned") no longer crash reads, display, or the admin UI — they render as a neutral "unknown" state, and transitioning one returns a clear blocked result instead of throwing.

  • State::unknown() / isUnknown(), Definition::findState() / resolveState()
  • engine can() / apply() / getAvailableTransitions(), WorkflowBehavior::isFinal() / hasFlag(), and WorkflowHelper::stateBadge() / getStateColor() all degrade instead of throwing
  • getState() stays strict for genuine misconfiguration (unknown transition targets)

Detection & remediation (zero-config)

Orphaned records are found by comparing each record's stored state to the current definition — no version tracking required.

  • the admin Orphans view (/admin/workflow/orphans, with a sidebar count badge) and workflow validate --check-data list records in undefined states
  • workflow migrate --map old:new moves them forward headlessly: refuses to run if any orphaned state is unmapped, runs atomically (rolls back if an audit-log write fails), logs each move, and resyncs the target state's timeouts
bin/cake workflow validate order --check-data
bin/cake workflow migrate order --map legacy:pending
bin/cake workflow migrate order --map legacy:pending --dry-run

On versions

A definition keeps its version number (already recorded on every transition and shown by workflow show); records carry no per-record version, and there is nothing to back-fill. null/absent simply means the baseline.

Out of scope (documented future work)

  • Per-record version stamping / stale-but-valid detection
  • Running multiple definition versions concurrently

Docs

README "Drift Safety" section, new guide/versioning (Drift Safety) page wired into the sidebar, CLI reference, and the comparison page.

Make the plugin safe when a workflow definition changes while records
are in flight.

Graceful degradation (always on, no config):
- Orphaned states (left by a definition change) no longer throw on read
  or display. Add State::unknown()/isUnknown() and Definition::findState()/
  resolveState(); the engine, behavior and helper degrade to a neutral
  state, and transitions on orphaned records return a clear blocked result.

Opt-in version stamping (off by default):
- New behavior config 'versioning' + 'versionField' stamps the definition
  hash onto a nullable entity column on transition and on new-entity save,
  skipping cleanly when the column is absent.
- getVersionStamp()/isStale() expose drift per record.
- getVersionHash() now fingerprints the full definition (version number,
  state and transition attributes), not just the state graph.

Tooling:
- workflow stamp backfills version stamps (--all, --dry-run).
- workflow migrate re-stamps stale records and maps orphaned records
  forward (--map), refusing on unmapped orphans, running atomically, and
  resyncing state timeouts for migrated records.
- workflow validate --check-data reports unversioned/stale records when
  versioning is enabled on the table.
- stamp/migrate default the version column to the behavior configuration.

Docs: README section, guide/versioning page (with sidebar entry), CLI
reference, and comparison page.
Copilot AI review requested due to automatic review settings May 22, 2026 11:24
@dereuromark dereuromark added documentation Improvements or additions to documentation enhancement New feature or request labels May 22, 2026
@codecov-commenter

codecov-commenter commented May 22, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 90.25974% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.96%. Comparing base (2b1c375) to head (dc5b237).

Files with missing lines Patch % Lines
src/Command/WorkflowMigrateCommand.php 88.37% 15 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@             Coverage Diff              @@
##             master      #16      +/-   ##
============================================
+ Coverage     72.37%   72.96%   +0.59%     
- Complexity     1148     1188      +40     
============================================
  Files            62       63       +1     
  Lines          4090     4236     +146     
============================================
+ Hits           2960     3091     +131     
- Misses         1130     1145      +15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds workflow “drift safety” so records don’t crash the engine/UI when their stored state no longer exists after a workflow definition change, and introduces opt-in per-record version stamping plus CLI tooling to detect/backfill/migrate drift.

Changes:

  • Add State::unknown() plus Definition::findState() / resolveState() and update engine/behavior/helper codepaths to degrade gracefully for orphaned states.
  • Add opt-in version stamping (versioning, versionField) in WorkflowBehavior, including new-entity stamping and stale detection helpers.
  • Add CLI tooling (workflow stamp, workflow migrate) and extend workflow validate --check-data to report version drift; update docs and tests accordingly.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/TestCase/View/Helper/WorkflowHelperTest.php Adds coverage for helper rendering/color behavior on orphaned states.
tests/TestCase/Model/Behavior/WorkflowBehaviorTest.php Adds tests for orphaned-state safety and version stamping/staleness APIs.
tests/TestCase/Model/Behavior/WorkflowBehaviorPersistenceTest.php Extends persistence schema to include version column.
tests/TestCase/Engine/StateMachineEngineTest.php Adds tests ensuring engine blocks/returns empty transitions for orphaned states.
tests/TestCase/Engine/Definition/StateTest.php Adds tests for the new State::unknown() and isUnknown().
tests/TestCase/Engine/Definition/DefinitionTest.php Adds tests for findState/resolveState and stronger version-hash sensitivity.
tests/TestCase/DatabaseTestCase.php Extends shared test schema to include versioning columns.
tests/TestCase/Command/WorkflowValidateCommandTest.php Adds integration tests for drift reporting gated by versioning config.
tests/TestCase/Command/WorkflowStampCommandTest.php New tests for backfill stamping behavior and version-field resolution.
tests/TestCase/Command/WorkflowMigrateCommandTest.php New tests for stale restamping, orphan mapping, timeout resync, and rollback on audit failure.
src/WorkflowPlugin.php Registers the new workflow stamp and workflow migrate commands.
src/View/Helper/WorkflowHelper.php Switches state lookups to resolveState() for orphan-safe rendering.
src/Model/Behavior/WorkflowBehavior.php Adds versioning config, stamping on create/transition, and getVersionStamp()/isStale().
src/Engine/StateMachineEngine.php Makes can/apply/getAvailableTransitions orphan-safe with clear blocked result.
src/Engine/Definition/State.php Adds unknown-state support (unknown flag, factory, accessor).
src/Engine/Definition/Definition.php Adds non-throwing state resolution and expands version hash to include structural attributes.
src/Command/WorkflowValidateCommand.php Extends --check-data validation to optionally report version drift when enabled on the table.
src/Command/WorkflowStampCommand.php New command to backfill version stamps onto existing records.
src/Command/WorkflowMigrateCommand.php New command to re-stamp stale records and map orphaned states forward with audit logging/timeout resync.
src/Command/VersionFieldOptionTrait.php Shared option resolution for version-field across commands.
README.md Documents drift safety, versioning enablement, and new CLI commands.
docs/reference/comparison.md Updates feature comparison to include drift safety/versioning/migration tooling.
docs/reference/cli.md Documents validate --check-data drift reporting and the new stamp/migrate commands.
docs/guide/versioning.md New guide page describing drift, version stamping, and migration workflow.
docs/.vitepress/config.ts Adds the versioning guide page to the sidebar.
.gitignore Ignores a docs working directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Command/WorkflowMigrateCommand.php Outdated
Comment thread src/Command/WorkflowMigrateCommand.php
dereuromark and others added 2 commits May 22, 2026 13:31
… stamp

- workflow migrate logs the human workflow version in
  workflow_transitions.workflow_version, consistent with normal transitions
  (the entity column keeps the structural hash for drift detection).
- New entities are always stamped with the current version hash, overwriting
  any client-supplied value, since the column is plugin-managed metadata.
- workflow migrate now logs and syncs by $entity->get('id'), consistent with
  TransitionLogger and TimeoutScheduler (previously used the table primary key,
  which diverged and would break non-id keys).
- Select only the id column when migrating a state group to keep memory bounded;
  the batch still runs in a single transaction for atomic all-or-nothing.
Copilot AI review requested due to automatic review settings May 22, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 1 comment.

Comment thread src/Command/WorkflowMigrateCommand.php Outdated
Per review, drop the opt-in per-record version stamping and its tooling in favor
of what works out of the box with no column or config:

- Keep graceful degradation: orphaned states never crash reads/display
  (State::unknown, Definition::findState/resolveState, engine/behavior/helper).
- Keep orphan remediation: workflow migrate --map moves orphaned records forward
  (atomic, logs each move, resyncs timeouts), alongside the existing admin
  Orphans view and workflow validate --check-data.
- Remove versioning/versionField config, version stamping, isStale/
  getVersionStamp, workflow stamp, VersionFieldOptionTrait, validate drift
  reporting, and the full-definition getVersionHash change.

Version stays a definition-level number (already logged); records carry no
per-record version. Docs updated accordingly.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 1 comment.

Comment thread docs/reference/comparison.md
@dereuromark dereuromark changed the title Add workflow versioning and drift safety Add workflow drift safety (orphaned-state handling) May 22, 2026
@dereuromark dereuromark merged commit 582e148 into master May 22, 2026
12 checks passed
@dereuromark dereuromark deleted the feature/versioning-drift-safety branch May 22, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants