refactor(flow): drop the forked flow frontend and the model it described - #606
Merged
rubenvdlinde merged 4 commits intoAug 28, 2026
Merged
Conversation
Hermiq had a parallel flow authoring frontend: its own canvas (FlowBuilder), sidebar, list, editor store, branch store, API client and four modals - 8,159 lines beside the shared one. It imported CnGraphCanvas, the raw drawing widget, but not CnFlowDetail, so it reused the geometry and reimplemented everything above it. src/store/store.js already quoted the rule this breaks: 'No other app SHALL own a flow store, a flow controller, or a flow execution service' (flow-storage/ spec.md). The comment was written to explain why there is no agentflow OBJECT store, while src/api/flows.js and src/store/flowEditor.js sat next to it doing exactly what the sentence forbids. The justification for the fork was a model that no longer exists. The manifest said 'a node is a PLACE and carries no config, an edge is the STEP that runs'. or-flow-action-nodes INVERTED that: a node is the action, holding step type and config, and an edge is sequence. Hermiq's own backend already agrees - HermiqAgentNode::execute(array $items, array $config, array $context), whose docblock calls $config 'The step configuration'. The frontend kept describing the pre-inversion world, and had already drifted from it once: the builder read the old shape and rendered 'No step type' onto all 16 lines of a healthy flow while the engine ran it correctly. So the fork is removed rather than repaired: FlowIndex, FlowIndexLegacyRedirect custom -> index + config.entitySource FlowDetail, FlowDetailLegacyRedirect custom -> flow The sidebar is now the SHARED CnFlowSidebar, registered from the library, so the only flow component this app still registers is the one the manifest names. What hermiq keeps is the supported extension point, which it was already using correctly: lib/Flow/HermiqAgentNode, HermiqWorkloadNode and HermiqWorkloadCollectNode, registered on OpenRegister's RegisterFlowNodesEvent. The app provides nodes; it does not provide an editor. Tests: flow-branches and flow-trigger-count evaluated the fork's own source files off disk and have no subject left. flow-builder-dialect and flow-canvas asserted the shared canvas's rendering, which is nc-vue's to prove. The survivors keep what is hermiq's: flow-seed (install-time seeding, via the OpenRegister API), flow-execution (the engine runs a flow, and a save must not disarm it - retargeted to the shared toolbar), and manifest-pages. Two comments in dashboard-and-agents each described the model as it had just stopped being; both are corrected, and the fixme there is re-explained rather than quietly greened - its cause moved but it still cannot pass, because a named source's row actions MERGE with the built-ins and so cannot express 'exactly one action'. Verified: check:manifest and check:manifest-v2 PASS against schema 2.26.0, webpack build exits 0 (which is what proves no dangling imports survive), eslint clean on both changed source files, and unchanged error counts on the three touched specs (3/5/6, all pre-existing, measured in place).
Two things the branch broke and local checks did not catch, because I ran
check:manifest, eslint and the build rather than the command CI runs.
1. package.json's check:specs still chained check:flow-branches and
check:flow-trigger-count. Their spec files are deleted, so the chain died
with MODULE_NOT_FOUND before reaching anything real.
2. tests/registry.spec.js evaluates registry.js by rewriting its imports into
stubs, and the pattern only matched DEFAULT imports:
/import\s+(\w+)\s+from\s+.../
\w+ cannot match '{ CnFlowSidebar }', so the first NAMED import in the file
survived the rewrite and vm.runInNewContext rejected the whole file with
'Cannot use import statement outside a module' — a parse error naming
neither the import nor the line. There were no named imports until the
registry started importing a shared component instead of declaring its own.
The evaluator now stubs both forms.
Verified by running the exact CI command, npm run check:specs, which now exits
0. That is the check that would have caught both before the push.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ❌ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 742/742 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-27 21:02 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 742/742 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-27 22:26 UTC
Download the full PDF report from the workflow artifacts.
The stub itself is load-bearing and stays: registry.js now imports a SHARED
component (`import { CnFlowSidebar } from '@conduction/nextcloud-vue'`) instead
of declaring its own, and this spec's rewriter only handled DEFAULT imports.
A named import survived the rewrite intact and vm.runInNewContext rejected the
whole file with 'Cannot use import statement outside a module' — a parse error
naming neither the import nor the line.
Verified by mutation: deleting the named-import branch reproduces that exact
error, and restoring it is byte-identical.
Only the formatting was wrong, which is what `prettier --check` caught.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 742/742 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-28 05:15 UTC
Download the full PDF report from the workflow artifacts.
This PR replaces hermiq's forked flow frontend with the shared `index` and `flow` page types, and those need 2.21.0: 2.20 declares a named index source's columns, create button and row actions without reading them. The LOCK is the part that matters — CI installs with `npm ci`, so the caret alone changes nothing about what actually installs. 2.21.0 is also what makes the shared CnFlowSidebar this PR registers resolve to the same version the canvas comes from.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 742/742 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-28 08:08 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 742/742 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-28 12:01 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.
Removes hermiq's parallel flow-authoring frontend — 9,317 lines deleted, 81 added — and moves its
four flow pages onto the shared
indexandflowpage types.The fork
src/views/FlowBuilder.vuesrc/store/flowEditor.jssrc/views/FlowSidebar.vuesrc/modals/Flow/*.vuesrc/api/flows.js,flowBranches.js,FlowIndex.vue,RunFlowDialog.vueIt imported
CnGraphCanvas— the raw drawing widget — but notCnFlowDetail, so it reused thegeometry and reimplemented everything above it, including its own API client and store.
store.jsalready quoted the rule this breaksThat comment was written to explain why there is no
agentflowobject store.src/api/flows.jsand
src/store/flowEditor.jssat directly beside it, doing what the sentence forbids. The rule wassatisfied on the backend and broken on the frontend, in the same file that cited it.
The justification was a model that no longer exists
The manifest said "a node is a PLACE and carries no config, an edge is the STEP that runs".
or-flow-action-nodesinverted that: a node is the action, carrying step type and config; an edgeis sequence.
hermiq's own backend already agrees —
HermiqAgentNode::execute(array $items, array $config, array $context), whose docblock calls$config"The step configuration". Only the frontend stilldescribed the pre-inversion world, and it had already drifted from it once: the builder read the old
shape and rendered "No step type" onto all 16 lines of a healthy flow while the engine ran it
correctly. That whole failure mode exists only because the app kept a second implementation.
What hermiq keeps
The supported extension point, which it was already using correctly:
The app provides nodes. It does not provide an editor. The sidebar is now the shared
CnFlowSidebar, registered from the library, so the only flow component this app registers is theone the manifest names.
Tests
Removed, because their subject is gone:
flow-branchesandflow-trigger-count— evaluated the fork's own source files off disk by pathflow-builder-dialectandflow-canvas— asserted the shared canvas's rendering, which is@conduction/nextcloud-vue's to proveKept, because they are hermiq's:
flow-seed— install-time seeding, through the OpenRegister API (untouched)flow-execution— the engine runs a flow, and a save must not disarm it (retargeted from.flow-builder__verbsto the shared.cn-flow-detail__toolbar)manifest-pages— the four flow routes leave the component map, since they no longer name oneTwo comments that were wrong when written
dashboard-and-agents.spec.tscarried two successive comments each describing the flow model asit had just stopped being — the second explaining the removal of a test for getting it backwards,
while itself stating the pre-inversion model. Both corrected.
Its
test.fixmeis re-explained rather than quietly greened. Its cause moved: the index is atype:"index"again, so that reason is gone. It still cannot pass, for a narrower reason — the namedsource declares exactly one row action (Edit), but a source's
rowActionsmerge with thebuilt-ins rather than replacing them, so "these and no others" is currently inexpressible. Suppressing
a built-in is the
show*Actiontoggles' job.Verification
check:manifestandcheck:manifest-v2— PASS, 0 errors, against schema 2.26.0eleven deleted files survives anywhere
place — linting copies elsewhere gives a different answer and is not a valid control
Blocked on
@conduction/nextcloud-vue2.21 (#810,#818) — 2.20 declares a named source's columns andcreate button without reading them
ConductionNL/.github#602— hydra-gates' vendored schema is still 2.25.0, which has neitherflownor
entitySourceE2E is the real proof for the editor swap and must be green before this merges.