Skip to content

docs(identity): re-point the cloud-identity ADR-0024 citations at the records that decide them - #18228

Merged
claude[bot] merged 2 commits into
mainfrom
claude/issue-14361-adr-0024-0081-citations
Sep 14, 2026
Merged

claude[bot] merged 2 commits into
mainfrom
claude/issue-14361-adr-0024-0081-citations

Conversation

@claude

@claude claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Part of #14361
Clause-②: no
Nothing this diff touches is an accept/reject surface: no schema, no validator, no guard predicate and no error code moves. Comments, doc comments, one operator-facing refusal sentence and four translation leaves move, and the only runtime string that changes is the ADR number inside a message whose condition, status and code are untouched.

Segments 2 and 3 of the card. Segment 1 (ADR-0071 → ADR-0134) landed as #18098 and the card was returned to pm:queue for these two.

The governing ruling

The director's amendment (comment 5507573601), not the ruling 14 minutes before it. The amendment changed both the target — a local ADR number for the open mechanism half, ⛔ not a cloud ADR-NNNN spelling — and the scope: 0071 + 0024 + 0081, one trip. Its three Blocked-by: mirror ADRs were released by the H19 unlock double-check (5594577301), and all three records are on main today: ADR-0133 (mirrors cloud 0081 D1/D3/D4), ADR-0134 (mirrors cloud 0071), ADR-0135 (mirrors cloud 0024's open half).

The classification rule, written down before any edit

ADR-0024 names two unrelated decisions from this repository's point of view, and the split is not guessable from a path. Three objective criteria decided every line:

  1. The local record has no D-letters at all. docs/adr/0024-mcp-connectors.md numbers its Decision section §1–§5 and contains zero occurrences of SSO, ai_seat or D5.2. So any ADR-0024 Dk citation is, mechanically, not about the local record.
  2. ADR-0135 publishes the mirror boundary itself. Its Decision section restates cloud ADR-0024's D1, D2, D4, D5, D5.2, D6, D7 and D9, keeping the cloud record's lettering on purpose — "The lettering is the cloud record's, kept so that a citation of cloud ADR-0024 D5.2 and a citation of this record's D5.2 name the same clause." Its "What stays in cloud ADR-0024" table names D3, D5.1, D8, D10 and V1 and states the rule: "code in this repository that means one of them must keep citing cloud ADR-0024".
  3. The ② spelling is identified by name. ADR-0135 D6: "Domain verification is opt-in — the clause this repository's code cites as ADR-0024 ②." So ADR-0024 ② becomes ADR-0135 D6, which is letter-checkable where ② was not.

ADR-0024 — the three-way split, which adds up

Measured per citation line with the anchors gate's own regex (ADR_CITATION in scripts/check-adr-anchors.mjs), pnpm-lock.yaml excluded, on merge base 1bdbf82cb.

Class Lines Disposition
Points correctly at the local MCP record 15 ⛔ untouched — byte-identical to main
Points at a clause ADR-0135 restates 73 re-pointed to ADR-0135 with its letter
Points at a clause that stays in cloud 6 re-pointed to cloud ADR-0024
Published archive — 7 package CHANGELOGs 36 ⛔ untouched (the ruling's fence)
Governed surface — docs/adr/** 22 ⛔ untouched (Prime Directive #14)
Undeterminable 0 —
Total 152

The dispatch brief's figure was 151, taken 2026-09-14. Re-measured today it is 152; the one added line is inside docs/adr/0135-*.md, which this diff does not enter.

Letters written, each cross-checked against the record's own decision index: D4 ×4 (source-of-truth marking), D5.2 ×27 (the break-glass invariant), D6 ×40 (SSO per environment, including the ② clause), D9 ×2 (environment users and org membership) — 73. Cloud-qualified: cloud ADR-0024 V1 ×1 (the SSO default-role provisioning — the very site ADR-0135's own consequence paragraph names as belonging to cloud) and cloud ADR-0024 §7 ×5 (the ai_seat synthesis).

⚠️ §7 is a judgement, stated plainly rather than buried. It is in neither of ADR-0135's two lists — not restated, and not in the "stays in cloud" table. It is provably not the local record (which has no §7 and no seat concept), and ai_seat appears zero times anywhere under docs/adr/. cloud ADR-0024 §7 is the only spelling that is true; re-pointing it at ADR-0135 would have put a pointer on a record that does not carry the clause. ⛔ No decision was invented — if the reviewer prefers a different disposition, these are 5 lines.

ADR-0081 — nothing is owed, and that is the finding

87 bare citation lines today (the brief said 86). Read per line, the split is not the ADR-0071 shape at all:

Class Lines
Points correctly at the local record (0081-trusted-react-page-tier.md — the kind:'react' tier) 22
Published archive — 8 package CHANGELOGs 41
Governed surface — docs/adr/** (incl. 17 lines that deliberately quote the historical bare label) 21
Identity/org meaning, inside a dated history[] revision entry in docs/qa/platform-checklist/areas/identity-auth.json 1
Points at the wrong record in live code 0
Total 87

The identity re-pointing for 0081 already landed — d1c86a745, "docs(adr): qualify the pre-repo ADR-0081 citations as cloud ADR-0081, letter-checked per site" (#15612, 2026-09-07). Every live ADR-0081 D1 / D2 site across platform-objects, plugin-auth, organizations, verify, plugin-security and publish-smoke.sh already carries the cloud qualifier; bare ADR-0081 D1 in those two package trees, excluding CHANGELOGs, measures zero, with cloud ADR-0081 D1 reading 7 as the firing control.

The one history-entry line is left alone deliberately: docs/qa/platform-checklist/README.md states that run records pin the revision they ran against and that the history exists to make old run records interpretable — the same archive class as a CHANGELOG.

Measurement controls

Every zero carries a firing control and a dark control, because a zero from a dead instrument looks identical to a real one.

Reading Value Firing control Dark control
bare ADR-0024 152 lines / 53 files ADR-0069 → 431 lines, ADR-0079 → 214 ADR-9024 → 0; git grep ADR-0024ZZZ → 0 files
bare ADR-0081 87 lines / 41 files same ADR-9081 → 0; git grep ADR-0081ZZZ → 0 files
bare ADR-0081 D1 in platform-objects + plugin-auth, non-CHANGELOG 0 cloud ADR-0081 D1 → 7 cloud ADR-0081ZZZ D1 → 0

Gate strength provably did not fall — it rose

check:adr-anchors, run on the merge base and on this branch, and every delta accounted:

merge base 1bdbf82cb : 133 decision number(s); 35559 citation(s) across 4538 file(s) resolve; 1022 decision-letter citation(s)
this branch 5e2bfda81 : 133 decision number(s); 35572 citation(s) across 4539 file(s) resolve; 1036 decision-letter citation(s)
  • +13 citations / +1 file = exactly the changeset file this PR adds (7 ADR-0024 + 6 ADR-0135 mentions in its prose). Nothing else in the corpus moved.
  • +14 decision-letter citations = the new ADR-0135 Dk in the two anchored files (auth-manager.ts 10, auth-plugin.ts 4). Those 14 are now letter-checked against ADR-0135's decision index, where the bare ADR-0024 Dk they replaced was checked against nothing.
  • Bare ADR-0024 still resolves exactly as before: the 15 MCP-meaning lines are byte-identical to main.

Ablations — three legs, restores settled by blob hash, never by an exit code

Each mutation is proved on disk by a before/after grep -c on the exact text, read before any result is read. Restores are settled by git hash-object equality with the HEAD blob plus an empty git diff HEAD; the trap was kept only as a crash-path convenience. No permanent test file was left by any leg. These gates read tracked source text, not dist/, so no rebuild sits between mutation and reading.

Leg Mutation Expected Observed
A1 ADR-0135 D5.2 → ADR-0135 D3 in auth-manager.ts red exit 0 — did NOT fire. See the finding below
A2 ADR-0135 D5.2 → ADR-0135 D99 in auth-manager.ts red exit 1 — ADR-0135 D99 is cited by 1 file(s), but ADR-0135 declares no D99
B ADR-0024 §2 → ADR-9024 §2 in mcp-connector.ts (an untouched local-MCP line) red exit 1 — ADR-9024 is cited by 1 file(s) but names no record

A2 is the leg that matters: the letter check does fire on the citations this PR writes, so the 73 re-pointed letters are validated rather than decorative. B shows the resolution check still reads the 15 lines this PR deliberately left alone.

⚠️ Finding surfaced by leg A1 — filed, ⛔ not fixed here

decisionIndexFor() in scripts/check-adr-anchors.mjs harvests a decision letter from any markdown table cell (DECISION_TABLE_CELL). ADR-0135's "What stays in cloud ADR-0024" table has rows | D3 |, | D5.1 |, | D8 |, | D10 | — the table whose whole purpose is to say those clauses are not recorded here. They land in ADR-0135's decision index anyway, so ADR-0135 D3 passes the letter check even though a reader following it lands on a row that explicitly disclaims the clause. That is the same "resolves, but not to what the citation says" family this card is about, one layer down. It is ⛔ not fixed here: the fix has to distinguish a table of decisions a record MAKES from a table of clauses it DISCLAIMS, which is a design call and not a mechanical edit. ADR-0133 has the same shape for cloud 0081 D2. This PR is unaffected — every letter it writes (D4, D5.2, D6, D9) is a real ### Dk heading.

Reverse read — which existing sentence does this change make false?

⛔ Not zero, and all of it lands on docs/adr/**, which this lane may not edit (Prime Directive #14). Filed, not fixed:

  1. docs/adr/0135-*.md:196 — "the clause this repository's code cites as ADR-0024 ②" is present tense and is now false: no code cites ADR-0024 ② any more. This PR is what falsifies it.
  2. docs/adr/0135-*.md consequence paragraph — "auth-manager.ts cites ADR-0024 V1" now reads stale: that site says cloud ADR-0024 V1. Its neighbouring "Until then the citation still resolves to docs/adr/0024-mcp-connectors.md" has had its "until then" end.
  3. The reverse direction — docs/adr/0133-*.md:40 — "The code still carries the pre-repo labels. Several files in packages/platform-objects and packages/plugins/plugin-auth cite ADR-0081 D1 in comments today" was already false on main before this diff, falsified by docs(adr): qualify the pre-repo ADR-0081 citations as cloud ADR-0081, letter-checked per site #15612 on 2026-09-07. Independent of this change.

Statements with a rev or a date attached were checked and are not stale: ADR-0135's "measured 64 citing lines on the commit that introduced it" is anchored to that commit and stays true.

Changeset — measured, not assumed

The only criterion is whether published bytes move; files[] ships dist in all five. Measured against the built tree with a positive control:

Package What moves User-visible?
@objectstack/plugin-auth the operator-facing break-glass refusal detail, and the guard's registration log yes — ⚠️ a deployment grepping that message for ADR-0024 should grep ADR-0135
@objectstack/platform-objects sys_sso_provider field help + protection.reason, and the matching leaf in all four shipped locales yes
@objectstack/spec the ssoDomainVerification doc comment, shipped in dist/ and as src/**/*.zod.ts no behaviour
@objectstack/core, @objectstack/cli doc comments in dist/ only no behaviour

So skip-changeset is wrong here and the label is not applied; a patch changeset naming all five is included.

The four locale files are not hand-edited generated output

packages/platform-objects/src/apps/translations/*.objects.generated.ts carry the ADR-0024 ② citation in a translated help leaf. Their own header is the rule: "Edit translations in place… Merge only fills gaps: correcting a source label/description does not push the correction into a leaf here that already holds a translation… Re-translate it by hand when its source changes." Re-running the extractor would have been a no-op on these four leaves. The producer (sys-sso-provider.object.ts) was corrected as well, and check:i18n-coverage reports 13 config(s), 621 baselined untranslated string(s), none new.

No generated reference doc carries ADR-0024 (grep over content/docs/references/ reads 0), so nothing under content/docs/references/ needed regenerating — the spec site changed is a /** */ doc comment, not a .describe() string.

Verification

  • Gates: 117 of 117 derived families accounted, 0 NOT-MEASURED, 0 UNRUN. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RECORDFILE at 5e2bfda81, every exit code captured before any pipe and recorded as COMMAND :: exit CODE (the literal word exit, then the numeric code). Three families first returned exit 3 / exit 1 PREREQUISITE NOT MET because sibling packages had no dist (check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage); after a full pnpm build all three were re-run whole and are green. ⛔ None of the three is counted as a finding or as a green measurement in its refused state.
  • Also run, outside the derivation: check:adr-anchors, check:adr-links, check:adr-symbol-anchors — all green.
  • Tests: spec 477 files / 13611 tests · core 51 / 1316 · platform-objects 40 / 575 · plugin-security 113 / 2181 · plugin-auth 111 / 2360 — 792 files, 20043 tests, 0 failures (VERDICT command-exit 0, 464s under the shared verify lock). @objectstack/cli --project unit: 207 files / 2971 tests, 0 failures. The integration tier is declared to CI — this diff touches no spawn entry, only a comment in serve.ts.
  • Typecheck: spec, core, platform-objects, plugin-auth, plugin-security, cli — VERDICT command-exit 0, 0 error TS. ⚠️ An earlier run of the same command reported TS7016 against packages/spec/dist/**; that run raced the concurrent gate sweep over dist/ and is reported as the artefact it was, not as a finding — the clean re-run after pnpm build is the reading.
  • Lint: not narrowed. The full union eslint . --no-inline-config --format json at 5e2bfda81: 6756 files eslint itself selected, 0 errors, 0 warnings. eslint.config.mjs carries no parserOptions.project and no typed rules, so no edit here could move a verdict on an untouched file.
  • Build: full pnpm build green before the final measurements.

Why this is Part of and not Fixes

Segment 2 is done. Segment 3 is measured to owe zero edits, but that is a reading a human should ratify rather than a seat closing the card on its own — and the residue the card still names is the three docs/adr/** sentences above, a governed surface this lane ⛔ may not touch. Closing is the PM's call.

⛔ Draft on purpose. ⛔ No auto-merge armed, not flipped out of draft, not queued.


Generated by Claude Code

… records that decide them

The number `ADR-0024` names two unrelated decisions from this repository's point
of view. `docs/adr/0024-mcp-connectors.md` is the MCP-as-connectors record; the
identity surface's citations mean `objectstack-ai/cloud` ADR-0024, whose open
mechanism half is mirrored here as ADR-0135 and whose commercial half stays in
cloud.

Read per citation line, never per path:

- 79 lines re-pointed. 73 mean a clause ADR-0135 restates (D4, D5.2, D6, D9) and
  now name it with its letter; 6 mean a clause ADR-0135 deliberately leaves in
  cloud (`V1`, the SSO default-role provisioning, and `§7`, the `ai_seat`
  synthesis) and now carry the gate's cross-repo qualifier `cloud ADR-0024`.
- 15 lines that mean the local MCP record are untouched.
- 36 CHANGELOG lines and 22 lines under `docs/adr/` are untouched: published
  archive and a governed surface respectively.

The `ADR-0024 ②` spelling becomes `ADR-0135 D6` — ADR-0135 D6 names that clause
as the one this repository's code cites that way.

The four locale files under `platform-objects/src/apps/translations/` are edited
at the leaf, which is the route their own header prescribes: `os i18n extract
--merge` only fills gaps and never pushes a source correction into a leaf that
already holds a translation.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
…inting

Published bytes move in five packages, measured rather than assumed: the
operator-facing break-glass refusal detail and the guard's registration log in
`@objectstack/plugin-auth`; `sys_sso_provider`'s field help, its
`protection.reason` and all four shipped locale bundles in
`@objectstack/platform-objects`; the `ssoDomainVerification` doc comment in
`@objectstack/spec`, published both in `dist/` and as the `src/**/*.zod.ts`
entry its `files[]` ships; and doc comments only in `@objectstack/core` and
`@objectstack/cli`.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:system tests tooling labels Sep 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/cli, @objectstack/core, @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/spec, touching 18 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/cli/src/utils/merge-boot-config.ts, packages/plugins/plugin-auth/src/auth-schema-config.ts, packages/plugins/plugin-auth/src/member-role-canonical.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

23 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 1bdbf82cb58119132d667713ae35099f388a3479.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/cli/src/utils/merge-boot-config.ts, packages/plugins/plugin-auth/src/auth-schema-config.ts, packages/plugins/plugin-auth/src/member-role-canonical.ts, …) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (symbol, 36 pages)
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 149 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 1bdbf82cb58119132d667713ae35099f388a3479 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 69efdbed9814ea14a93ccace1f43be6541ff5fe8 — the merge of head 5e2bfda81c2e06f053b02a9a9fad68437472960e into base 1bdbf82cb58119132d667713ae35099f388a3479, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 69efdbed9814ea14a93ccace1f43be6541ff5fe8 && git checkout 69efdbed9814ea14a93ccace1f43be6541ff5fe8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1bdbf82cb58119132d667713ae35099f388a3479 5e2bfda81c2e06f053b02a9a9fad68437472960e && git checkout -B drift-repro 1bdbf82cb58119132d667713ae35099f388a3479 && git merge --no-ff 5e2bfda81c2e06f053b02a9a9fad68437472960e

node scripts/docs-audit/affected-docs.mjs --json 1bdbf82cb58119132d667713ae35099f388a3479

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 1bdbf82cb58119132d667713ae35099f388a3479 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@claude
claude Bot marked this pull request as ready for review September 14, 2026 22:32
@claude
claude Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 0f95f43 Sep 14, 2026
37 checks passed
@claude
claude Bot deleted the claude/issue-14361-adr-0024-0081-citations branch September 14, 2026 23:02
os-elon-musk pushed a commit that referenced this pull request Sep 16, 2026
…s grep recipe

Two stale artefacts in the identity mirror record, neither of which survives on
its own: the Consequences bullet still describes a transition that ended, and
the Provenance grep recipe reads like a per-site population filter it never was.

The Until then sentence is kept byte-identical and marked as history. Rewriting
a mirror record's Consequences prose is a governance judgment for the approver,
not a dev edit, so the two original sentences stand as the record of the state
this decision was taken in, with the marking above them and the reading below.
The reading: #14361's re-pointing landed 2026-09-14 in #18228, and on 8b81ab6
all 14 ADR-0024 occurrences left in packages/plugins/plugin-auth/src and
packages/platform-objects/src/identity carry the cloud ADR-0024 qualifier -
firing control ADR-0135 62 occurrences in the same two trees, dark control zero.

The recipe itself is unchanged - it is true in its own sentence - and gains one
sentence of scope: it counts LINES on the one historical commit that introduced
this record, and a line can carry two citations while a citation can wrap across
two lines, so the shape mis-sorts in both directions the moment it is reused as
a census of citation sites. That reuse already happened twice.

No other citation in the file is touched.

Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…ode now says (objectstack-ai#18344)

Fixes objectstack-ai#14361

Third and final segment. Segments 1 and 2 (PR objectstack-ai#18228, last commit
`0f95f4341`) handled the 152 citations under `packages/**` and
`content/docs`. This segment is the remainder: every unqualified
`ADR-0024` under `docs/adr/`.

Clause-②: no

## The instrument is wrong in both directions

The dispatch handed me a population of **21 bare lines in 7 files**,
measured as "lines matching `ADR-0024`, minus lines containing `cloud
ADR-0024`". I reproduced that 21 exactly. Then I re-measured per
**occurrence** instead of per **line**:

| instrument | count |
|:--|--:|
| `ADR-0024` occurrences under `docs/adr/` | 35 |
| lines matching, minus lines containing `cloud ADR-0024` | 21 |
| occurrences whose preceding token is not `cloud` (backticks allowed) |
**18** |

- **4 false positives.** ADR-0135 lines 3, 31, 232, 271 spell the
qualifier with backticks in the middle — `` `objectstack-ai/cloud`
ADR-0024 `` and `` `cloud` ADR-0024 ``. The literal-string filter does
not see the qualifier. They are qualified and correct.
- **1 false negative.** ADR-0135 line 248 begins `` `ADR-0024 V1` `` and
*also* contains `` `cloud ADR-0024` `` later in the same line, so `grep
-v` discarded the whole line — hiding a bare citation. It is one of the
two defects below.

`21 − 4 + 1 = 18`. A third artifact is invisible to any line filter:
ADR-0135 line 79 begins `ADR-0024 chose the other direction`, and its
qualifier `cloud` is the last word of line 78. Line-wrapped, therefore
qualified.

Controls for every zero reported here are in the dev report.

## Verdicts — 18 occurrences, each checked against the other end

The D-letter test that segments 1 and 2 used has **zero** resolving
power here (0 of 18 carry a `D` clause letter), so every row is a read
of the cited record.

### Local `docs/adr/0024-mcp-connectors.md` — 10 sites, all correct

| site | claim made | other end | verdict |
|:--|:--|:--|:--|
| `0023-openapi-to-connector-generator.md:148` | "the complementary path
— wrapping live MCP servers as connectors" | explicit link
`./0024-mcp-connectors.md`; title is *MCP Servers as Connectors* |
correct |
| `0024-mcp-connectors.md:1` | the record's own H1 | self | correct |
| `0025-plugin-package-distribution.md:7` | Related list, labelled
"(connectors)" | explicit link | correct |
| `0025-plugin-package-distribution.md:395` | "ADR-0024 (MCP
connectors), any integration describable by OpenAPI/MCP is a declarative
connector config" | ADR-0024 TL;DR: one adapter, "no per-server code" |
correct |
| `0025-plugin-package-distribution.md:529` | Related list | explicit
link | correct |
| `0036-app-as-rest-api-and-mcp-server.md:5` | "MCP as connectors — the
*inbound* sibling: ObjectStack **consuming** external MCP servers" |
ADR-0024 section 1 ships `@objectstack/connector-mcp`, an adapter that
connects *to* MCP servers — inbound | correct |
| `0097-declarative-connector-instances.md:5` | Builds-on, "(MCP servers
as connectors)" | explicit link | correct |
| `0097-declarative-connector-instances.md:199` | "sandboxed execution
remains the enterprise tier (ADR-0024 §4)" | ADR-0024 `### 4.` tier
table, enterprise row, reads "sandboxed stdio execution" | correct —
exact |
| `0097-declarative-connector-instances.md:205` | "MCP credentials ride
the transport (ADR-0024)" | ADR-0024 `### 3.` "Credentials and config
live with the MCP server… transport `env`/`headers` carry credentials" |
correct — exact |
| `0101-mcp-stdio-principal-admission.md:5` | "ADR-0024 §4 (trust
model)" | `### 4.` is the open/enterprise boundary carrying the trust
assumption | correct |

The two section citations are also structurally disambiguating: the
local record numbers its sections `### 1.` … `### 5.`, while cloud
ADR-0024 letters its clauses `D1…D10`/`V1`. A `§4` cannot be a cloud
clause.

### `docs/adr/0135-identity-and-access-architecture.md` — 8 sites, 6
correct, 2 fixed

| line | text | kind | other end | verdict |
|:--|:--|:--|:--|:--|
| 54 | "A bare `ADR-0024` in this repository's code does NOT resolve to
this record" | **mention** | `0024-mcp-connectors.md` exists and is
unrelated; `0071` here is *Dataset semantic-layer depth* as the same
sentence claims | correct — adding `cloud` inverts it |
| 63 | `git grep -n -E "ADR-0024" -- packages/…` | **literal** | re-ran
the command verbatim: 14 lines, 0 unqualified. The sentence is
explicitly scoped "on the commit that introduced it… measured 64", so it
is historical and still true | correct — editing it breaks the command |
| 79 | `ADR-0024 chose the other direction` | **use** | `cloud` is the
last word of line 78 | correct — line-wrap artifact |
| **196** | "the clause this repository's code cites as `ADR-0024 ②`" |
**mention** | the live source cites `ADR-0135 D6` — `auth-manager.ts`
2793/3667/6654, `auth-plugin.ts` 2796, `register-sso-provider.ts` 316,
`auth-route-ledger.ts` 238–239. `ADR-0024 ②` survives only in CHANGELOGs
| **fixed** |
| 246 | "a bare `ADR-0024` citation in this tree is not mechanically
re-pointable" | **mention** | — | correct |
| **248** | "`auth-manager.ts` cites `ADR-0024 V1`" | **mention** |
`auth-manager.ts:3661` reads `cloud ADR-0024 V1` | **fixed** |
| 260 | "Whether any individual bare `ADR-0024` citation should move" |
**mention** | — | correct |
| 265 | "A reader who follows an `ADR-0024` citation out of
`plugin-auth`" | **mention** | — | correct |

## The two fixes

Both are present-tense descriptions of **what this repository's code
says**. Both were true when the record was written and were made false
by segments 1 and 2 of this same card.

**Line 196.** Under either reading the line was wrong. Read as a
quotation of the code, it quotes a string the live source no longer
contains. Read as naming the cloud clause, it is a bare cloud citation,
which Prime Directive objectstack-ai#13 forbids and which resolves against *this*
registry to `0024-mcp-connectors.md` — a record numbered 1–5 with no
clause ②. The fix names the cloud clause *and* the spelling the code now
uses, so both readings land.

**Line 248.** A one-token insert. The same sentence already prescribes
"those keep the `cloud ADR-0024` spelling"; `auth-manager.ts` now does,
and the quotation did not.

Everything else is untouched. No citation that was already correct was
moved.

## Left alone, deliberately

ADR-0135's Consequences bullet (line 265 ff.) reads "**Until then** the
citation still resolves to `docs/adr/0024-mcp-connectors.md`". Its
antecedent has now passed: the verbatim grep on line 63 returns 0
unqualified citations in `plugin-auth/src` and
`platform-objects/src/identity`. The **citation** on that line is a
correct mention, so it is not a defect of the kind this card is about;
the surrounding tense is merely spent, and rewriting a mirror record's
Consequences prose is a maintainer's call, not a per-site citation call.
Flagged rather than edited — say the word and it moves in this PR.

## 维护者速读(草稿)

**改了什么** —— `docs/adr/0135-identity-and-access-architecture.md`
两处散文里对「本仓代码怎么引用」的描述,改成代码现在真正写的样子。共 7 增 6 删,只动这一个文件。

**为什么改** —— 这两句话在写下时是真的,是 objectstack-ai#14361 前两段自己把它们变假的。196 行说域名验证子句「本仓代码引作
`ADR-0024 ②`」,而活代码七处已经引 `ADR-0135 D6`,`ADR-0024 ②` 只剩在 CHANGELOG
里;它同时还是一个裸的 cloud 引用,按 Prime Directive objectstack-ai#13 会解析到本仓的
`0024-mcp-connectors.md`,而那份记录分节是 1–5,根本没有子句 ②。248 行把 `auth-manager.ts`
引述成 `ADR-0024 V1`,文件里写的是 `cloud ADR-0024 V1` —— 恰是同一句话下半段要求保留的拼写。

**风险与代价(含回滚)** —— 纯散文,零代码、零发布物。`docs/adr/**` 不在任何包的 `files[]` 里(70 个声明了
`files[]` 的包全部为 0 命中),所以无 changeset。回滚 = revert 这一个
commit,没有任何下游。唯一的判断风险是 196 行的改写:它比 248 行多改了几个字,如果您认为镜像记录应当只记 cloud
的字母而不提本仓代码今天的拼写,把它退回成 `` `cloud ADR-0024` 的子句 ② `` 即可,一行的事。

**席位意见** ——

**你要做的** —— 这是治理面,需要您手工批准并合并;席位不入队、不武装 auto-merge。请特别看一眼「Left alone,
deliberately」那一节:ADR-0135 Consequences 里的 "Until then"
已经过期,我没有动它,因为改镜像记录的 Consequences 散文不该由 dev 自己决定。您说改我就在本 PR 里改。

## Acceptance notes

- `docs/adr/**` is a governed surface. Draft, maintainer-merged. Not
queued, auto-merge not armed.
- **Changeset: skipped, measured.** 70 workspace packages declare
`files[]`; 0 of them ship any `docs`/`adr` path, and `docs/` contains no
`package.json`, so `docs/adr/**` is not inside a workspace package at
all. Fire control on the same scan: all 70 ship a `dist` entry. Nothing
published moves.
- Population after this PR: 35 occurrences, 19 qualified, 16 unqualified
— all 16 verdicted correct above.
- `noted, not filed:` the "Until then" tense in ADR-0135's Consequences,
above. Carrier named: this PR's reviewer.
- `noted, not filed:` the line-based `grep -v 'cloud ADR-0024'` recipe
is quoted inside ADR-0135 line 63 itself and mis-bins 5 of 21 sites. It
is correct *as written there* (that sentence counts citing lines at a
past commit), so there is nothing to fix; but anyone reusing that recipe
as a population filter will inherit the same 4 false positives and 1
false negative. Carrier: none — no open PR or queued card reuses it.

## Verification

All 19 gate families derived by `scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` (which takes the change
set from the merge base itself) are green at `c4d13026b`.
`check:doc-formula-expressions` first exited **3 — PREREQUISITE NOT
MET**, which is not a finding; after `turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint` it exits 0.

`pnpm lint` is not owed and is not narrowed — it is **not measured
because the population is empty**. eslint's own verdict on the only file
in this diff is `File ignored because no matching configuration was
supplied`, `eslint.config.mjs` contains no markdown entry, and line 328
of that config records that no `parserOptions.project` and no typed
rules are enabled, so this diff cannot move any untouched file's
verdict.

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…s grep recipe's scope (objectstack-ai#18399)

Fixes objectstack-ai#18345

ADR-0135 carried two stale artefacts, and neither would have survived on
its own: both were
tied to work tracked by card objectstack-ai#14361, which PR objectstack-ai#18344 retired on
2026-09-16. This card exists
to outlive that retirement, and this PR is the whole of it — two
passages, nothing else in the
file touched.

## 1. The `Until then` bullet is MARKED as history, not rewritten

The Consequences bullet described a transition that has ended. Following
the seat's direction,
the four original lines are kept **byte-identical** — rewriting a mirror
record's Consequences
prose is the governance judgment the approver makes, not a dev edit. The
bullet gains a history
marker above them and the reading below them, so a reader cannot reach
the stale sentences
without first being told they are historical.

**One correction to the dispatch's suggested wording, measured rather
than assumed.** The
re-pointing did NOT land together with card objectstack-ai#14361's retirement on
2026-09-16. It landed on
2026-09-14 in PR objectstack-ai#18228 (merge commit `0f95f434`, merged
2026-09-14T23:02:43Z). Card objectstack-ai#14361
itself then stayed open two more days and was retired by PR objectstack-ai#18344 at
2026-09-16T06:27:30Z. The
text I added names **the landing**, not the card's retirement, because
the landing is what made
the `Until then` state end.

Reading, taken on `objectstack-ai/objectstack` at `8b81ab60` (the branch
point):

| measurement | count |
|:---|---:|
| `ADR-0024` occurrences in `packages/plugins/plugin-auth/src` +
`packages/platform-objects/src/identity` | 14 |
| of those, occurrences carrying the `cloud ADR-0024` qualifier | 14 |
| **bare, unqualified occurrences — the ones the bullet is about** |
**0** |
| firing control: `ADR-0135` occurrences in the same two trees | 62 |
| dark control: a string that must not exist there | 0 |

The card's own reading was `ADR-0024 ②` = 0 with `ADR-0135 D6` = 12
files as the firing control;
both reproduce here. The occurrence-level measurement above is stronger
than the marker count:
it shows there is no bare citation left at all, not merely that one
spelling is gone. Every one
of the 14 was checked with its left context, so the count does not rest
on a line filter — which
is exactly the instrument error item 2 is about.

## 2. The grep recipe is UNCHANGED and gains one sentence of scope

The recipe in Provenance point 3 is true in its own sentence and is not
edited. What it lacked
was its range, so it read like a per-site population filter and was
reused as one twice. One
sentence now states what it counts (LINES, on the one historical commit
that introduced this
record) and why the shape cannot be reused as a census of citation
sites: a line can carry two
citations and a citation can wrap across two lines, so sorting lines by
the `cloud ADR-0024`
spelling mis-sorts in both directions.

## Acceptance notes

- **The card declared one gap unmeasured; it is now measured.** The card
said it had never
checked whether a THIRD stale conditional sits elsewhere in this file.
Swept on `e29b3af7`
for transition-shaped prose — `Until then`, `for now`, `not yet`, `once
#`, `pending`,
`in the meantime`, `temporarily`, `soon` — with a dark control. The only
hits are the bullet
  this PR already marks. **There is no third occurrence in this file.**
- The card's other unmeasured item — where else the line-vs-occurrence
error has skewed
readings OUTSIDE `docs/adr/` — is still unmeasured, and this PR does not
touch it.
- `turbo ls --affected` was run and is NOT cited as evidence anywhere
here: it printed all 81
packages rather than narrowing, so it carries no information about this
diff.
- No other citation in the file is touched. The diff is 11 insertions, 1
deletion, one file.

## Verification

All 18 gate families derived for this diff, green, run with the exit
code captured before any
pipe. Derivation is mechanical, not copied from the dispatch:
`dispatch-gates.mjs --commands`
against this tree at `e29b3af7`, reconciled afterwards with `--ran`.

```
18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN  (a DERIVED zero - all 18 recorded an exit code)
```

check-adr-links · check-adr-links --self-test · check-adr-symbol-anchors
·
check-adr-symbol-anchors --self-test · check-ci-filter-parity ·
check-closing-keyword-parity ·
check-closing-keyword-parity --self-test · check-comment-mask-corpus ·
check:doc-formula-expressions ·
check:adr-anchors · check:cross-package-test-inputs ·
check:doc-authoring · check:driver-memory-census ·
check:nul-bytes · check:pm-governed-merges · check:pm-prior-rulings ·
check:refd-timer-probe ·
check:watch-hint-literal — all exit 0.

`check:doc-formula-expressions` first answered exit 3, PREREQUISITE NOT
MET, which its own text
declares measures nothing. It was re-run to exit 0 after building
`@objectstack/formula` and
`@objectstack/lint`, and only the re-run is recorded as a result.

Governed-surface assertion: `check-governed-merges.mjs --test` on the
file answers **exit 3,
GOVERNED** — `docs/adr/**`, rules layer.

**Repo-wide lint is not owed here, and that is a measurement rather than
a skip.** Three pieces:
1. Population, read from eslint's own config: every `files:` block in
`eslint.config.mjs`
declares only `{ts,tsx,mts,cts,js,jsx,mjs,cjs}`, and the config mentions
markdown zero times.
2. Count, read from eslint's own `--format json`: run against the one
changed file, eslint
answers `File ignored because no matching configuration was supplied` —
0 of 1 changed files
   are inside its population.
3. Invariance for untouched files: no `parserOptions.project` and no
`projectService`, so
type-aware linting is not enabled and this diff cannot move any
untouched file's verdict.

No package is affected: the workspace globs are `packages/*`, `apps/*`,
`examples/*` and
friends, and `docs/` is under none of them — so no build, no package
test and no typecheck is
owed, and this PR declares none.

`skip-changeset` is measured, not assumed: of 77 package manifests,
**0** ship `docs/` or `adr`
in `files[]`, against a positive control of 70 that ship `dist`. Nothing
published moves.

## 维护者速读(草稿)

**改了什么** — 一个文件、两段话、11 行加 1 行减。① Consequences 里那条描述"过渡期"的
bullet:原来的四行**一个字节都没动**,只在它上面加了一行历史标记、下面加了一句实测读数。
② Provenance 里那条 grep 配方:**配方本身一个字符都没改**,只在它后面补了一句说明它数的是什么。
文件里其它任何引用都没碰。

**为什么改** — 这两处都是 objectstack-ai#14361 完工之后留下的过期物,而且**留不住**:它们原本挂在
objectstack-ai#14361 上,那张卡已经被 PR objectstack-ai#18344 收掉了,不单独接出来就会一起消失。①的问题是读者会读到一个
今天已经不存在的过渡状态,并信以为真;②的问题更实:那条配方读起来像一个通用的"逐站点筛选器",
已经被两个席位、三份文档当成筛选器复用过,而它在这个文件上两个方向都会数错。

**风险与代价(含回滚)** — 风险极低。这是纯散文改动,不含任何代码、schema、行为或已发布内容;
77 个包里没有一个把 `docs/` 或 `adr` 放进 `files[]`,所以对 npm 消费者零影响。18 个门禁族全绿。
回滚就是 revert 这一个 commit,没有任何下游产物需要一起回退、也没有迁移要撤。唯一的判断题在①:
本 PR 选择"标注为历史"而不是"改写成现在时" —— 因为改写一份镜像记录的 Consequences 属于治理判断,
应当由批准人来做,不该由 dev 塞进来。若您认为应当直接改写成现在时,那是您的判断,不是本 PR 的缺口。

**席位意见** — (留空,待席位定稿)

**你要做的** — 读①的那条 bullet 和②的那句量程说明,确认措辞。**特别请确认一处事实修正**:
派发建议的措辞写的是"objectstack-ai#14361 于 2026-09-16 落地",实测是**不对的** —— 真正的重指工作是
2026-09-14 由 PR objectstack-ai#18228 落的地,objectstack-ai#14361 这张卡本身多开了两天、2026-09-16 才由 PR objectstack-ai#18344 收掉。
本 PR 的正文写的是**落地日**。确认无误后,这是治理面(`docs/adr/**`),需要您本人批准并由您合入;
⛔ 本席不翻 ready、不入队、不武装 auto-merge。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant