Skip to content

Record "server enforces, client is courtesy" as a real ADR decision — ~30 in-repo sites plus 9 in objectui cite it, and no ADR decides it #9628

Description

@os-steve

Split out of #9255 (that card's retarget half). This card is the part a dev seat may not do: authoring an ADR entry. docs/adr/** is maintainer-authored, so the #9255 implementation stopped at the traceability half and left the decision itself unrecorded.

What the corpus search found (measured at 14935ab71, whole docs/adr/ corpus, 127 records)

The rule is real and universally practised; no ADR decides it. Searched by content, not by number — courtesy, server enforces, server is the authority, client is a courtesy, enforcement point, fail-closed, UI absence, plus every decision heading matching server|client|enforc|authorit.

CandidateWhat it actually decidesIs it the rule?
0057-erp-authorization-core-business-units-and-scope-depth.mdD10 (:422)Setup-nav surfacing follows the capability (ADR-0029 K2); the object stays openNo — nav-entry tiering
same file, PS-2 implementation note (2026-06-22, :516) — "D10 realized via server-enforced requiresService"The Setup nav is filtered server-side in filterAppForUser; requiresObject is "a client-side (objectui) gate, not enforced in this repo"Closest ancestor — but scoped to nav/app-metadata visibility, not a general enforcement-location rule
0057-system-data-lifecycle-and-retention.mdLifecycle/retention; uses §3.1–3.6, zero D<n> headingsNo
ADR-0049 (no unenforced security properties)Whether a declared property is enforced (declared = enforced, no silent fail-open)No — different axis (whether, not where)
ADR-0104 addendum, "the enforcement-point principle" (:398)Build/validate time vs runtimeNo — different axis (when, not where)
ADR-0111 D2 / D6Management authority enforced in the service, not just the routeNarrow instance, not the general rule

Provenance. The first commit to ever write the string is 2256e9369"feat: gate Setup Org/Invitations nav on multi-org, server-side (ADR-0057 D10) (#2150)". So the citation began correct and nav-scoped, and the general principle was back-formed from the PS-2 note's server-vs-client contrast. Every later site inherited the number without the scope.

Why this needs a decision record, not a comment fix

The rule is load-bearing across five packages and both repos, and it is the reason for at least three deliberate behaviours: rule-validator's unbound-root-resolves-to-LOCKED narrowing of ADR-0058 D5, the /me/permissions FLS fold in current-user-endpoints.ts, and RUNNER rule 4 ("both sides of every gate"). Those all rest on a sentence with no recorded decision behind it. #9255's PR converts the assertive citations to attributive phrasing so no reader is sent to a decision that does not say this — that removes the false trail but leaves the rule unanchored.

Proposed disposition (maintainer's to make)

The 2026-08-18 ruling on #9255 already authorized this shape: add a decision entry to 0057-erp-authorization-core-business-units-and-scope-depth.md at the next free number (D13), marked as a retroactive recording of an already-practised rule, whose text is the standing sentence and whose scope is explicitly general (not nav-only). Once it exists, the attributive phrasing landed by #9255 becomes a mechanical retarget to a resolvable anchor.

Two things worth deciding at the same time:

  1. Scope wording. The rule as practised covers visibility gates, field locks, FLS maps and route authority. If D13 is written nav-narrow it will be re-generalized by the next citer, which is exactly how this happened.
  2. Home. ERP-authorization ADR-0057 is where the lineage sits, but the rule is not ERP-specific. A general enforcement-authority ADR of its own may age better, and would sidestep the 三个 ADR 编号各自指向两个不同的已接受决策(0010 / 0019 / 0057)—— adr-anchors.json 与 328 处代码注释按裸编号引用,无法消歧 #5992 bare-ADR-0057 ambiguity permanently.

Blocking relationship

#9592 (teaching check-adr-anchors to validate that a cited Dk is a real heading of the cited ADR) is pm:blocked on the retarget. That gate cannot go green on these sites until this decision exists — attributive phrasing removes the false anchor but does not give the sites a resolvable one.

Refs: #9255, #9592, #5992, #8386, objectui#3888.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions