docs(spec,service-datasource): name the three CryptoContext producer vocabularies and narrow the AAD guarantee - #12838
Conversation
…vocabularies and narrow the AAD guarantee (#12599) `CryptoContext.namespace` / `.key` documented themselves as a settings coordinate, one layer below the three producers that construct the type: SettingsService (settings namespace / specifier key), the ObjectQL engine's secret-field path (object name / field name) and the datasource secret binder (caller-supplied namespace, default `datasource` / datasource name). The prose now names all three, and `CryptoHandle.id`, `encrypt()` and `rotateKey()` stop describing `sys_setting.value_enc` as the general destination of a handle. The load-bearing correction is the AAD sentence, which claimed a binding over this pair "Helps reject ciphertexts that were copied across namespaces". The pair is one flat space shared by all three vocabularies, `sys_secret` declares it non-unique by design, and nothing reserves a name in one vocabulary against another — so the sentence is replaced by the guarantee that actually holds: such a binding rejects a swap WITHIN one producer's vocabulary and does NOT exclude a cross-vocabulary pair. The docblock records this as the contract's present state and names the intended end state so the weak guard is not read as the designed one. The identical settings-only prose on `DatasourceSecretBinderDeps.namespace` is corrected in the same change. Documentation only: no behavior change, no schema shape change — `gen:schema`, `gen:openapi` and `gen:docs` all regenerate byte-identically over this diff. Recorded under the maintainer ruling of 2026-08-27 on #12599. Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin dbc59088e7d910b68fd2d47bc272308fd54bba2f && git checkout dbc59088e7d910b68fd2d47bc272308fd54bba2f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f907fbe9ed62c68d3de1a8a49d5ac3b9ba13abfc 9cb1c6eb03138c48e215945185ef91f9c6321599 && git checkout -B drift-repro f907fbe9ed62c68d3de1a8a49d5ac3b9ba13abfc && git merge --no-ff 9cb1c6eb03138c48e215945185ef91f9c6321599
node scripts/docs-audit/affected-docs.mjs --json f907fbe9ed62c68d3de1a8a49d5ac3b9ba13abfc
|
Fixes #12599
Option A of the maintainer ruling of 2026-08-27 (comment 5434987411, verbatim 「其他同意」): the contract prose tells the truth now. Zero behaviour change, zero schema shape change.
What was wrong
CryptoContext.namespace/.keydocumented themselves as a settings coordinate ("Settings namespace the value belongs to" / "Specifier key within the namespace"), one layer below the three producers that actually construct the type. Re-verified on this branch's base:ctx.namespacectx.keyhandle.idis recordedSettingsService(settings-service.ts:1639)sys_setting.value_encengine.ts:6053)secret:ref on the business rowdatasource-secret-binder.ts:~93)datasourcesys_secret:credentialsRefWhat changed
The load-bearing correction is the AAD sentence. It read "Helps reject ciphertexts that were copied across namespaces", which overstates what a binding over this pair can provide:
(namespace, key)is one flat space shared by all three vocabularies,sys_secretdeclares the pair non-unique by design, and nothing reserves a name in one vocabulary against another. It is replaced by the guarantee that actually holds — such a binding rejects a swap within one producer's vocabulary and does not exclude a cross-vocabulary pair, with the constructiblemail|api_keycase named concretely. Per the ruling, the flat shared space is documented as current reality, not the intended end state, and the docblock says so in as many words.The rest of the file follows the same correction: the
ICryptoProviderheader gains a "Producers" section naming all three, andCryptoHandle.id, the lifecycle list,encrypt()androtateKey()stop describingsys_setting.value_encas the general destination of a handle.Folded in per the ruling (recorded on the card, no separate issue): the identical settings-only prose on
DatasourceSecretBinderDeps.namespace— "Settings namespace recorded on the secret row".Why a changeset rather than
skip-changesetMeasured, not assumed.
gen:docsregenerates 229 files with zero delta, so — contrary to the usual argument for spec docblocks — these comments are not reader-visible reference pages. Butpackages/specemits its TSDoc into the published.d.ts, so the corrected prose is what every@objectstack/specconsumer reads at the call site. That ships, so it gets apatchchangeset for both packages.The ADR is deliberately not cited by number here
Direction B is recorded in a companion draft PR adding
docs/adr/0128-*.md, which waits on the maintainer's hand-merge. CitingADR-0128from this PR would turncheck:adr-anchorsred — it hard-fails a citation naming no record underdocs/adr/— on this PR and then onmainuntil the ADR lands. So the docblock names the intended end state in prose without an id, and the ADR cites the contract rather than the reverse. Each PR is independently green in either landing order.Verification
Union re-run after the final commit, at
9cb1c6eb(origin/main@f907fbe9merged in). Gate set derived bynode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackfrom the actual changed set — 32 families, exit codes captured before any pipe.check:adr-anchors,check:adr-links,check:nul-bytes).pnpm --filter @objectstack/spec build(which runsgen:schema+gen:openapi) andgen:docsboth leavegit statusshowing only the source edits.check:authorable-surfaceandcheck:docsgreen.typecheck:@objectstack/spec+@objectstack/service-datasource, scope 2 of 79, both echoed — green.test:@objectstack/spec436 files / 11560 tests passed;@objectstack/service-datasource27 files / 585 tests passed.PREREQUISITE NOT METline:check-dev-prereqs(the container's workspace is not fully built — 66 of 67 packages have nodist/) andscripts/pm/check-half-states.mjs(exit 3 — no valid GitHub credential in this container; it swept no issue and ran no predicate). Neither reads this diff; CI runs both.--no-inline-config --format json— 2 files linted, 0 errors, 0 warnings. Population read from eslint's own config, not guessed: the changeset.mdreports "File ignored because no matching configuration was supplied", so the two TS files are the whole of this diff's eslint population. Invariance for untouched files:eslint.config.mjsnever enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for any file — stated in the config with its own positive-control measurement — so this diff cannot move the verdict on a file it does not touch.Landing is serialized behind #12718 and #12737 per the generated-artifact relay; the PM lands. Not flipped ready or enqueued.
Generated by Claude Code