Skip to content

Commit 6c5bef5

Browse files
docs(adr): ADR-0128 §4 dated note — the keyed-digest break did not carry D1, and why (#21343)
Fixes #21327 Clause-②: no ADR-0128 §4 ("The trigger that would fund it") gets one dated sentence under its third trigger, "`ICryptoProvider` is opened for another breaking change". The sentence records three things. The keyed-digest break (#21263, PR #21292) met that trigger and landed without D1, by the maintainer's ruling A (comment 5945612493 on #21263). The reason is that the director's census (comment 5945420994) measured zero out-of-repo `ICryptoProvider` implementations in the organisation's repositories. And D1 is scheduled on its own as #21326. ⛔ D1–D4, the status line and every other section are unchanged. No code and no changeset: `docs/adr/**` publishes nothing. ## The change - **File:** `docs/adr/0128-producer-discriminated-aad-for-cryptocontext.md`, one added line. Line count 130 → 131 (+1 / −0). - **Placement:** an indented blockquote inside the third trigger's list item. It renders under that bullet, and the fourth trigger stays a list item. Checked by rendering lines 97–106 with micromark 4.0.2 from this workspace: the note comes out as a `blockquote` inside the third `li`, followed by the fourth `li`. - **Form:** `> **Note (2026-10-02).** …`. ADR-0128 carried no dated note before, so the form is the most common inline dated-note label in `docs/adr/` at `222ecc27`: 5 blockquotes open with "Note (YYYY-MM-DD", against 4 "Amended (", 3 "Revision note" and 1 "YYYY-MM-DD note". "Amended" was not used, because the note amends no decision. - **Links:** the two forms this ADR already uses, `[#N](…/issues/N)` and `comment [id](…/issues/N#issuecomment-id)`, plus `PR [#21292](…/pull/21292)`. All are absolute GitHub URLs, so `check-adr-links` (relative links only) has nothing new to resolve. The sentence as landed, links shown as plain text: > **Note (2026-10-02).** This trigger was met by the keyed-digest break (#21263, PR #21292: a required `keyedDigest` member on `ICryptoProvider`), which landed without D1 by the maintainer's ruling A (comment 5945612493) because the director's census (comment 5945420994) measured zero out-of-repo `ICryptoProvider` implementations in the organisation's repositories, so pulling D1 in would have saved a second breaking-export change for no measured implementer population; D1 is scheduled on its own as #21326. ## Premise check (against `origin/main` at `222ecc27`) - **PR #21292 is merged as `222ecc27`**, a squash with one parent (`bada8d35`). Its diff adds the required member `keyedDigest(plain: string)`, an asynchronous method that yields a string, to `ICryptoProvider` in `packages/spec/src/contracts/crypto-provider.ts`, and `LocalCryptoProvider` implements it. That is the "opened for another breaking change" the third trigger names. - **It carries no D1.** In the 414-line `git show` of `222ecc27`, zero added or removed lines match `aad|discriminant|CryptoContext|scope` (case-insensitive). The positive control is 40 such lines matching `keyedDigest`. On the merged tree, `CryptoContext` is still `{ namespace, key, tenantId? }`, and `LocalCryptoProvider.aadOf` still joins `namespace` and `key` with an unescaped separator. - **The sources the sentence cites:** comment 5945420994 (the census) and comment 5945612493 (the ruling) are on #21263, and both read as the card quotes them. #21326 is open. ## Verification Everything ran at HEAD `0524a7d9`, the only commit on this branch, over base `222ecc27`. `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 19 commands from the branch's change set, the same 19 as at dispatch. All 19 ran, and each exit code was captured before any pipe. | Command | Exit | The gate's own verdict line (excerpt) | |:--|:--|:--| | `node scripts/check-adr-links.mjs` | 0 | ✅ 695 relative link destination(s) under docs/adr/ resolve | | `node scripts/check-adr-links.mjs --self-test` | 0 | ✅ discrimination, census, ADR-0046 pin and baseline staleness all verified | | `node scripts/check-adr-symbol-anchors.mjs` | 0 | ✅ 2166 anchors across 140 records resolve | | `node scripts/check-adr-symbol-anchors.mjs --self-test` | 0 | ✅ every finding class provoked, healthy anchors silent | | `node scripts/check-ci-filter-parity.mjs` | 0 | OK: all 192 declared cross-package glob(s) are covered | | `node scripts/check-closing-keyword-parity.mjs` | 0 | OK (3 parsers agree on all 9 keywords) | | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | ✓ 40 assertions, 5 mutations each driven to red | | `node scripts/check-comment-mask-corpus.mjs` | 0 | ✓ 7914 files, 0 disagree, 0 unparseable | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 | ✓ 22 record-scoped formula example(s) across 459 files judged clean | | `pnpm check:adr-anchors` | 0 | OK (57 anchored file(s), 134 decision number(s)) | | `pnpm check:cross-package-test-inputs` | 0 | OK: 30 package(s) read outside themselves, all declared | | `pnpm check:doc-authoring` | 0 | ✓ 17158 customer-facing string(s) clean | | `pnpm check:driver-memory-census` | 0 | OK — every declaration is ledgered | | `pnpm check:gitlink-declared` | 0 | OK (0 gitlink(s) at mode 160000) | | `pnpm check:nul-bytes` | 0 | OK (9636 text file(s); no raw ASCII control bytes) | | `pnpm check:pm-governed-merges` | 0 | ✓ 454 assertions; the real generator certified this tree | | `pnpm check:pm-prior-rulings` | 0 | ✓ 155 cases pass | | `pnpm check:refd-timer-probe` | 0 | OK, 7909 source file(s) swept | | `pnpm check:watch-hint-literal` | 0 | ✓ 72 declaration(s) across 4 rostered name(s) | - **Reconciliation:** `dispatch-gates.mjs --ran`, given the 19 commands each with its exit code, printed "Run reconciliation — 19 derived, 19 run, 0 NOT-MEASURED, 0 UNRUN" and "a DERIVED zero — all 19 recorded an exit code". - **Build before the formula gate:** that gate imports the built `@objectstack/lint` and `@objectstack/formula`. So `pnpm --workspace-concurrency=2 --filter '@objectstack/lint...' build` ran first under `scripts/pm/os-verify-lock.sh` ("VERDICT command-exit 0"). `git status --porcelain` was empty after the build. - **NOT MEASURED locally, CI's to run:** the families `dispatch-gates` lists outside its 19 commands, which are the artifact rosters, the wide-population families, the path-scheduled `Test Core` job and the type-check lanes. This diff touches no TypeScript and no package. ## Acceptance notes - **Why the sentence is scoped as it is.** The census states its own limit: implementers outside the organisation, who consume the published `@objectstack/spec` from npm, cannot be measured from there. So the sentence says "in the organisation's repositories" and "no measured implementer population", not "no implementers". - **Nothing filed** from this diff. ## Landing This is Tier H (`docs/adr/**`). The PR stays a draft and is held for the maintainer's own approval, as the ruling says. This seat does not mark it ready, queue it or arm auto-merge. ## 维护者速读(草稿) **改了什么**:ADR-0128 §4「触发条件」第三条(`ICryptoProvider` 再有破坏性变更)下面,加了一句带日期(2026-10-02)的注。注里说:密钥化摘要那次破坏性变更(#21263 / PR #21292)已经触发了这一条,但没有顺带做 D1。只加了一行;D1–D4、状态行和其它章节一个字都没动。 **为什么改**:ADR 原文写着「再有破坏性变更时,应把 D1 一起带上」,而您批的裁决 A 让 #21292 单独落地了。不留这句注的话,以后读 ADR 的人会以为是漏了 D1。注里写明了理由:总监席普查了组织内的所有仓库,仓外的 `ICryptoProvider` 实现数为零,所以捆绑 D1 想省下的「第二次破坏」没有实际的承受方。D1 已经单独排期,在 #21326。 **风险与代价(含回滚)**:纯文档改动,只有一行。不发包,不影响任何运行时行为。普查只覆盖组织内的仓库,从 npm 引用的外部实现方是测不到的,所以注里的措辞限定为「组织内的仓库」和「没有实测到的实现方」。回滚就是 revert 这一行。 **席位意见**:(留空,由席位定稿时填写) **你要做的**:读一下这句注的措辞;没问题的话,在本 PR 上点 Approve(Tier H,需要您本人批准)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01FNKm1SmPpuJASnbjxWGtsJ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 04f0cc4 commit 6c5bef5

1 file changed

Lines changed: 1 addition & 0 deletions

File tree

‎docs/adr/0128-producer-discriminated-aad-for-cryptocontext.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Any **one** of these turns the deferral over; none requires re-opening the direc
101101
- **The hazard stops being hypothetical**: any intersection appears between the settings-namespace set and the set of object names carrying a `Field.secret()` field — in shipped platform metadata, in an example app, or in a reported customer deployment. §1.4's "no intersection" is the whole of the deferral's evidence, and it is a fact with a shelf life.
102102
- **A fourth producer of `CryptoContext` is added**, or an existing one's vocabulary is widened to accept names it does not control. A fourth vocabulary in a flat space is the same defect with more surface, and the discriminant is far cheaper to introduce *with* the new producer than after it.
103103
- **`ICryptoProvider` is opened for another breaking change.** The migration is the expensive half; the breaking-export half should be paid once. A queued breaking change to this interface should pull D1 in with it.
104+
> **Note (2026-10-02).** This trigger was met by the keyed-digest break ([#21263](https://github.com/objectstack-ai/objectstack/issues/21263), PR [#21292](https://github.com/objectstack-ai/objectstack/pull/21292): a required `keyedDigest` member on `ICryptoProvider`), which landed without D1 by the maintainer's ruling A (comment [5945612493](https://github.com/objectstack-ai/objectstack/issues/21263#issuecomment-5945612493)) because the director's census (comment [5945420994](https://github.com/objectstack-ai/objectstack/issues/21263#issuecomment-5945420994)) measured zero out-of-repo `ICryptoProvider` implementations in the organisation's repositories, so pulling D1 in would have saved a second breaking-export change for no measured implementer population; D1 is scheduled on its own as [#21326](https://github.com/objectstack-ai/objectstack/issues/21326).
104105
- **A compliance or customer requirement asks the platform to state its at-rest ciphertext binding.** The honest present-tense answer is §1's, and an organisation that needs a stronger one funds the work.
105106

106107
## 5. Alternatives considered

0 commit comments

Comments
 (0)