feat(hydra-gates): gate 94 — retired-git-host-metadata - #546
Merged
Conversation
ConductionNL moved onto Codeberg 2026-05-29 and moved back off 2026-07-23. The git layer completed — verified 2026-08-22, zero codeberg remotes fleet-wide. The shipped metadata did not. 23 apps still declare <bugs>, <website>, <repository> and <screenshot> URLs pointing at codeberg.org/Conduction/<app> in appinfo/info.xml. That file is published to the Nextcloud app store, so this is user-visible: <bugs> sends a bug reporter to a tracker nobody reads, and <screenshot> 404s in the listing. Gate 94 is full-tree, deliberately NOT diff-scoped — the same reasoning gates 84 and 93 give, but sharper here. The drift this gate exists to catch is already in the tree, put there by a migration that touched every repo at once. A diff-scoped version would report nothing on the ~99% of PRs that never open appinfo/info.xml, i.e. it would be blind to 100% of the debt it was written for. Scope is shipped metadata only: appinfo/info.xml (root, since an NC app ships exactly one) plus package.json / composer.json, and within those only the fields carrying a URL a user follows. Docs, changelogs, specs, learnings and archived openspec changes are out of scope — they record what was true when written, and a gate noisy enough to be switched off catches nothing. custom_apps/ and friends are skipped: a dev environment drops checkouts of other apps inside a repo, and walking them made the gate report openregister as having 69 findings, 63 of which belonged to seven other apps. A gate that blames a repo for a neighbour's metadata teaches people to ignore it. Verified: procest → FAIL (9), nextcloud-vue → PASS, hydra → SKIP(na).
This was referenced Aug 21, 2026
rubenvdlinde
added a commit
that referenced
this pull request
Aug 22, 2026
Gate 94 landed in #546 without a fixture bundle, and main's "Package invariants" job is red because of it: the coverage ratchet in test_gate_acceptance_matrix.sh is a hard failure for a gate declared in the runner with neither a bundle nor an UNCOVERED.md row — deliberately, so a gate cannot land untested in silence. This is that bundle. The two arms differ ONLY in the host the shipped URLs point at, and planted/ reproduces the fleet's actual 2026-08-22 state: <bugs>, <website>, <repository> and <screenshot> all still on codeberg.org a month after the last codeberg remote was removed. Two shapes are pinned on purpose: - Both arms carry a prose mention of "codeberg" in <description>. Gate 94 is scoped to URL-bearing FIELDS, never to the file's prose — a scanner that grepped the whole file would fail the clean/ arm on that line, and a gate that flagged every historical mention would be noisy enough to switch off. - Both arms carry an identical <screenshot> PATH, so a checker keyed on the path rather than the host would misgrade the pair. The subject substring is `codeberg.org`, not a field name: the finding is "this URL points at a retired host" and the URL is what a human needs to see. A gate reporting only a count would satisfy a nonzero-exit check for free while naming nothing. Verified locally: acceptance matrix 181 passed / 0 failed, gate 94 now in the covered set (67 of 74 gates fixtured). Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
pushed a commit
to ConductionNL/openregister
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, added in ConductionNL/.github#546) flags six shipped URLs here: website, bugs, repository and three screenshots, all still pointing at codeberg.org. GitHub is the only host; Codeberg is retired. The screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and all three were checked rather than assumed — HTTP 200 each. A green gate with dead image URLs would be worse than the finding. Not caused by this branch's lock bump: gate-94 landed at 02:18 UTC and this app's last development run was 01:51 UTC, so the PR runs are simply the first to be measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/filinq
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags nine shipped URLs here — website, bugs, repository, three screenshots and three markdown links in the description. Two things had to change together, and only one of them is the host: codeberg.org/Conduction/DOCUDESK -> github.com/ConductionNL/FILINQ The Codeberg URLs still carry the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/docudesk — which resolves today purely because GitHub redirects a renamed repo, and stops resolving the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and all three were fetched rather than assumed — HTTP 200 each. Not caused by this branch's lock bump: gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it.
rubenvdlinde
pushed a commit
to ConductionNL/dossiq
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/procest -> github.com/ConductionNL/dossiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/procest, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/integriq
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/openconnector -> github.com/ConductionNL/integriq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/openconnector, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/launchpad
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/launchpad -> github.com/ConductionNL/launchpad The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/launchpad, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/zaakafhandelapp
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/zaakafhandelapp -> github.com/ConductionNL/zaakafhandelapp The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/zaakafhandelapp, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/stackiq
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/softwarecatalog -> github.com/ConductionNL/stackiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/softwarecatalog, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
pushed a commit
to ConductionNL/thematiq
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/nldesign -> github.com/ConductionNL/thematiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/nldesign, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible.
rubenvdlinde
added a commit
to ConductionNL/dossiq
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/procest -> github.com/ConductionNL/dossiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/procest, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/filinq
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(info.xml): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags nine shipped URLs here — website, bugs, repository, three screenshots and three markdown links in the description. Two things had to change together, and only one of them is the host: codeberg.org/Conduction/DOCUDESK -> github.com/ConductionNL/FILINQ The Codeberg URLs still carry the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/docudesk — which resolves today purely because GitHub redirects a renamed repo, and stops resolving the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and all three were fetched rather than assumed — HTTP 200 each. Not caused by this branch's lock bump: gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/integriq
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/openconnector -> github.com/ConductionNL/integriq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/openconnector, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/launchpad
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/launchpad -> github.com/ConductionNL/launchpad The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/launchpad, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/openregister
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(info.xml): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, added in ConductionNL/.github#546) flags six shipped URLs here: website, bugs, repository and three screenshots, all still pointing at codeberg.org. GitHub is the only host; Codeberg is retired. The screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and all three were checked rather than assumed — HTTP 200 each. A green gate with dead image URLs would be worse than the finding. Not caused by this branch's lock bump: gate-94 landed at 02:18 UTC and this app's last development run was 01:51 UTC, so the PR runs are simply the first to be measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/stackiq
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(psalm): stub OpenRegister's contract, which v1.9.0 stopped autoloading hydra-gates v1.9.0 removed `OCA\OpenRegister\Contract\` from its runtime psr-4 autoload (ConductionNL/.github#531). The removal was correct — that prefix is longer than openregister's own, so a vendored copy in ANY app defined the contract for the whole process — but it was verified against PHPUnit only. Psalm never runs the test bootstrap; it resolves types through the composer autoload map. So the guarded require added to this app's bootstrap does nothing for it, and 213 UndefinedClass errors appeared for a class the app typehints but does not own. This is the same situation as the decidesk event stubs already in this file: a sibling Nextcloud app supplies the type at runtime, so it is absent from the analysis path and gets declared here. A stub teaches the analyser the shape without putting the class back into the runtime autoloader, which is what caused the original defect. Measured in this checkout on the real v1.9.0: 213 errors before, 0 after — "No errors found!", psalm exit 0. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/softwarecatalog -> github.com/ConductionNL/stackiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/softwarecatalog, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/thematiq
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/nldesign -> github.com/ConductionNL/thematiq The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/nldesign, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/zaakafhandelapp
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(metadata): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags the shipped URLs in this app's metadata — website, bugs, repository and screenshots — as pointing at codeberg.org. GitHub is the only host. Two things changed together, and only one of them is the host: codeberg.org/Conduction/zaakafhandelapp -> github.com/ConductionNL/zaakafhandelapp The Codeberg URLs still carried the app's PRE-RENAME name. Swapping only the host would have produced github.com/ConductionNL/zaakafhandelapp, which resolves today purely because GitHub redirects a renamed repo — and stops the moment anyone creates a repo at the old path. Screenshots move to raw.githubusercontent.com rather than a github.com/raw redirect, and every one was fetched rather than assumed: HTTP 200 each. A green gate with dead image URLs would be worse than the finding it silenced. Not caused by this branch's lock bump — gate-94 landed at 02:18 UTC, after this app's last development run, so the PR runs are simply the first measured against it. Pre-existing debt, newly visible. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
rubenvdlinde
pushed a commit
to ConductionNL/nextcloud-app-template
that referenced
this pull request
Aug 22, 2026
… host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags seven shipped URLs here: the three <documentation> links, website, discussion, bugs, repository and the app-store screenshot. GitHub is the only host. This repo keeps its name, so unlike the renamed apps only the host changes. The screenshot moves to raw.githubusercontent.com rather than a github.com/raw redirect, and was fetched rather than assumed — HTTP 200. Worth fixing here above all: this is the template every new app is scaffolded from, so the Codeberg URLs were being copied forward into each new app's metadata.
rubenvdlinde
added a commit
to ConductionNL/nextcloud-app-template
that referenced
this pull request
Aug 22, 2026
* chore(deps): refresh the shared Conduction locks hydra-gates v1.8.2 -> v1.9.0 nc-vue 2.10.1 -> 2.11.1 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. * fix(info.xml): point shipped URLs at GitHub, not the retired Codeberg host gate-94 (retired-git-host-metadata, ConductionNL/.github#546) flags seven shipped URLs here: the three <documentation> links, website, discussion, bugs, repository and the app-store screenshot. GitHub is the only host. This repo keeps its name, so unlike the renamed apps only the host changes. The screenshot moves to raw.githubusercontent.com rather than a github.com/raw redirect, and was fetched rather than assumed — HTTP 200. Worth fixing here above all: this is the template every new app is scaffolded from, so the Codeberg URLs were being copied forward into each new app's metadata. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Conduction Release Bot <release-bot@conduction.nl> Co-authored-by: Conduction <dev@conduction.nl>
rubenvdlinde
added a commit
to ConductionNL/dossiq
that referenced
this pull request
Aug 22, 2026
…ow the moves GATE 94 CAUGHT THIS REPO. The gate added in ConductionNL/.github#546 failed on this PR with 9 shipped URLs pointing at Codeberg — procest's own appinfo/info.xml. Worth recording why the fleet sweep missed it: that sweep read each repo's DEFAULT branch, and procest's default is `main`, which had already been repointed. `development` had not. So the app store metadata was correct on one branch and stale on the branch everything is built from — exactly the split the gate exists to find, and it found it on the change that introduced it. Repointed the same way the fleet sweep does: repo name resolved from the API (procest -> ConductionNL/dossiq, renamed 2026-08-21) and every asset URL probed until one returns 200, rather than substituting the domain and hoping. `main`'s version was NOT copied — it carries a different, single-screenshot set. E2E, three scenarios following surfaces this change moved: - Doorlooptijd's heading is the dashboard page's title now ("Processing time"), not the retired component's <h2>Processing Time Analytics</h2>. The old wording survives as the subtitle, which the same spec already asserts. - The two Settings scenarios are retargeted from the retired in-app /apps/procest/settings at /settings/admin/procest. One of them carried FIXME(#719) saying the in-app page rendered only section chrome and that "the same components do render on /settings/admin/procest" — retiring that page resolves the FIXME rather than working around it, so the test is un-fixme'd. gate-94 verified locally: 0 failures across 5 shipped metadata files.
rubenvdlinde
added a commit
to ConductionNL/dossiq
that referenced
this pull request
Aug 22, 2026
…st side) (#1328) * refactor(ai): delegate human-oversight decisions to hermiq (E1, procest side) Hermiq owns AI oversight; procest owns the moment a handler accepts, rejects or corrects a suggestion. This is procest's half of E1 — it lands AFTER hermiq #514, which added the advisory Approval variant and the event contract this dispatches. WHAT MOVES, AND WHAT DELIBERATELY DOES NOT. procest's audit log holds two kinds of entry: `action: suggestion`, which records that the model RAN, and `userAction: accepted|rejected|modified`, which records that a human JUDGED it. Only the second is EU AI Act Art. 14 evidence. Sending the first would put rows in hermiq's oversight log that nobody decided, so suggestion entries stay in procest's own log. VOCABULARY IS TRANSLATED, NOT FLATTENED. procest says `modified`; hermiq says `overridden`. Getting that mapping backwards would file every correction as a rejection, which is the one distinction an oversight audit most needs. THE LOCAL WRITE STAYS. Delegation is additive, not a replacement: on an instance without hermiq the local copy is the only one, and dropping it would make the existence of an oversight trail depend on which apps happen to be installed. `recordUserAction()` now also returns `delegated`, so the caller can tell "recorded centrally" from "local only" — those look identical otherwise. DELEGATION NEVER FAILS THE HANDLER. Unlike ContractDecisionDelegationService, which fails closed because it GATES work, this records something that already happened: the handler has acted and the case has moved on. Refusing after the fact would turn an audit outage into a functional one. MIGRATION RUNS THROUGH THE SAME PATH. MigrateAiOversightToHermiq replays existing decisions through the identical event, so old evidence travels exactly as new evidence does and hermiq never reads procest's register. Idempotent (stable externalRef per source entry) and non-fatal — an upgrade must not fail because an audit projection could not complete, and the local copies remain to replay from. Retires `/settings/ai-oversight` and `/settings/ai-oversight/:id`, replacing the menu entry with a deeplink to `/apps/hermiq/ai-oversight` (hermiq is history-routed like procest, unlike OpenRegister which needs the `#/` form — verified, not assumed). Also records C2 and D1–D4 as DEFERRED in tasks.md: the fleet is mid-way through consolidating the flow engine into OpenRegister (ADR-065), and re-expressing automatic actions or decidesk's approval routes against a moving target would have to be redone. Tests: AiOversightDelegationServiceTest 10/10 covering each vocabulary mapping, the suggestion-only refusal, document-vs-case subject resolution, the no-subject refusal, map-value rendering, replay idempotency and the hermiq-absent path. Adds tests/Stubs/Hermiq so the by-name contract is actually exercised. Full suite 2313 tests green. psalm 0 errors. check:manifest Ajv PASS (50 pages). l10n and prettier clean. Live-verified: /apps/procest/settings/ai-oversight no longer resolves; exactly one nav entry, pointing at /apps/hermiq/ai-oversight, which returns 200. * fix(quality): close the three Hydra gate failures — and a real regression they exposed CI ran gate-16, gate-26 and gate-60 against #1323. All three were genuine, and gate-26 was pointing at something worse than a missing baseline. gate-26 REVEALED FOUR DELETIONS THAT SILENTLY CAME BACK. It flagged DoorlooptijdDashboard.vue and TenantOnboardingDashboard.vue as "new page components" — files this change had DELETED. They were deleted in A3 and B3, and then re-added by the F1 commit: `git add -A src/` re-staged them because they were still sitting on disk. ProcessMiningDashboard.vue and TermijnDashboard.vue came back the same way. All four were dead — nothing imports them, the manifest routes to their widget replacements — so the app was correct and the repository was not. Deleted again, and this is exactly the kind of thing that survives every green check except a gate that asks "what is new here". gate-16: 66 changed methods without @SPEC, now 0. The widget components, the mixins and the three stores each carried only a file-level tag; every computed, action, getter and lifecycle hook now points at the capability spec. Four members had no docblock at all and got one. gate-60: `ShieldAccountOutline` on the AVG link is not registered in src/icons.js, so it rendered as NO icon rather than a fallback. Replaced with `ShieldLockOutline`, which is both registered and ADR-077's `privacy` concept — the AVG processing register is privacy, not administration. The first attempt at that fix was WRONG and the gate caught it: it also changed the admin link from `CogOutline` to `ShieldAccountOutline`, reading ADR-077's "admin" row. Running the checker locally showed ADR-077 Tier A requires `CogOutline` for the `settings` concept, and that the icon swapped in was itself unregistered. Reverted; the lesson is that the ADR table has two rows that look applicable and only the checker knows which one governs a given label. Also removes an orphaned comment fragment the C1 visual-spec edit left behind. All three gates verified locally against the real checkers from ConductionNL/.github: spec-coverage count=0, visual-coverage PASS (7 new pages, all with a visual proof), icon-vocabulary 0 failures. eslint 0 errors, prettier clean, l10n en/nl in sync, check:manifest Ajv PASS, phpunit 2313 green. * fix(ai): the oversight replay read the wrong key — it would have migrated nothing phpstan caught a silent no-op before it ever ran. MigrateAiOversightToHermiq read `$batch['results']`, but AiAuditLog::list() returns `{entries, total, limit, offset}`. The key never existed, so `?? []` handed back an empty array on every page, the loop broke on the first iteration, and the step would have reported "AI oversight replay: no audit entries to consider" on an instance full of them — while the upgrade went green and the evidence stayed behind. This is the exact failure shape this change set has been finding all along: a default-valued read turning missing data into confident wrong behaviour. Fixed to `$batch['entries']`, and read WITHOUT a `??` fallback so a future shape change fails loudly instead of quietly returning nothing. MigrateAiOversightToHermiqTest pins it: an entries-bearing log must report "1 decision(s) sent", not "no audit entries". Also closes the other CI findings on this PR: - phpcs: missing @PARAM for the new AiAuditService dependency, three internal calls needing named arguments, and a missing @SPEC on getName(). - phpmd: run() was doing paging AND per-entry handling AND reporting (cyclomatic 11, NPath 219, an else clause). Split into replayPage() and report(); the gate was right that two concerns were tangled. phpcs 0 errors across lib/, phpmd clean, phpstan [OK] No errors, phpunit 2327 tests green. * fix: gate-94 catches procest's own info.xml, and three e2e specs follow the moves GATE 94 CAUGHT THIS REPO. The gate added in ConductionNL/.github#546 failed on this PR with 9 shipped URLs pointing at Codeberg — procest's own appinfo/info.xml. Worth recording why the fleet sweep missed it: that sweep read each repo's DEFAULT branch, and procest's default is `main`, which had already been repointed. `development` had not. So the app store metadata was correct on one branch and stale on the branch everything is built from — exactly the split the gate exists to find, and it found it on the change that introduced it. Repointed the same way the fleet sweep does: repo name resolved from the API (procest -> ConductionNL/dossiq, renamed 2026-08-21) and every asset URL probed until one returns 200, rather than substituting the domain and hoping. `main`'s version was NOT copied — it carries a different, single-screenshot set. E2E, three scenarios following surfaces this change moved: - Doorlooptijd's heading is the dashboard page's title now ("Processing time"), not the retired component's <h2>Processing Time Analytics</h2>. The old wording survives as the subtitle, which the same spec already asserts. - The two Settings scenarios are retargeted from the retired in-app /apps/procest/settings at /settings/admin/procest. One of them carried FIXME(#719) saying the in-app page rendered only section chrome and that "the same components do render on /settings/admin/procest" — retiring that page resolves the FIXME rather than working around it, so the test is un-fixme'd. gate-94 verified locally: 0 failures across 5 shipped metadata files. * fix(dashboard): the straplines were declared under a key nothing reads CnDashboardPage's prop is `description`, not `subtitle`. The manifest schema accepted `subtitle`, Ajv passed, and all three converted dashboards rendered NO strapline — the components they replaced each drew one, so the conversion silently dropped it. A declaration that validates and does nothing. The e2e suite is what caught it: the Doorlooptijd scenario asserts its heading AND its strapline, the heading fix landed, and the strapline assertion then failed on text that was never rendered. Worth noting the shape — an assertion that survived a rewrite is what found a regression the rewrite introduced. Verified live: `.cn-dashboard-page__description` now reads "SLA adherence and processing time analysis". Also fixes the Settings scenario's strict-mode violation. Retargeting it at /settings/admin/procest worked — and that surface renders EVERY section, four of which have their own labelled save ("Save mandate matrix settings", "Save consultation settings", …). The retired in-app page rendered only chrome, so a loose "Save" matched exactly one button there. Now matched with `exact: true`, which is the Configuration section's own control. check:manifest Ajv PASS, prettier clean. * fix(dashboard): restore the back-to-Dashboard control the conversion dropped Third and last thing the Doorlooptijd e2e scenario caught. The component this replaced drew an explicit back-arrow to the Dashboard; the dashboard page type draws no such control, so converting the page silently removed it. The spec asserted it, which is how it surfaced — the affordance is restored as a declarative `config.headerActions` navigate action rather than deleting the assertion. The icon took two tries and the gate was right both times. `ArrowLeft` mirrors what the old component drew, but it is not registered in src/icons.js — it would have rendered as NO icon, not a fallback — and ADR-077 Tier A requires `ViewDashboardOutline` for the `dashboard` concept anyway. The action's DESTINATION names it, not the direction of travel. Live-verified: heading "Processing time", description "SLA adherence and processing time analysis", and one "Dashboard" button — the three things the scenario asserts, in one page load. icon-vocabulary 0 failures, check:manifest Ajv PASS. * docs(openspec): resume plan for C2/D now the flow engine is consolidated The flow-engine consolidation has landed, so C2 and D1-D4 are no longer blocked. PLAN-RESUME.md carries the state, the step order and the traps, written to survive a context compaction rather than living in a conversation. The thing worth reading before starting C2: `automaticAction` is NOT a standalone object. `caseType.workflowSteps` embeds references to it in three places (`automaticActions`, `config.autoActions`, `config.escalationRule`), so retiring the two pages does not retire the concept — every case type in the field points at these by id, and a migration has to rewrite those references or keep them resolvable. That has to be established before the spec, not after. D1 keeps one gate the flow engine did not open: it removes the two besluitvorming routes that the active consume-decidesk-besluitvorming-leaf change deliberately keeps alive, so it lands after that one, not beside it. Also records the merge order for the open stack — #1328 into #1323, then #1323 into development, because #1323 alone still fails gate-16 and gate-60 and those fixes live on #1328.
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.
Why
ConductionNL moved onto Codeberg on 2026-05-29 and moved back off on 2026-07-23. The git layer completed — verified 2026-08-22, zero codeberg remotes fleet-wide. The shipped metadata did not.
23 apps still declare Codeberg URLs in
appinfo/info.xml, which is published to the Nextcloud app store:<bugs><screenshot><repository>/<website>This is not cosmetic drift — it is the part of a host migration that is invisible from the git layer, which is exactly why it survived a month past the migration.
Why a gate, and why full-tree
Why a gate and not just a sweep: it needs both. A sweep fixes today's 23; the gate is what stops the 24th. The fleet has done two host migrations in three months — the specific host won't recur, but the shape will.
Why NOT diff-scoped — the reasoning gates 84 and 93 give, but sharper here. The drift this gate catches is already in the tree, put there by a migration that touched every repo at once. A diff-scoped version reports nothing on the ~99% of PRs that never open
appinfo/info.xml— it would be blind to 100% of the debt it was written for.Scope: shipped metadata, not prose
Reads only files whose contents reach a user or a package registry:
appinfo/info.xml— root only (an NC app ships exactly one; a nested one belongs to a different app)package.json/composer.json— any depth (workspaces, per-package design tokens are legitimate)and within those, only the fields carrying a followable URL (
bugs,website,repository,documentation,screenshot,homepage,support).Deliberately out of scope: docs, changelogs, specs, learnings, archived openspec changes. Those record what was true when written; rewriting them makes the history untrue. A gate noisy enough to be switched off catches nothing.
custom_apps/and friends are skipped. A dev environment drops checkouts of other apps inside a repo. Walking them made the first draft report openregister as having 69 findings — 63 of which belonged to seven other apps. A gate that blames a repo for a neighbour's metadata teaches people to ignore it.Verification
procestinfo.xml+package.jsonopenregistercustom_appsfixdecidesknextcloud-vuehydrainfo.xml/package.json/composer.jsonChecker exit codes:
0clean,1findings,4not applicable.bash -nclean; rc-to-verdict mapping exercised in isolation (PASS / FAIL / SKIP(na) all reproduced).How this reaches the fleet
Per the delegator contract, each app's
Code QualitycallsConductionNL/.github/.github/workflows/quality.yml@mainand resolves the gate package at run time — so merging this reaches every app with no commit in any app.fleet-drift-sweep.ymlthen surfaces which apps fail it.Note on the sweep: it currently exits non-zero on every run — but that is the sweep working. Its 2026-08-21 run dispatched to all 21 apps, collected verdicts, and failed on
not green on development: pipelinq shillinq. Those are real app failures, not sweep breakage. Issue #523 ("the sweep has never run") is closed and its premise no longer holds.