docs(apphost): correct the laziness claim and document the mandatory autoload prelude - #2358
Open
rubenvdlinde wants to merge 3 commits into
Open
docs(apphost): correct the laziness claim and document the mandatory autoload prelude#2358rubenvdlinde wants to merge 3 commits into
rubenvdlinde wants to merge 3 commits into
Conversation
…relude Bootstrap.php claimed 'Lazy by construction — a disabled OpenRegister never fatals NC bootstrap', and Routes.php claimed that referencing no OR symbol makes requiring it 'safe even when OpenRegister is disabled'. Both hold for the closure BODIES, not for resolving Bootstrap/Routes themselves: calling either is an ordinary autoload of an OCA\OpenRegister\ class, and it throws when that prefix is unavailable. That is the normal case for leaves sorting before openregister, because getEnabledApps() sort()s the app list and Coordinator::registerApps() calls registerAutoloading() then register() one app at a time. Documents the required autoload prelude as part of AppHost adoption, records why loadApp() and a relative vendor/autoload include are both wrong, and adds the matching requirement + scenarios to the canonical apphost-boilerplate spec. Documentation only — no behaviour change.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ✅ | ✅ 713/713 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-05 21:26 UTC
Download the full PDF report from the workflow artifacts.
… diff [hydra-gate-security-change-has-tests exclude] Documentation-only diff: two docblocks under lib/AppHost/ and one openspec spec file. gate-47 classifies Bootstrap.php as security-touching because the FILE CONTAINS matching tokens, not because this diff changes any of them — no executable statement is added, removed or altered, so there is nothing for a test to exercise. Recorded as an empty commit because the gate reads the head commit message, and a re-run replays the original event payload rather than an edited PR body.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ✅ | ✅ 713/713 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-05 21:45 UTC
Download the full PDF report from the workflow artifacts.
…he PR body The previous opt-out lived only in the commit message. On a pull_request event actions/checkout checks out the refs/pull/N/merge ref, so the gate's `git log -1 --pretty=%B` reads GitHub's merge commit, not ours, and never sees it. HYDRA_GATE_PR_BODY is the path that works — and a re-run replays the original event payload, so a fresh push is needed for the edited body to be picked up.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ✅ | ✅ 713/713 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-05 22:03 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.
Documentation only. No behaviour change, no code change.
The claims that are wrong as written
lib/AppHost/Bootstrap.php:lib/AppHost/Routes.php:Both are true of the closure bodies and of these files' contents. Neither is true of resolving
Bootstrap/Routesthemselves. To callBootstrap::register()the leaf must resolve the symbolOCA\OpenRegister\AppHost\Bootstrap, and that is an ordinary autoload. If the prefix is unavailable, the call throws before a single closure has been created — the array builder being pure does not help, because control never reaches the array.Why that matters more than it looks
OC_App::getEnabledApps()doessort($apps), andCoordinator::registerApps()walks that sorted list callingOC_App::registerAutoloading($appId, $path)and then$application->register()for one app at a time. So an app'sregister()runs before the PSR-4 prefix of every alphabetically-later app exists.Scope, stated precisely: this affects AppHost consumers whose app id sorts before
openregister— on a healthy instance, with OpenRegister enabled. Apps sorting after are unaffected today; for them the prelude is defensive only. Observed on doriath (audit listener recorded ZERO dispatched events after an unguarded reference aborted the wholeregister()) and independently on openconnector (class_exists at register(): falseon a clean install). openbuild is hit in CI.Guarded, it degrades quietly:
class_exists()answers FALSE, the plumbing is skipped, and classes existing ONLY as aliases registered byBootstrap— a leaf'sController\HealthControlleraliasingAppHost\Controller\GenericHealthController— then fail to resolve, so those endpoints return 500, not 404. Unguarded, the\Erroraborts the leaf's entireregister();Coordinatorlogs anemergencyand continues, so the app stays enabled with half its wiring missing.What this PR changes
Bootstrap.php— replaces the "Lazy by construction" section with one that separates lazy bodies from non-lazy resolution, documents the sort-order trap, and specifies the required prelude, including whyIAppManager::loadApp()(it callsCoordinator::bootApp(), booting OR before its ownregister()has run) and a relativeinclude_once ../../../openregister/vendor/autoload.php(assumes one shared apps directory; silently no-ops on multi-apps_paths) are both wrong.Routes.php— corrects the safety claim and shows theclass_exists()-guarded canonical form. Also notes that route files are loaded during request matching, long after every app has registered, soroutes.phpis not exposed to the registration-order trap — a bare unguardedreturn Routes::standard()is simply a hard dependency on OpenRegister being installed and enabled.openspec/specs/apphost-boilerplate/spec.md— the "One-Call Bootstrap" requirement carried the same overclaim ("lazily, such that Nextcloud bootstrap never fatals when OpenRegister is disabled"); its "Disabled OpenRegister degrades, never fatals" scenario is only true for a leaf that guards the call, so the GIVEN now says so. Adds a Requirement: Autoload Prelude for AppHost Adoption with two scenarios.The
Bootstrap.phpinvariant note is kept but narrowed: no OR symbol outside a closure is what keeps the closures lazy, and it says nothing about resolvingBootstrapitself, which is the leaf's responsibility.Verification
php -lclean on both files. PHPCS clean on both (run under the container's PHP 8.4 with openregister's own ruleset). I checked that no test reads these files as text before addingOCA\OpenRegister\…names into their docblocks —tests/Unit/AppHost/BootstrapTest.phpasserts behaviour, not source text (positive-controlled: a control pattern matched the same file 8×).Enforcement of the prelude across adopting leaves is hydra gate-64 (ConductionNL/.github#174). Leaf-side fixes: doriath#162, opencatalogi#812, openconnector#1157, docudesk#390, launchpad#58, pipelinq#711.
[hydra-gate-security-change-has-tests exclude] Documentation-only diff: two docblocks under lib/AppHost/ and one openspec spec file. gate-47 classifies Bootstrap.php as security-touching because the FILE CONTAINS matching tokens, not because this diff changes any of them — no executable statement is added, removed or altered, so there is nothing for a test to exercise.