Skip to content

chore(sync): carry main back into beta - #184

Open
rubenvdlinde wants to merge 76 commits into
betafrom
sync/main-to-beta-20260830153207
Open

chore(sync): carry main back into beta#184
rubenvdlinde wants to merge 76 commits into
betafrom
sync/main-to-beta-20260830153207

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

main held 75 commit(s) beta did not — release version bumps committed onto the branch the release ran from, and anything else that arrived out of band. Left unreconciled, both branches change the version file independently and the next betamain promotion conflicts on it.

152 file(s) change. A 0-file result is the normal and correct outcome once the content has already reached beta by other means — recording the ancestry is the payload. Without it the merge base never moves and the next promotion conflicts exactly as before.

Version files were resolved to beta's side, whose line is ahead of the released one, so this never moves a version backwards. Any conflict outside them aborts the merge for a human instead.

Opened by fleet-back-merge.yml, using the algorithm release.yml already applies to its own sync/*-to-development pull requests.

WilcoLouwerse and others added 30 commits March 24, 2026 15:02
Release: promote beta to stable — initial CI pipeline and app scaffold
…uctors

Issues caught by every code review on apps built from this template:
- <licence>agpl</licence> → <licence>eupl</licence>
- OC.requestToken → getRequestToken() from @nextcloud/auth
- Added SPDX-License-Identifier: EUPL-1.2 to all PHP/JS/Vue files
- Constructor properties: private → private readonly
The app store only recognises agpl/apache/mit — not eupl.
Source files use SPDX EUPL-1.2 headers (the actual licence).
info.xml uses agpl for store compatibility only.
…uth dep

Fixes from planix: correct .phphunit → .phpunit typo, remove duplicate
glob patterns, and add @nextcloud/auth for getCurrentUser() support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the individual conduction symlink inside openspec/schemas/ with
a single symlink at openspec/schemas pointing to hydra schemas directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…asset only) (#24)

The central Quality workflow (ConductionNL/.github#34) now publishes SBOMs
exclusively as release assets — see SECURITY.md "Software Bill of Materials".

This PR cleans up the per-app remnants:
- delete .github/workflows/sbom.yml (the central job replaces it)
- delete the checked-in sbom.cdx.json (release asset is the source of truth)
- gitignore SBOM files so future generations don't accidentally land in repo

Stable URL for clients:
  https://github.com/ConductionNL/nextcloud-app-template/releases/latest/download/sbom.cdx.json

Co-authored-by: SBOM Cleanup <ops@conduction.nl>
Path-based codeowner mapping per the OR-abstraction-audit follow-up
(2026-05-03). PRs that touch each domain auto-request review from
the matching owners; first-to-approve unblocks per the org ruleset.
Wires the ConductionNL bundling pattern from ADR-004 (Build / bundling)
into the template so apps cloned from this repo get a working dashboard
widget out of the box and never trip the appName/devtool/duplicate-framework
pitfalls that landed across opencatalogi/pipelinq/procest/docudesk.

What is added:

- webpack.config.js: optimization.splitChunks with stable-filename shared
  chunks for Vue + @nextcloud/vue + pinia + icons + @conduction/nextcloud-vue.
  Each entry-point keeps only entry-specific code; shared chunks load once.
- lib/Dashboard/ExampleWidget.php: minimal IWidget. load() attaches shared
  chunks BEFORE the per-widget bundle (vendor → nc-vue → widget). Comments
  explain why and reference ADR-004.
- src/exampleWidget.js: webpack entry that registers the Vue renderer via
  OCA.Dashboard.register. Hard-coded id matches Widget::getId() from PHP.
- src/views/widgets/ExampleWidget.vue: minimal NcDashboardWidget that fetches
  /api/items via @nextcloud/axios with try/catch + graceful empty state.
- AppInfo/Application.php: registerDashboardWidget(ExampleWidget::class).
- README: 'Adding a dashboard widget' how-to listing the 5 registration
  points and pointing at ADR-004 for the full rationale.

Apps that don't need a dashboard widget delete:
  - lib/Dashboard/ + src/exampleWidget.js + src/views/widgets/
  - the registerDashboardWidget(...) line in Application.php
  - the exampleWidget entry in webpack.config.js

The splitChunks block is harmless with only main + adminSettings entries
(produces small shared chunks that two entries reuse) and starts paying off
the moment a widget is added.
Spec the canonical Tier-4 scaffolding for the JSON manifest renderer
pattern in nextcloud-app-template. Codifies hydra ADR-024's "new apps
MUST adopt the manifest from inception" requirement at the source —
the template — rather than retrofitting per app.

Includes proposal, design, tasks, and 10 REQ-TMV1-* requirements
covering manifest contents, bootstrap pattern, registry contract,
webpack alias, dependency floor, and the manifest-first README
quickstart.
Add the canonical template manifest with 4 example pages (one each of
type dashboard / index / detail / settings) and 4 menu entries.
Declares openregister as the default dependency. Settings page
demonstrates the version-info rich-section widget.

Add src/customComponents.js as the empty-by-default registry contract
with a single example placeholder (CustomExample) so the registry's
role is visible to first-time cloners. The manifest does NOT
reference CustomExample by default — it only ships as documentation.

Add tests/validate-manifest.js (copied from decidesk's reference) for
Ajv-based schema validation. Wired up via npm run check:manifest.

Trim l10n/en.json to a clean baseline aligned with the new manifest
strings; add an empty l10n/en_US.json placeholder.
Adopt decidesk's mount-survivable bootstrap pattern (commits 50e4df7c
+ 866ff132) as the template default:

main.js:
  - Import bundledManifest from './manifest.json' and customComponents
    from './customComponents.js'.
  - Build vue-router routes from manifest.pages[*].{id,route} via a
    routesFromManifest() helper that uses a shallow-cloned
    CnPageRenderer ({ ...CnPageRenderer }) — required because Vue 2's
    Vue.extend() mutates the component options object with a _Ctor
    cache, which throws against the lib's frozen barrel exports.
  - Pass shallow-cloned defaultPageTypes and customComponents to
    App.vue as props.
  - Mount on #content immediately, NOT inside loadTranslations'
    callback (NC dev installs commonly 404 the /l10n/<locale>.json
    route, which would silently kill boot). Translation load is
    fire-and-forget; strings fall back to English on miss.

App.vue:
  - Mount <CnAppRoot> with manifest + customComponents + pageTypes
    props, app-id, translateForApp closure, and permissions array.
  - Provide an objectSidebarState reactive channel via provide() and
    mount <CnObjectSidebar> in the #sidebar slot — the standard
    pattern for CnDetailPage → host-rendered sidebar.

settings.js + AdminRoot.vue:
  - Keep the Nextcloud admin app-settings webpack entry-point (a
    distinct surface from the manifest's type:'settings' SPA page).
    Replace the deleted views/settings/AdminRoot.vue with a minimal
    placeholder NcSettingsSection that documents the divergence.

Delete the legacy shell:
  - src/router/index.js (routes built from manifest at boot).
  - src/navigation/MainMenu.vue (CnAppNav replaces it).
  - src/views/Dashboard.vue (manifest type:'dashboard' replaces it).
  - src/views/settings/ (manifest type:'settings' replaces it).
…README

package.json:
  - @conduction/nextcloud-vue ^0.1.0-beta.3 → ^1.0.0-beta.12 (the
    published lib version with the Vue.extend frozen-component fix).
  - @nextcloud/router ^2.0.1 → ^3.1.0 — required by @nextcloud/vue
    8.37+ (NcDashboardWidget / NcAvatar import getBaseUrl, missing
    from router 2.x).
  - Add ajv ^8.17.1 + ajv-formats ^3.0.1 devDependencies for the
    manifest validator.
  - Add scripts.check:manifest → node tests/validate-manifest.js
    (satisfies the fleet adoption spec's build-time validation gate).

webpack.config.js:
  - Add @nextcloud/axios$ alias to force the lib's transitive axios
    import to resolve to the app's installed copy (decidesk pattern,
    commit ed34703c). Without the $ exact-match suffix webpack walks
    up to the lib's own node_modules and loads a second axios
    instance, breaking shared interceptors / CSRF tokens.

eslint.config.js:
  - Override no-console / n/no-process-exit / n/shebang for the
    tests/validate-manifest.js Node CLI script.

README.md:
  - Lead with manifest-first messaging in the intro paragraph and
    in the OpenRegister callout.
  - Add an "Adding a page (manifest-first)" section that documents
    the page-type table and tells cloners to edit src/manifest.json
    rather than writing per-page Vue files. Custom Vue components
    are only required for type:"custom" pages.
  - Add a "Renaming the app" section listing the files where the
    app id appears (the manifest itself does NOT carry the id).
  - Update the directory-structure diagram to reflect the new
    layout (manifest.json, customComponents.js, no router/, no
    navigation/).
feat: scaffold the JSON manifest renderer pattern as the template default (template-manifest-v1)
…copy with placeholders

The SCSS webpack rule was added to webpack.config.js in a prior PR
(commit 209224e "chore: bump nextcloud-vue + router, add axios alias"),
but the actual sass + sass-loader packages were never added to
devDependencies. Result: running 'npm run build' on a fresh clone of
the development branch produces a 'Module parse failed: Unexpected
token' error whenever nextcloud-vue components ship <style lang=scss>
blocks.

Fix:
- package.json devDependencies: add sass ^1.99.0, sass-loader ^16.0.8
  (matching the versions already in opencatalogi + decidesk)
- package-lock.json regenerated via 'npm install --package-lock-only'

Separately, appinfo/info.xml currently ships marketing copy for the
template itself (Nextcloud App Template, A template for creating new
Nextcloud apps, plus a 13-line feature-list description). This copy
survives '/app-create' scaffolding because the placeholder substitution
phase only rewrites identifier-shaped tokens (app-template ->
{APP_ID}, AppTemplate -> {APP_NAMESPACE}). Result: every newly
scaffolded app ships with the template's own description until a human
remembers to rewrite it.

Fix:
- appinfo/info.xml: replace summary + description content with
  {APP_NAME}, {APP_SUMMARY}, {APP_DESCRIPTION} placeholders so the
  /app-create skill can auto-substitute them from openspec/app-config.json
  during scaffolding.

Companion PR in ConductionNL/market-intelligence updates the
.claude/skills/app-create placeholder-replacement-guide.md so the new
placeholders get processed automatically.
…-placeholders

fix: add sass + sass-loader to devDeps + use placeholders for info.xml marketing copy
… deps

PR #28 (merge 4601d2c) shipped a package-lock.json that was missing 11
transitive dependencies of pinia (devtools-kit, devtools-shared, mitt,
perfect-debounce, speakingurl, superjson, copy-anything, is-what,
rfdc, plus a nested @nextcloud/dialogs > pinia entry).

Result: `npm ci` failed on every CI quality job, taking down 5
checks at once (Security npm, License npm, ESLint, Stylelint, SBOM).

Fix: regenerate the lockfile with `npm install` (no package.json
changes). Verified locally:
- `npm ci` now succeeds
- `npm run lint` passes
- `npm run stylelint` passes
- `npm audit --audit-level=high` reports 0 high/critical (25 low/moderate)
chore: regenerate package-lock.json to fix quality CI failures
…hecks (#32)

Adds a Spec Validation workflow + three checks that catch the class of bug
seen in scholiq Wave 2 *before* it reaches development:

- tests/validate-json-strict.js — strict JSON parse of src/manifest.json +
  lib/Settings/*_register.json; FAILS on duplicate keys (git merges JSON
  line-by-line; two adds at the same key but different file positions produce
  no textual conflict, just a doc with a dup key, and json_decode keeps the
  last → silent schema/page loss) and on `appendOnly` nested inside an
  x-openregister block (OpenRegister's Schema::hydrate only reads a top-level
  appendOnly, so a nested one is silently dropped).
- tests/validate-register.js — structural checks on the register seed: every
  schema has slug/type/required/properties; slug uniqueness; lifecycle
  `requires:` references a PHP class that exists under lib/ (catches scholiq's
  missing CoursePublishGuard); a 'schema looks clobbered / is a stub' heuristic
  (≤3 props + no x-openregister-*). Optional deep check against OR's
  configurations/validate endpoint when OR_BASE_URL + OR_BASIC_AUTH are set.
- package.json: check:register, check:json-strict, check:specs scripts.
- .github/workflows/spec-validation.yml: runs `npm run check:specs` on every
  push/PR. Add 'Spec Validation / validate' to the branch-protection ruleset's
  required checks to make it block merges.
- README: documents the new checks and why they matter.

Every app scaffolded from this template inherits the gate.
* feat: adopt the Features & Roadmap menu

Ships the in-product Features & Roadmap page wired into the manifest so apps
scaffolded from this template inherit the Settings-section "Features & roadmap"
entry (powered by OpenRegister's github-issue-proxy, UI from
@conduction/nextcloud-vue). Mirrors the per-app adoptions (procest/decidesk/
pipelinq/scholiq/openbuilt).

- src/views/FeaturesRoadmap.vue — thin wrapper around CnFeaturesAndRoadmapView;
  the `repo` fallback is `ConductionNL/nextcloud-app-template` — cloners change
  it (or provide it via IInitialState `<appId>::features_roadmap_*`)
- customComponents.js — register it as the `FeaturesRoadmap` custom component
- manifest.json — add the `FeaturesRoadmap` custom page (route /features-roadmap)
  + a `FeaturesRoadmapMenu` entry in the settings section
- bump @conduction/nextcloud-vue ^1.0.0-beta.12 → ^1.0.0-beta.35; pin
  @nextcloud/axios ~2.5.2 (+ overrides) — 2.6.0 ships a broken `exports` field
  that breaks @nextcloud/vue under webpack (cf. ConductionNL/openregister#1489)

Refs: ConductionNL/hydra#251

* chore: refresh package-lock.json for the @conduction/nextcloud-vue ^1.0.0-beta.35 bump
…ate (#31)

Wires the AI Chat Companion MCP-tool pattern (hydra ADR-034/035) into the
template so new Conduction apps get it by default.

- lib/Mcp/ExampleToolProvider.php — heavily-commented copy-me provider with
  two trivial example tools: app-template.ping and app-template.describeApp
- lib/AppInfo/Application.php — registers the provider under the alias
  OCA\OpenRegister\Mcp\IMcpToolProvider::{appId}
- tests/Stubs/Mcp/IMcpToolProvider.php — stub interface until openregister
  PR #1466 ships the real one; wired via composer autoload-dev + bootstraps
- tests/Unit/Mcp/ExampleToolProviderTest.php — contract test (7 cases)
- README: new "AI Chat Companion / MCP tools" section
- psalm.xml: allow the OCA\OpenRegister\Mcp\IMcpToolProvider cross-app ref

Widget mount: the template already mounts CnAppRoot from
@conduction/nextcloud-vue (^1.0.0-beta.12), which renders the companion FAB
once nextcloud-vue beta.31 (CnAiCompanion) is published — no bump needed.
…ings) (#34)

- Change SettingsMenu entry to action: "user-settings" / icon: "Cog"; drop route + section.
  CnAppNav invokes cnOpenUserSettings inject (CnAppRoot) which opens NcAppSettingsDialog.
  The Settings type:'settings' page stays for direct-URL /settings access.
- App.vue: import NcAppSettingsSection; add #user-settings slot to CnAppRoot
  with a placeholder NcAppSettingsSection — replace with real settings content
  when scaffolding a new app.
- README: one-line note in the manifest/menu section documenting the pattern.
- Upgrade @conduction/nextcloud-vue from beta.30 → beta.35 (schema 1.5.0) to
  support action: "user-settings" — package.json already declared ^1.0.0-beta.35;
  node_modules was behind.
- validate-manifest PASS (0 Ajv errors, schema 1.5.0); lint 0 errors; build succeeds.
Keeps the app template on the current lib — CnIndexPage store-backed
self-fetch (nc-vue #223) + columns[].formatter / .widget / .aggregate +
pages[].config.filter (#219/#221/#222) — so scaffolded apps start at the
right version.
chore(deps): bump @conduction/nextcloud-vue to ^1.0.0-beta.40
…fold (#36)

Every new Conduction app currently inherits only a documentation.yml
deploy-workflow stub with no Docusaurus site behind it. This adds the
default so a scaffolded app is born docs-ready:

- docs/ on @conduction/docusaurus-preset 2.6.1 — createConfig/BRAND_THEME,
  brand <DetailHero>/<WidgetShelf> landing page, intro.md, custom.css,
  sidebars.js, package-lock.json, static/CNAME (app-template.conduction.nl),
  static/img/logo.svg.
- journeydoc scaffold (hydra ADR-030) — tutorials/{_category_,user/,admin/}
  with two stock stories (user "open the app for the first time" → Dashboard,
  admin "manage settings" → Admin Settings), tests/e2e/docs-screenshots.spec.ts
  capture suite, and playwright.config.ts with chromium + docs-capture projects.
- .github/workflows/documentation.yml — deploy from `development` to
  app-template.conduction.nl (was: `documentation` branch / app-template.app).
- .gitignore — docs/ build artefacts, docs/i18n/nl/, playwright outputs.
- README.md — Screenshots section now points at the journeydoc workflow,
  directory-structure block lists docs/ + tests/e2e/ + playwright.config.ts,
  and a Documentation section describes the docs-site build + deploy.

Build verified locally (`npm ci --legacy-peer-deps && npm run build`).
…Vue-mount failure) (#37)

webpack.config.js uses splitChunks with `enforce: true` cacheGroups that
emit two shared chunks (`<appId>-shared-vendor`,
`<appId>-shared-nc-vue`). The main and adminSettings entry bundles wrap
their Vue mount in `__webpack_require__.O(0, [shared chunks], …)`, which
only fires once every listed chunk has registered itself on
`self.webpackChunk<appId>`. With only the entry script in
`addScript()`, the shared chunks never load, the mount callback never
fires, and the app silently renders nothing.

Mirrors the canonical fix in zaakafhandelapp#206. ExampleWidget.php
already loaded the shared chunks correctly; this brings the page and
admin-settings templates into line.

Because this is the scaffold template, every newly-generated app
inherits the fix.
Defense against supply-chain attacks (e.g. shai-hulud, nx-style
compromised publishes) by blocking install of any package version
published less than 24h ago.

- .npmrc: `min-release-age=1` (npm 11.5+ native; older npm ignores it)
- .github/dependabot.yml: `cooldown.default-days: 1`, with @conduction/*
  excluded so first-party releases reach our apps immediately

For release-day consumption of fresh @conduction/* deps, use
`npm install --min-release-age=0 @conduction/pkg@x.y.z`.
rubenvdlinde and others added 29 commits May 29, 2026 13:13
The openbuilt app was renamed to openbuild; update stale references
(comments, docs, examples, lint configs, test data).
The mydash app was renamed to launchpad; update stale references
(comments, docs, examples, lint configs, test data).
… path

The loadConfiguration() method was calling
$configurationService->importFromApp(appId, force:) with two named args,
but OR's ConfigurationService::importFromApp signature is
(string $appId, array $data, string $version, bool $force=false).
Every reader of the build-an-app-tutorial Part 2 ran into:

    Argument #2 ($data) not passed

…and the import silently failed, leaving the register + schemas
uncreated even though the JSON shipped in lib/Settings/<appId>_register.json
was valid.

Switch to importFromFilePath(appId, filePath, version, force) which
takes a path and does the parse internally. The catch: OR's
ImportHandler resolves filePath relative to \OC::\$SERVERROOT
(it prepends /var/www/html/), so absolute paths fail with
"Configuration file not found". Strip SERVERROOT to produce the
relative path OR expects.

Also reads the JSON's info.version up-front so the version arg lands
correctly for OR's idempotency check, and returns it back to the caller
instead of the hardcoded 'unknown' fallback.

Validated end-to-end as part of the build-an-app-tutorial Phase 4
walkthrough: after this fix, POST /api/settings/load returns
success: true and the petstore register + category/pet/order schemas
land in OR on the first try.
Resolves conflicts by keeping the modernised feat branch state. The deleted
src/router/index.js, src/views/Dashboard.vue, and src/views/settings/{AdminRoot,Settings}.vue
were superseded by the manifest-driven shell in the modernisation work;
main's modifications to those files no longer apply.

Community-sync additions from main (CONTRIBUTING.md, CODE_OF_CONDUCT.md,
SECURITY.md, SUPPORT.md, .forgejo/issue_template/*) are taken as-is.
… + SettingsService importFromFilePath fix' (#6) from feat/docs-ai-baseline into main
New apps are scaffolded from this template, so the placeholder
Documentation entry should land on https://www.conduction.nl/academy
(learn-to-build-Conduction-apps) rather than the template's own repo URL.
…ademy' (#7) from fix/docs-url-academy-main into main
NC renders SVGs via Imagick server-side where currentColor resolves to black.
Replace placeholder stack icon with document/lines icon using fill: #fff CSS class.
…g favicon compatibility' (#9) from fix/icons-favicon-fill into main
Picks up the dist-CSS :deep() lowering fix so scoped rules (e.g. the
OpenBuild edit button's orange accent) render in dist-built consumers.
… built-in object-table manifest entry

Implements ADR-049 Decision 5 (chain link 4). Closes #11.

- Delete src/widgets/ExampleWidget.vue and its kind:"widget" registry
  entry: the scaffold no longer re-seeds a bespoke widget into every new
  app.
- Add a "recent-examples" object-table widget entry to the Dashboard
  page in src/manifest.json demonstrating the full ADR-049 Decision 2/3
  contract: token-resolved source (filter created[lt]=@today, order,
  limit), columns with the daysSince formatter and cn-cell-- classes,
  compact-list mode (hideHeader + borderless), rowRoute, viewAllRoute,
  emptyText, and one object-op row action (op: patch, values:
  {status: "closed"}, confirm: true). Intent only — OpenRegister RBAC
  is the authority.
- registry.js documents the built-in-first rule: custom kind:"widget"
  entries require a _note (hydra gate 29).
- tests/registry.spec.js: widget kind is no longer required (target: 0);
  any widget entry that IS added must carry a justifying _note.
…nDataTable compact-list pattern

The NATIVE Nextcloud Dashboard (OCA.Dashboard) demo surface stays — it
teaches IWidget + webpack-entry + renderer wiring — but the renderer
moves off the legacy NcDashboardWidget pattern onto the universal
CnDataTable compact list: :rows + two columns with cn-cell--strong /
cn-cell--muted cn-cell--end classes, hide-header, borderless,
:empty-text, @row-click same-tab navigation via window.location.href,
and a #footer View-all link (mirrors procest's migrated widgets).

openspec/specs/scaffold-components: REQ-COMP-003 updated to the
CnDataTable row shape { id, mainText, subText, targetUrl }; the intro
no longer mislabels the native dashboard widget as a registry
kind:"widget" (the scaffold ships zero of those — ADR-049).
The beta carrying the ADR-049 list-widget enrichment (object-table
declarative source / daysSince formatter / object-op actions /
stats-block entries[]). package-lock.json intentionally untouched:
1.0.0-beta.154 is not on npm yet (beta tag at .153 at commit time) —
run npm install --package-lock-only @conduction/nextcloud-vue@1.0.0-beta.154
once it publishes. The lockfile was already out of sync with
package.json on main (pre-existing).
…ted custom ExampleWidget

README: built-in-first widget rule (ADR-049) with the recent-examples
manifest entry as the worked example; custom-widget steps now require a
_note; native-Dashboard section clarified as the OCA.Dashboard surface
with the CnDataTable renderer. docs/intro.md + docs landing page copy
updated to match.
…r scripts

Pre-existing issues surfaced while linting this change: the hydra
gate-16 @SPEC traceability tag warned as an invalid jsdoc tag, and only
tests/validate-manifest.js was exempted from no-console /
n/no-process-exit while the four sibling CLI validators
(validate-register, validate-json-strict, manifest-v2.spec,
registry.spec) use the same pattern.
Published from nextcloud-vue#87 (ADR-049 link 2). Lockfile-only update;
--min-release-age=0 per the .npmrc's documented first-party override.
… — ADR-049 chain link 4' (#12) from feat/zero-custom-widgets into main
# Conflicts:
#	package-lock.json
#	package.json
…eferences to launchpad' (#10) from chore/rename-mydash-to-launchpad into main
Inhoud is exact de Codeberg-main (merge -s ours-equivalent via commit-tree);
de banner-commit van 29-05 (GitHub-archivering) wordt alleen als ancestor
opgenomen zodat de push naar GitHub-main een fast-forward is — geen force,
geen content-wijziging. Onderdeel van de Codeberg→GitHub-migratie 16-07.
The fleet timeout sweep bounded the development branches; main carries an
older, separate copy of these workflows that was missed. Add job-level
timeout-minutes so a hung runner fails fast instead of burning the full
6-hour GitHub default.

Bounds are deliberately loose relative to observed durations: a timeout
that fires under normal contention turns a slow run into a phantom defect.
The goal is to catch a hang, not to enforce speed.

Jobs that call a reusable workflow via uses: are untouched - GitHub rejects
timeout-minutes on such a job and the whole file becomes a startup_failure.
Those are bounded at source in ConductionNL/.github.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…ion` (#119)

GitHub names a reusable-workflow status context `<caller-job-id> / <called-job-name>`, so this
caller reported as `check / check-branch` while the org ruleset requires
`branch-protection / check-branch`. The required context never reported at all, leaving PRs to
main/beta permanently BLOCKED.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/nextcloud-app-template @ 9ece206

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
composer ✅ 100/100
npm
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright ⏭️
Hydra gates ⏭️

Quality workflow — 2026-08-30 15:59 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants