feat(flow): approval and event steps land on the one engine, with the steps-to-graph migration - #1822
Merged
Merged
Conversation
… the steps-to-graph migration integriq-flow-nodes is implemented in the tree; this closes its open ends and starts retire-integriq-flow-schema in dependency order. Task 1 (the missing nodes): - openconnector.approval-request: persists a pending approval_request (the HITL system of record, mirrored onto shared tasks) and suspends the run the way openregister.await-signal does, with a heartbeat. Approved resumes with the decision on every item; rejected routes (or fails under failOnReject); expired fails closed. The heartbeat re-reads the record, so a lost signal costs one heartbeat, never the flow. - openconnector.event-emit: one CloudEvent per item through the existing EventService pipeline. Confirmed first that openregister ships no emitter. - ApprovalsController approves/rejects engine-run requests and delivers the decision through EngineSignalService, a guarded FlowRunSignalService:: signalAs wrapper (BrokeredCallService idiom). The record resolves first; delivery is best-effort by design. - approval_request gains engineRunUuid/signalNodeId/question in the register fragment, because OpenRegister silently drops undeclared properties. Task 2 (the steps-to-graph migration): - FlowStepsToGraphTranslator: pure steps[] -> nodes/edges. Step order becomes the node id verbatim so branch targets survive; branch conditions ride the edges; duplicate orders and inexpressible features are refused with reasons, never approximated. - FlowGraphMigrationService + repair step + occ integriq:flow:steps-to-graph: additive (steps stays as the rollback shape), idempotent (nodes present = skipped), dry run by default, --rollback restores the steps-only shape. - The flow schema declares nodes/edges so the written graph is not dropped. Docs (docs/features/flow-nodes.md), Dutch strings for both nodes, and the tasks.md state of both changes updated to match what is actually true, including why flow-nodes Task 4 stays open (the demo seed was never wired; lib/sources.seed.json is the PDOK file and has no importer).
…andle is not a relation The three verbatim flow-engine stubs carried OpenRegister's own @SPEC paths, which resolve to nothing in this repo (gate-46); stripped, like every earlier stub. engineRunUuid dropped its format:uuid so it does not read as a relation-shaped property: an engine run lives in OpenRegister's flow_runs table, not as an object any schema here could $ref (gate-54).
rubenvdlinde
requested review from
Rem-Dam,
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48 and
rjzondervan
as code owners
September 2, 2026 14:36
Contributor
Quality Report — ConductionNL/integriq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ❌ | ||||
| composer | ✅ | ✅ 141/141 | |||
| npm | ✅ | ✅ 537/537 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-02 14:41 UTC
Download the full PDF report from the workflow artifacts.
…the new orchestration code Three CI findings, three remedies: - check:l10n-js: l10n/nl.js is generated from nl.json by npm run l10n:build, and the hand-inserted keys sat in a different order than the generator writes. Regenerated. - gate-101 (demo-data-coverage): touching the flow schema means the flow schema owes three valid demo objects, and the mock register had none. Three added by hand: a linear pipeline, a gated publish with an approval step, and a branched flow carrying the migrated nodes/edges graph beside its steps. Deliberately NOT the generator's full rewrite, which would have replaced the whole curated mock register with 165 generated objects. - coverage guard: 768 new statements at 74 percent read as a drop. New unit tests for the occ command (all four paths), the repair step (counts, refusal logging, the never-throws contract), EngineSignalService's guarded branch, and both nodes' config forms.
Contributor
Quality Report — ConductionNL/integriq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 141/141 | |||
| npm | ✅ | ✅ 537/537 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-09-02 18:05 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Closes out
integriq-flow-nodesand startsretire-integriq-flow-schemain dependency order, converging integriq's app-local flow machinery onto the OpenRegister engine.The two missing nodes (retire task 1)
openconnector.approval-request: persists a pendingapproval_request(the HITL system of record, mirrored onto shared tasks per feat(approvals): mirror every HITL approval into the shared OpenRegister task service #1812) and suspends the run the wayopenregister.await-signaldoes, with a heartbeat. Approved resumes with the decision on every item. Rejected routes by default, or fails the run underfailOnReject. Expired fails closed, always. The heartbeat re-reads the record, so a lost signal costs one heartbeat, never the flow.openconnector.event-emit: one CloudEvent per item through the existingEventServicepipeline. Confirmed first that openregister ships no emitter (its send nodes address people, not systems), so the change grew by this node exactly as its gate predicted.ApprovalsControllerresolves engine-run approvals and delivers the decision through the newEngineSignalService, a class_exists-guardedFlowRunSignalService::signalAs()wrapper (theBrokeredCallServiceidiom). The record resolves first; delivery is best-effort because the node's heartbeat treats the record as the authority.approval_requestgainsengineRunUuid/signalNodeId/questionin the register fragment, because OpenRegister silently drops undeclared properties.The steps-to-graph migration (retire task 2)
FlowStepsToGraphTranslator: puresteps[]tonodes/edges. Steporderbecomes the node id verbatim, so branch targets survive without renumbering. Branch JsonLogic conditions ride the edges, matching the engine's edge-condition routing. Duplicate orders and inexpressible features are refused with one reason each, never approximated.FlowGraphMigrationService+ repair step +occ integriq:flow:steps-to-graph: additive (stepsstays as the rollback shape, the two engines dual-run through the window), idempotent (an object carryingnodesis skipped), dry run by default,--rollback --applyrestores the steps-only shape and refuses a flow whose graph is its only shape.flowschema declaresnodes/edgesso the written graph is not silently dropped on save.Bookkeeping
docs/features/flow-nodes.mdplus Dutch strings for both nodes.tasks.mdupdated to what is actually true. Flow-nodes task 4 stays open with a note: the demo seed was never wired (lib/sources.seed.jsonis the PDOK file and has no importer), so the live end-to-end run has not happened.flowschema row and zero live flow objects.Deliberately NOT in this PR
Retire tasks 3 to 5: deleting
FlowRunnerService, movingFlowsControlleroff the integriq register,RuleToFlowGeneratorgraph emission, removing theflowschema, the prune, and the gate-106 baseline regeneration. Those only start once migrated flows have run in anger on the engine; the change is staged for multi-PR delivery.Verification
composer lint,phpcs,phpmd,psalm,phpstan,check:routes,check:no-legacy-types: all green, run individually.--scope-to-diffagainstorigin/development: green except gate-53 locally, which is the composer-locked v1.10.0 schema not knowing theflowpage type this repo already ships; the gates@main schema CI actually runs allows it (verified against the raw file).npm run lintandnpm run format: green.🤖 Generated with Claude Code