Skip to content

docs(adr): ADR-0015 §8 binds its example credential with a sys_secret: handle, the only form the shipped binder resolves - #27906

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-ti9z26
Oct 30, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-ti9z26

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

Fixes #27688

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:ADR-0015 第 8 节端到端示例的凭据引用,从 secret:warehouse/readonly 改为 sys_secret:9f2c… 句柄形态;示例下加一条带日期的修订注,原句照录,不是静默改写;文件头 Amended by 行指向这条注。
  • 为什么改:平台只认密钥库铸出的 sys_secret: 句柄(Setup → 数据源连接表单的密钥栏写入)。照抄旧示例写出的引用能通过校验,但启动时解析不到,数据源一直连不上。PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656 已改好 schema 提示和 drivers 文档,这个 ADR 是最后一处还在教旧写法的地方。
  • 风险与回滚:只改一个 ADR 文件(+22/−2),不发布任何包,不动代码、门禁和 §10 正文;回滚即 revert 这一个提交。
  • 先看:docs/adr/0015-external-datasource-federation.md 第 8 节第一个代码块和紧随其后的修订注,再看文件头第 7 行。
  • 两分钟验证:grep -n "credentialsRef: '" docs/adr/0015-external-datasource-federation.md,预期两行:示例里的 sys_secret:9f2c…,和修订注里照录的旧句。
  • 需要你做的:暂无。contract review owed(契约复审未完成,席位复审后再求批);本 PR 改 ADR,落地为 Tier H,由维护者批准。

What changes

One file, docs/adr/0015-external-datasource-federation.md (+22 / −2).

  • §8 example. credentialsRef: 'secret:warehouse/readonly' becomes credentialsRef: 'sys_secret:9f2c…', with a three-line comment that says where the handle comes from. The comment mirrors the one PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656 put in examples/app-showcase/src/system/datasources/showcase-external.datasource.ts.
  • A dated amendment note right under that code block, in the house form of ADR-0099's 2026-10-23 note and ADR-0097's notes: the text is corrected in place and the note records what it replaced. The note quotes the original line, says why it fails (it parses, but nothing shipped resolves it), names what the binder resolves and where (packages/services/service-datasource/src/datasource-secret-binder.ts#parseCredentialsRef), and points at the schema refusals and content/docs/data-modeling/drivers.mdx, which teach the same form after PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656.
  • The header's Amended by line gains one entry pointing at the note.

Not edited: §10's body, §4.1, and the example's other keys. D7 (external.connector) is not taught here.

Why — measured at 04740b7c

  • The whole ADR has one secret: example, :657. No other prose spells the form.
  • The binder accepts a ref only with the sys_secret: prefix: parseCredentialsRef returns the id after it, else undefined. resolve() then returns undefined with no read, and DatasourceConnectionService leaves the datasource unconnected with failed-credentials ("could not be resolved or decrypted"). os serve wires createDatasourceSecretBinder as that resolver (packages/cli/src/commands/serve.ts).
  • PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656 teaches sys_secret:9f2c… in drivers.mdx and the showcase example, and sys_secret:9f2c in the two password refusals in datasource.zod.ts.
  • DatasourceSchema.safeParse, run with tsx against packages/spec/src:
Variant of the §8 datasource Result
As written on main refused, one issue: external.label (removed in 17.0.0, ADR-0049)
Only the secret: ref; every other key in its current spelling parses — the silent defect this card is about
external.label moved to top level, config.connectionString kept refused: connectionString → url
label top level, config.url or discrete fields, sys_secret: ref parses

So the secret: form is the one stale key in the example that fails silently. The other two are refused by name, each with its fix.

Deviations from the dispatch order

The first two were reported to the seat before the push and accepted (seat note, 2026-10-30T03:05Z):

  • One sentence beyond the credential fix. The note says the rest of the example is kept as written, as the surface this record was taken on, and that today's parse refuses external.label and config.connectionString by name. Neither key is edited.
  • One clause on §10. §10 says credentialsRef "points at service-secrets"; no package of that name exists. The note says the binder is the component that resolves the reference and that what it yields is the password, injected at connect. §10's body is not edited.
  • The full test suite was not run before opening. The diff touches no package; every gate family dispatch-gates derives was run instead (Verification).

Acceptance notes

  • §10 still names service-secrets in its body; the §8 note now says what it is. A body edit is for the maintainer if wanted.
  • §8 still shows external.label and config.connectionString. Both are refused loudly at the parse, each naming its fix.

Verification

dispatch-gates

node scripts/pm/dispatch-gates.mjs --commands (no paths, from the committed diff against merge base 04740b7c) derives 21 families. All 21 were run with exit codes captured before any pipe. pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 (@objectstack/formula and @objectstack/lint not built in a fresh worktree); after pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint it exited 0. --ran: 21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED. Re-derived after the commit: identical list. The ADR gates among them: check:adr-index, check:adr-anchors, check:adr-symbol-anchors, check-adr-links.mjs (+ --self-test), all exit 0. Changed lines 24 (+22 / −2), under the 3000 threshold. --tier answers MANDATORY CONTRACT_REVIEW_TIER for docs/adr/**.

git merge-tree against origin/main e320e366 is clean, and no commit since the base touched this file. scripts/check-nul-bytes.mjs exit 0.

Contract review owed: governed docs/adr/**, at CONTRACT_REVIEW_TIER. Landing is Tier H (triage 6505597349: "Tier H: the maintainer merges").

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind. (no names added)
  • 3. No formatter ran; pnpm lint passes (prose only; the full run is CI's).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset. docs/adr/** publishes nothing, so skip-changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 is the sanctioned Chinese channel).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…: handle

The §8 end-to-end example still taught credentialsRef 'secret:warehouse/readonly'.
That form parses, but the shipped datasource secret binder resolves only a
sys_secret:<id> handle minted by the secret store, so a copied example leaves
the datasource unconnected at boot. The example now uses the handle form the
schema refusals and drivers.mdx teach, and a dated amendment note under §8
records the original line, names what the binder resolves, says which component
§10's service-secrets turned out to be, and marks the rest of the example as kept
as written (two of its keys are refused by name today). The header's Amended by
line points at the note.

Claude-Session: https://claude.ai/code/session_016WV4cwBHtvCZyayQ8RFyUN
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 30, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 30, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract-review-claim: session_016WV4d1QkPVVzNz3FJqb2iS · head faa40f0 · 2026-10-30T03:15Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: faa40f0038add8c19530e0bd48e3da8f42ec0a2c
Local-runs: none
Scope: docs/adr/0015-external-datasource-federation.md + packages/services/service-datasource/src/datasource-secret-binder.ts, packages/services/service-datasource/src/datasource-connection-service.ts, packages/cli/src/commands/serve.ts, packages/spec/src/data/datasource.zod.ts, content/docs/data-modeling/drivers.mdx, examples/app-showcase/src/system/datasources/showcase-external.datasource.ts, docs/adr/0097-declarative-connector-instances.md, docs/adr/0099-posture-adjudicated-tiering-and-external-rung.md, scripts/adr-index.mjs, scripts/symbol-anchors.mjs

Read on the head, merge base 04740b7c; origin/main has no commit touching the ADR since that base. Diff is one file, +22/−2, as the claim's file surface lists. Author objectstack-fleet[bot].

① Derived judgments

  1. The §8 line swap is true against the binder. datasource-secret-binder.ts defines REF_PREFIX = 'sys_secret:'; parseCredentialsRef returns the id after that prefix and undefined for anything else; resolve() returns undefined with no store read when the id is undefined. DatasourceConnectionService (datasource-connection-service.ts ~:589-618) resolves a declared credentialsRef fail-closed before building the driver: a null/empty result is handleFailure(record, 'failed-credentials', "credential ... could not be resolved or decrypted (missing sys_secret row, or the encryption key changed)"), leaving the datasource unconnected. os serve wires createDatasourceSecretBinder as that resolver (serve.ts :5278, :5310). So the original secret:warehouse/readonly parses (credentialsRef is z.string().optional(), datasource.zod.ts :363) and never resolves; the amended sys_secret:9f2c… is the resolvable form. Correct.
  2. The new form matches PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656 (bfdaba432). drivers.mdx :994 teaches credentialsRef: 'sys_secret:9f2c…'; the showcase datasource :61-62 carries the same three-line comment and value the §8 example now mirrors; the two password refusals in datasource.zod.ts (:316-318, :633) teach sys_secret:9f2c and say the binder resolves only sys_secret: handles. The ADR's new comment and note repeat that wording, not a new one. Correct.
  3. Amendment-note claims, each checked. (a) "Setup → Datasources connection form's secret field encrypts the password into sys_secret and writes the handle here" is the schema refusal's own sentence (:314-316). (b) The anchor datasource-secret-binder.ts#parseCredentialsRef names an exported symbol (service-datasource index.ts :188) in the path#symbol grammar symbol-anchors.mjs accepts (same form as the D7 note's stack.zod.ts#collectDatasourceConnectorErrors). (c) "what it yields is the password, injected beside a credential-free config at connect": the resolved cleartext is spread as secret into factory.create({ ...toSpec(record), secret }) while the schema refuses config.password and userinfo URLs. Correct. (d) The note's "no decision changes" and the header's "example only" agree; the §10 rule (credentials only via credentialsRef, D7 aside) is untouched.
  4. Deviation 1 (the kept keys) is true today. datasource.zod.ts :100-103: "external.label was removed in @objectstack/spec 17.0.0 (ADR-0049) — ... Use the datasource's TOP-LEVEL label" (top-level label is :223). :618: connectionString: belongsInConfig('connectionString', 'url'), a refusal naming url. Both are loud refusals with the fix in the message, as the note says; spec is at 18.2.0 so the "removed in 17.0.0" citation is past tense and accurate. The note records them rather than editing them, inside the claim's one-sentence allowance.
  5. Deviation 2 (§10's service-secrets) is true. No packages/services/service-secrets directory and no package named @objectstack/service-secrets on the head; the component that resolves the reference is the service-datasource binder through the connection service's cfg.secrets.resolve. §10's body (:759-764) is not edited; the note's one clause says what the name denotes. Inside the claim's allowance.
  6. No other secret: teaching remains in the ADR. Grep of the head's ADR: the only secret:warehouse spelling is the quoted original inside the note (:667); :236-237 is the schema .describe() excerpt ("Reference into the secrets store"), :568 and :759-761 say "secrets store"/"secrets service" without a form. The secret: mention in crypto-provider.ts :73 is the business-row secret field, a different producer, out of this card's scope.
  7. House form. ADR-0097's header **Amended by**: carries #NNNN (date, ...) — ...; see the note under ... entries and dated blockquote **Amendment note (date, ref) ... blocks; ADR-0099's 2026-10-23 note uses "the X above is the amended one ... the original read ...". The new header entry and note follow both (edit in place, original quoted, dated 2026-10-30, card named). adr-index.mjs reads only the **Status**: line and the date/supersedes header fields, not Amended by; the status line is untouched, so the index gate is unaffected.
  8. D7 boundary. The diff adds no external.connector teaching; the D7 header entry and note are byte-identical to main. PR feat(spec): an API-backed datasource names a declared connector instance in external.connector and borrows its credential (ADR-0015 D7) #27783 is merged (merged_at 2026-10-30T01:37Z); open PR feat(service-datasource): a datasource naming a connector instance in external.connector borrows its credential, and its driver presents it per request (ADR-0015 D7) #27819's 23 files are all under packages/**, .changeset/ and pnpm-lock.yaml, none under docs/adr/**. No collision.

② Semver level

docs/adr/** only; no package, schema or runtime file in the diff. skip-changeset is on the PR and Check Changeset is green. Clause-②: no, as declared. No level to grade.

③ Boundary flags

  • Dev flags: the two accepted deviations (①4, ①5) hold against code; the full-suite skip is proportionate to a prose-only diff with the 21 derived gate families run. open_questions empty.
  • Out-of-scope findings in the report (§10's service-secrets body wording; §8's two refused keys) are correctly left unfiled: the note carries them, the maintainer may edit the body.
  • Author is objectstack-fleet[bot]; no login note owed.
  • CI at review time (check-runs on faa40f00): green Governed Surface Queue Guard, PR-body guards, Check Changeset, Check PR Size, Check Documentation Links, Lint · PM dispatch-gates, Lint · scoped x3, Plan Test Core shards, Dogfood Regression Gate, Test UI, Auto Label; still in_progress: Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · ESLint, Type Check x5, Test Core (1/8). No red. The landing precheck's all-green reading is the dispatch seat's once they converge.
  • The note body spells sys_secret: followed by the id in angle-bracket placeholder form inside a code span; fine in the markdown file (the D7 note and ADR-0097 use the same), noted only because it must not be pasted into a comment body.

④ Design judgment

The ADR's §8 is its worked example, and its first consumer is whoever copies it: an author or an agent writing a defineDatasource. Against that consumer the change is the right one: the only key in the example that fails silently (parses, never resolves, datasource stuck failed-credentials) is replaced with the form every other shipped teacher uses, and the example's comment says where the handle comes from, so a reader does not hand-write one. The amendment note keeps the record honest without widening the card: it quotes the original, names the resolving code by symbol anchor, and marks the two remaining stale keys as loud refusals rather than quietly rewriting them, which is what the ruling asked ("a dated amendment note rather than a silent rewrite"). The execution plan is complete in one commit: no gate, schema or runtime follows from it, and the D7 work it sits beside is already on main. Nothing here contradicts §10's rule or the D7 note.

Implemented-by: claude/project-thread-ti9z26
Reviewed-by: session_016WV4d1QkPVVzNz3FJqb2iS

VERDICT: PASS

Must-fix: none.
Should-fix: none.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

维护者速读

  • 改了什么:ADR-0015 §8 示例里的 credentialsRef: 'secret:warehouse/readonly' 改成 sys_secret:9f2c…,并在示例下面加一条带日期的修订注(2026-10-30),原句原样引在注里。注里还说明:该示例其余部分照原样保留,但其中 external.label 和 config.connectionString 两个键今天会被解析按名拒绝;§10 说的 service-secrets 指的就是 service-datasource 的凭据绑定器。文件头 Amended by 行加一条。1 个文件 +22/−2,不改任何决定。
  • 为什么改:数据源 schema 引用这份 ADR,可旧写法 secret:… 能通过解析,却没有任何已发布组件能解析它;照抄的人或代理会拿到永远连不上的数据源(failed-credentials)。已发布的绑定器只认 sys_secret: 句柄,PR fix(spec): datasource password refusals prescribe a credential shape that parses #27656 在 schema 提示和 drivers.mdx 里教的也是这个写法。
  • 风险与回滚:只改 ADR 文字,不碰代码;按 ADR-0097 / ADR-0099 的修订注写法,是带日期的修订,不是悄悄改写。revert 即回滚。D7(external.connector)一字未动,和进行中的 PR feat(service-datasource): a datasource naming a connector instance in external.connector borrows its credential, and its driver presents it per request (ADR-0015 D7) #27819 不重叠。
  • 先看:docs/adr/0015-external-datasource-federation.md 第 657–660 行的新示例,然后看第 665 行起的修订注。
  • 两分钟验证:git grep -n "secret:warehouse" -- docs/adr/0015-external-datasource-federation.md 只应命中修订注里引的原句(第 667 行)。
  • 需要你做的:docs/adr/** 是受管面,登记册判 Tier H,请批准本 PR(契约复核 PASS 6514127387,Head-sha: faa40f0038add8c19530e0bd48e3da8f42ec0a2c)。

席位意见:ACCEPT。契约复核 PASS,没有 must-fix,也没有 should-fix。CI 全绿。

席位核对

  • 形态: one PR, Fixes #27688, one file docs/adr/0015-external-datasource-federation.md (+22/−2), skip-changeset. Governed docs/adr/**: check-governed-merges.mjs --pr 27906 reads Tier H (人合).
  • 前提实测: on origin/main 04740b7c the ADR's only secret: example was :657; datasource-secret-binder.ts parseCredentialsRef returns an id only after sys_secret:, else resolve() is undefined and the connection service marks the datasource failed-credentials (claim 6513831269; review ①).
  • 门禁 check-runs: head faa40f0038add8c19530e0bd48e3da8f42ec0a2c: 46 check-runs, 26 success, 20 skipped (UI and build jobs path-filtered on a docs-only diff), 0 failed, 0 pending; Governed Surface Queue Guard, Lint & Repo Gates, TypeScript Type Check and Test Core success. Locally 21 derived gates, all exit 0.
  • 接受的偏差: the claim-amended extras — one sentence that the rest of the §8 example stands as recorded while external.label and config.connectionString are refused by name today, and one clause that §10's service-secrets is the service-datasource binder; both verified true against datasource.zod.ts and the tree (review ③④). §10 and the other keys are not edited.
  • 范围外去向: none filed: the two refused keys are refused loudly with their fix named, and §10's wording is now glossed by the note.
  • 落地条款: Tier H: a GOVERNED_APPROVERS approval, then the seat lands it under the maintainer's landing grant for this term (seat thread, 2026-10-29 22:04Z).

@os-zhuang
os-zhuang marked this pull request as ready for review October 30, 2026 03:28
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 813fd9b Oct 30, 2026
55 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-ti9z26 branch October 30, 2026 03:53
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 size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(adr): ADR-0015 §8 still teaches credentialsRef 'secret:warehouse/readonly', a form the shipped binder never resolves

2 participants