From 68e0b9957d6e602f0eac764ae13b35280c3f471b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 14:28:04 +0000 Subject: [PATCH] docs(citations): re-anchor the eight ADR-0057 D10 citations to the framework's ADR-0124 D1 (#5701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The eight sites cite `ADR-0057 D10` for "the server enforces; client-side gating is a usability courtesy". That decision is "Setup-nav surfacing follows the capability (ADR-0029 K2); the object stays open" — nav-entry tiering, not enforcement location. The rule they invoke is decided by the framework's ADR-0124 D1 (Accepted 2026-08-18). The anchor is derived from an authority, not chosen: framework ADR-0057 carries a note aimed at exactly this citation — "If a citation of `ADR-0057 D10` brought you here looking for that rule, ADR-0124 is where it is decided." Comment text only; `git diff -U0` carries zero non-comment lines. The "framework numbering; this repo's own ADR-0057 is an unrelated document" parenthetical retires with the number it disambiguated — this repo has no ADR-0124 (its own series stops at 0059) — while the "the framework's ..." possessive stays, so every site still says whose numbering it means. `packages/data-objectstack/src/appAccessProbe.test.ts` is left byte-untouched: it cites the same decision for the capability/service-gating case that decision genuinely does decide. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m --- .../reanchor-adr-0124-d1-citations-5701.md | 33 +++++++++++++++++++ .../app-shell/src/views/RecordDetailView.tsx | 5 ++- .../studio-design/PackageOwdOverviewPanel.tsx | 3 +- .../studio-design/StudioDesignSurface.tsx | 12 +++---- packages/core/src/evaluator/fieldRules.ts | 3 +- .../src/useRecordEditable.test.tsx | 3 +- .../plugin-detail/src/useRecordEditable.ts | 6 ++-- .../src/hooks/useRecordCrudVerdicts.ts | 8 ++--- packages/react/src/hooks/useCapabilityGate.ts | 9 +++-- 9 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 .changeset/reanchor-adr-0124-d1-citations-5701.md diff --git a/.changeset/reanchor-adr-0124-d1-citations-5701.md b/.changeset/reanchor-adr-0124-d1-citations-5701.md new file mode 100644 index 0000000000..e176b10272 --- /dev/null +++ b/.changeset/reanchor-adr-0124-d1-citations-5701.md @@ -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. diff --git a/packages/app-shell/src/views/RecordDetailView.tsx b/packages/app-shell/src/views/RecordDetailView.tsx index 7d1c439cf9..e2a8c03f3f 100644 --- a/packages/app-shell/src/views/RecordDetailView.tsx +++ b/packages/app-shell/src/views/RecordDetailView.tsx @@ -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, diff --git a/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx b/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx index 4b204731dc..71f078be27 100644 --- a/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx +++ b/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx @@ -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; diff --git a/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx b/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx index 1ef20a4f3b..eeee6d56c5 100644 --- a/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx +++ b/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx @@ -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 D10 — framework 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 D1 — server 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(null); React.useEffect(() => { let cancelled = false; diff --git a/packages/core/src/evaluator/fieldRules.ts b/packages/core/src/evaluator/fieldRules.ts index 746c58fd32..fb3a940f54 100644 --- a/packages/core/src/evaluator/fieldRules.ts +++ b/packages/core/src/evaluator/fieldRules.ts @@ -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. * diff --git a/packages/plugin-detail/src/useRecordEditable.test.tsx b/packages/plugin-detail/src/useRecordEditable.test.tsx index 4aac359588..1ab2ac024a 100644 --- a/packages/plugin-detail/src/useRecordEditable.test.tsx +++ b/packages/plugin-detail/src/useRecordEditable.test.tsx @@ -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'; diff --git a/packages/plugin-detail/src/useRecordEditable.ts b/packages/plugin-detail/src/useRecordEditable.ts index b3d6ac61c4..e78aeab54d 100644 --- a/packages/plugin-detail/src/useRecordEditable.ts +++ b/packages/plugin-detail/src/useRecordEditable.ts @@ -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 D10 — framework 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 D1 — server 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 diff --git a/packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts b/packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts index a6939b81bb..b8af8141c3 100644 --- a/packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts +++ b/packages/plugin-grid/src/hooks/useRecordCrudVerdicts.ts @@ -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 D10 — framework 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 D1 — server 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 diff --git a/packages/react/src/hooks/useCapabilityGate.ts b/packages/react/src/hooks/useCapabilityGate.ts index 265258601f..6a297e9b64 100644 --- a/packages/react/src/hooks/useCapabilityGate.ts +++ b/packages/react/src/hooks/useCapabilityGate.ts @@ -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';