Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .changeset/reanchor-adr-0124-d1-citations-5701.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
---
---

Traceability only — this publishes nothing, declared explicitly with an empty frontmatter
rather than left undeclared. The change is comment text at eight live source sites; no
executable line moves, and `git diff -U0` carries zero non-comment added or removed lines.

`server enforces, client is courtesy` was cited at those eight sites as the framework's
**`ADR-0057 D10`**. That decision reads *"Setup-nav surfacing follows the capability
(ADR-0029 K2); the object stays open"* — nav-entry tiering, not enforcement location. The
rule these sites actually invoke is decided by the framework's **`ADR-0124 D1`**, *"The
server is the enforcement point; client-side gating is a usability courtesy"* (Accepted
2026-08-18). #5699 fixed the repo-ambiguity half of this defect — whose ADR numbering the
citation meant — and deliberately left the anchor half; this is that half.

The new anchor is derived from an authority, not chosen: the framework's own ADR-0057
carries a note aimed at precisely this citation — *"If a citation of `ADR-0057 D10`
brought you here looking for that rule, ADR-0124 is where it is decided."* The substantive
claim at every site is unchanged; only the anchor moves.

The disambiguating parenthetical #5699 shipped — *"framework numbering; this repo's own
ADR-0057 is an unrelated document"* — is retired along with the number it disambiguated.
It warned about a collision specific to `0057`; this repository has no ADR-0124 at all
(its own series stops at `0059`), and ADR-0124 records that a fresh, unambiguous number
was chosen so that citations of it would not need such a warning. The `the framework's …`
possessive stays at every site, so each one still says whose numbering it means.

Left byte-untouched, deliberately: `packages/data-objectstack/src/appAccessProbe.test.ts`
cites the same decision for *"an app gated by an absent optional service"* — the
capability/service-gating family that decision genuinely does decide, so it is correct as
it stands. `docs/adr/0036-field-conditional-rules.md`, the wording all eight derive from,
carries the same misattribution and moves in its own PR: `docs/adr/**` is a governed
surface that stops at draft for human merge.
5 changes: 2 additions & 3 deletions packages/app-shell/src/views/RecordDetailView.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -1062,9 +1062,8 @@ export function RecordDetailView({ dataSource, objects, onEdit, objectNameOverri
// a record the user may only read: the form opened, the user retyped a
// field, and the server rejected the save with a 403. Ask the explain
// engine for the row-level verdict (fail-open; the server stays the
// authority per the framework's ADR-0057 D10 — framework numbering; this
// repo's own ADR-0057 is an unrelated document) and fold it into the same
// affordance gates.
// authority per the framework's ADR-0124 D1 — server enforces, client is
// courtesy) and fold it into the same affordance gates.
const recordWriteAllowed = useRecordEditable(
objectDef?.name,
pureRecordId,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -87,8 +87,7 @@ export interface PackageOwdOverviewPanelProps {
onDraftSaved?: () => void;
/**
* Courtesy gate: read-only packages render badges only (the framework's
* ADR-0057 D10 — framework numbering; this repo's own ADR-0057 is an
* unrelated document).
* ADR-0124 D1 — server enforces, client is courtesy).
*/
readOnly?: boolean;
locale: SupportedLocale;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -423,12 +423,12 @@ export function StudioDesignSurface({ aiSlot }: StudioDesignSurfaceProps): React
const tab = params.tab ?? 'interfaces';
const locale = useMetadataLocale();

// Courtesy gate (the framework's ADR-0057 D10framework numbering; this
// repo's own ADR-0057 is an unrelated document): a read-only code/installed
// package refuses authoring server-side (ADR-0070), so don't let the user
// build up doomed local edits first — disable the authoring affordances up
// front. Unknown writability (fetch failed / still loading) stays ungated;
// the server gate remains the authority either way.
// Courtesy gate (the framework's ADR-0124 D1server enforces, client is
// courtesy): a read-only code/installed package refuses authoring
// server-side (ADR-0070), so don't let the user build up doomed local edits
// first — disable the authoring affordances up front. Unknown writability
// (fetch failed / still loading) stays ungated; the server gate remains the
// authority either way.
const [pkgWritable, setPkgWritable] = React.useState<boolean | null>(null);
React.useEffect(() => {
let cancelled = false;
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/evaluator/fieldRules.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,8 +35,7 @@
* `stripReadonlyWhenFieldsMulti` on the bulk path — DELETES that key from the
* UPDATE payload. This file keeps the same fault fail-OPEN. For that one class
* the two ends therefore point in OPPOSITE directions, deliberately: the
* framework's ADR-0057 D10 — server enforces, client is courtesy (framework
* numbering; this repo's own ADR-0057 is an unrelated document) — makes the
* framework's ADR-0124 D1 — server enforces, client is courtesy — makes the
* server the authority, so the courtesy layer does not get to guess "locked"
* and grey out a field the server might have accepted.
*
Expand Down
3 changes: 1 addition & 2 deletions packages/plugin-detail/src/useRecordEditable.test.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,8 +17,7 @@
*
* Every uncertainty must fail OPEN — a courtesy hint may never be the reason a
* permitted user cannot act. The server is the authority (the framework's
* ADR-0057 D10 — framework numbering; this repo's own ADR-0057 is an
* unrelated document).
* ADR-0124 D1 — server enforces, client is courtesy).
*/
import * as React from 'react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
Expand Down
6 changes: 3 additions & 3 deletions packages/plugin-detail/src/useRecordEditable.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,9 +13,9 @@
* sharing rule sits inside an object the user may otherwise create and edit
* freely — so the header offered a primary "Edit" CTA that opened the form, let
* the user retype a field, and only then bounced with a 403. The server is the
* authority (the framework's ADR-0057 D10framework numbering; this repo's
* own ADR-0057 is an unrelated document) and stays so; this is the courtesy
* check that stops the UI from inviting a write it knows will fail.
* authority (the framework's ADR-0124 D1server enforces, client is
* courtesy) and stays so; this is the courtesy check that stops the UI from
* inviting a write it knows will fail.
*
* The answer comes from the explain engine's record-grained verdict
* (`POST /api/v1/security/explain` with a `recordId`, ADR-0090 D6 / ADR-0095
Expand Down
8 changes: 4 additions & 4 deletions packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,10 +41,10 @@
* SPA origin where the cookie doesn't reach the API: the row's verdict is
* `undefined` and the caller keeps the OBJECT-level answer, i.e. exactly what
* this list rendered before this hook existed. The server is the authority
* (the framework's ADR-0057 D10framework numbering; this repo's own
* ADR-0057 is an unrelated document) and stays so; hiding a capability on
* missing data would be a worse defect than the wasted click this fixes, and
* it is the same posture `useRecordEditable` takes for the detail header.
* (the framework's ADR-0124 D1server enforces, client is courtesy) and
* stays so; hiding a capability on missing data would be a worse defect than
* the wasted click this fixes, and it is the same posture `useRecordEditable`
* takes for the detail header.
*
* The probe rides the host's AUTHENTICATED fetch (`SchemaRendererProvider`'s
* `apiFetch`) rather than the bare global one: a bearer-token session carries
Expand Down
9 changes: 4 additions & 5 deletions packages/react/src/hooks/useCapabilityGate.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,11 +24,10 @@
*
* **Fail-OPEN when unknown.** No runner, no user, no `systemPermissions` array:
* the action shows. Unknown is not denied, the server is the authority
* (the framework's ADR-0057 D10 — framework numbering; this repo's own
* ADR-0057 is an unrelated document), and hiding a permitted user's button on
* missing client data is the worse failure. An EMPTY array is not unknown —
* it means "holds
* nothing" and gates normally.
* (the framework's ADR-0124 D1 — server enforces, client is courtesy), and
* hiding a permitted user's button on missing client data is the worse
* failure. An EMPTY array is not unknown — it means "holds nothing" and gates
* normally.
*/

import { useCallback, useContext } from 'react';
Expand Down
Loading