fix: publish schedule mirrors for the version pin, and stop the chat probe answering 503 when unconfigured - #754
Merged
rubenvdlinde merged 3 commits intoSep 2, 2026
Conversation
OpenRegister's FlowRunVersionPin refuses every scheduled dispatch of a flow with no published version, so a mirror the bridge only inserted was a clock that never ticked: the schedule left the local dispatcher and gained nothing back. Reproduced twice on the dual-app rig; one manual publish made the whole chain work, which is exactly the fix a machine-managed mirror must not need. The bridge now publishes server-side through FlowVersionService, the class the publish endpoint itself uses, resolved lazily and guarded on the class existing. Create: insert, publish, mark, so a publish failure deletes the flow and the schedule keeps its local clock. Refresh: draft, update, publish, because the engine runs the pinned version, not the flow row, so a changed cadence must land as a new published version. The undrifted branch publishes any unpublished head, healing pre-publish mirrors on the next sync pass. The publish and shape concerns move to ScheduleFlowPublisher and ScheduleMirrorDefinition, keeping the bridge under the complexity ceiling. The new stubs model the pin's contract, so a bridge that skips publishing fails its tests instead of running unpublished flows.
GET /api/chat/health answered 503 {status:no_provider} on an instance
without an LLM provider. That is a configuration state, not an outage, and
the 5xx tripped every co-installed app's strict no-5xx e2e guard (it broke
dossiq's KPI spec on the rig). The probe now answers 200
{status:unconfigured, configured:false, capabilities:[]} so a consumer
decides on the body; 5xx is reserved for the app itself being broken, so a
failing config read stays 503 {status:config_error}.
The three e2e carve-outs that excused the designed 503 are gone: with this
contract a 503 from the probe means the app is broken and must fail the
sweep like any other 5xx. The sibling /api/health keeps its 503, which
reports a genuinely dead OpenRegister dependency, not a missing setting.
Known follow-up: nextcloud-vue's CnAiCompanion renders on any 2xx, so an
unconfigured instance now shows the launcher; the widget should branch on
the capabilities list.
rubenvdlinde
requested review from
Rem-Dam,
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48 and
rjzondervan
as code owners
September 2, 2026 18:15
… birth state The real Flow entity is born draft, so the bridge publishes directly; the standalone stub is born blank, so the bridge drafts first. The order assertion encoded the stub's shape and failed on the server matrix. Pin what matters on both: publish happens, and the marker comes after it.
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 | ✅ | ✅ 730/730 | |||
| 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 19:05 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 | ✅ | ✅ 730/730 | |||
| 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 19:19 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.
Two defects from the live rig proof.
Schedule mirrors never published their flow (HIGH)
OpenRegister's
FlowRunVersionPinrefuses every scheduled dispatch of a flow with no published version.ScheduleFlowBridge::mirrorcreated the engine flow and stopped there, so every mirrored schedule left the local dispatcher and gained a clock that never ticked. Reproduced twice on the rig; after one manualPOST /api/flows/{id}/publishthe whole dual-app chain worked (trigger tick, queued run,hermiq.schedule-dispatch,runNow(), run-audit row). A machine-managed mirror must not need that hand.The bridge now publishes server-side through
FlowVersionService, the same class the publish endpoint uses, resolved lazily from the container and guarded on the class existing. The chosen semantics:New unit tests assert the flow's lifecycle after every path, and the version-service fake models the pin's contract: only a publish call can move a flow to published, so a bridge that skips publishing fails the suite (verified: 4 failures against the unfixed bridge). The publish and shape concerns moved into
ScheduleFlowPublisherandScheduleMirrorDefinitionto keep the bridge under the PHPMD complexity ceiling. The openspec change (schedules-onto-engine-triggers) carries the semantics in design.md, tasks.md and two new spec scenarios./api/chat/health answered 503 when unconfigured (MEDIUM)
An instance without an LLM provider is healthy, not broken, and the designed 503 tripped every co-installed app's strict no-5xx e2e guard (it broke dossiq's KPI spec on the rig). The probe now answers 200
{status: unconfigured, configured: false, capabilities: []}; 5xx stays reserved for the app itself being broken, so a failing config read remains 503config_error. The three e2e carve-outs that excused the old 503 are removed, because under the new contract a 503 from this route is a real fault. The sibling/api/healthkeeps its 503: a dead OpenRegister dependency is an outage, not a setting.Follow-up for nextcloud-vue:
CnAiCompanionrenders on any 2xx, so an unconfigured instance now shows the launcher; the widget should branch on thecapabilitieslist instead.Verification
lint,phpcs,phpmd(per subdir),psalm,phpstan: cleanphpunit: 1,879 tests green (dev baseline 1,875)--scope-to-diffvs origin/development: 42 of 42 applicable gates greenplaywright test --liststill collects🤖 Generated with Claude Code