docs(adr): write the ADR-023 adoption record 27 @spec tags already pointed at - #68
Merged
Merged
Conversation
…inted at Six files carried 27 `@spec openspec/architecture/adr-023-action-authorization.md` tags — ActionAuthService, ActionMatrixController, both action repair steps, the admin matrix component and its api.js client — against a file that had never been written. Every one was a dangling reference (gate-46 spec-anchor-existence), so an entire implemented capability was annotated against nothing. The decision is company-wide and its canonical home is hydra. This is the LaunchPad adoption record: what this repo concretely does to satisfy it, derived from the code, so the anchors resolve to something that describes what they annotate. Anchors cannot point at hydra directly — gate-46 matches `@spec openspec/…` and resolves it inside the repo, so a cross-repo path would not be a reference, it would be invisible. Recorded, all read out of the implementation: the four-step resolution order in requireAction() and why `@all` is tested BEFORE the admin-only short-circuit; default-deny through getAllowedGroups() including the corrupt-JSON path; why the `@` prefix on the sentinel cannot collide with a real group id; why the matrix editor is gated with #[AuthorizedAdminSetting] instead of by the matrix itself; and why ApplyActionBaseline broadens only pristine entries — an admin who narrowed an action must not have it re-broadened on upgrade. Also records an invariant that holds today and is worth keeping: the seed and the enforced set are equal in both directions (seed=78, used=78, neither difference non-empty). Drift either way is silent — a seeded-but-unenforced action governs nothing, and an enforced-but-unseeded one falls through default-deny and becomes admin-only on every install with nothing reporting it. gate-46 spec-anchor-existence, whole tree, hydra-gates at .github@main 756fe89: without this file 27 findings with this file 0 findings Not a blind pass — the gate still fires after the change: @SPEC …/adr-999-invented.md -> target file not found (1) @SPEC …/adr-023-…md#requirement-no-such-heading -> anchor not found in file (1) No code changes.
Contributor
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| composer | ✅ | ✅ 102/102 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-08 10:38 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.
What
Writes
openspec/architecture/adr-023-action-authorization.md, the file that27
@spectags in six files already pointed at and that had never beenwritten. No code changes.
src/components/admin/ActionAuthMatrix.vuelib/Service/ActionAuthService.phplib/Controller/ActionMatrixController.phplib/Repair/ApplyActionBaseline.phplib/Repair/InitializeActions.phpsrc/services/api.jsAn entire implemented capability — action RBAC service, admin matrix endpoint,
two repair steps, the admin UI and its API client — was annotated against
nothing.
Why a local file, when ADR-023 is a hydra ADR
The decision is company-wide and its canonical home stays
hydra/openspec/architecture/adr-023-action-authorization.md. This is anadoption record: what LaunchPad concretely does to satisfy it, derived from
this repo's code. Where the two disagree, hydra wins and this file is the bug.
The anchors cannot point at hydra instead. gate-46 matches
@spec openspec/…and resolves it inside the repo, so a cross-repo path would not be a reference —
it would be invisible to the gate entirely.
Everything recorded was read out of the implementation: the four-step resolution
order in
requireAction()and why the@allsentinel is tested before theadmin-only short-circuit; default-deny through
getAllowedGroups()including thecorrupt-JSON path; why the
@prefix cannot collide with a real Nextcloud groupid; why the matrix editor is gated with
#[AuthorizedAdminSetting]rather thanby the matrix itself; and why
ApplyActionBaselinebroadens only pristineentries — an admin who narrowed an action must not have it re-broadened on
upgrade.
It also records an invariant that holds today and is worth keeping: the seed and
the enforced set are equal in both directions.
Drift either way is silent. A seeded-but-unenforced action is a configuration
surface that governs nothing. An enforced-but-unseeded one falls through
default-deny to
["admin"]and quietly becomes admin-only on every install,with nothing reporting an error.
Proof it can fail
gate-46 spec-anchor-existence, whole tree, hydra-gates at.github@main(
756fe89) — the two arms differ only by the presence of the file:And the zero is real, not a gate gone blind. After the change, gate-46 still
fires on both of its failure modes:
The second matters most: a fragment naming a heading this new file does not
contain still fails, so the file cannot be used to launder future anchors.
Full-tree gate delta
No CI job in this repo produces a full-tree verdict — push runs scope to ~1 file,
PR runs to the diff — so this was measured directly, both arms in the same
environment:
origin/development@70420eb5Fixed:
gate-46(27 findings — the largest single bucket on this repo, 38% ofall findings). Regressions: none.