Repository navigation
fix(spec): datasource read redaction resolves a driver's identity the way its sibling helper does - #21963
Conversation
… way its sibling helper does The per-driver half of `redactableConfigKeys` now looks a driver up through `resolveDriverId`, the resolver `passthroughSecretPaths` and the write door's contract lookup already use. Every spelling the write door judges against a builtin driver's contract is redacted as that driver, and a crafted driver id answers as a driver with no shipped contract instead of throwing. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
… credential migration hold under every accepted spelling of a builtin driver Lockstep pins for the spec redaction fix: the item read withholds the still-writable credential key under each accepted spelling, an untouched Save restores the stored value, a crafted driver id is read with its credentials withheld, and the migration planner reads the same list as under the canonical spelling. No source line in this package moves. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
…er identity Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 138 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 9c12aae5c41bf6212c21662baad61cf30fc6a747 && git checkout 9c12aae5c41bf6212c21662baad61cf30fc6a747
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 80f9f7e6ba5d2097a4cb32ca696908dcf9678102 cbea11408e36bb46b64f71d04cd367a442ef4777 && git checkout -B drift-repro 80f9f7e6ba5d2097a4cb32ca696908dcf9678102 && git merge --no-ff cbea11408e36bb46b64f71d04cd367a442ef4777
node scripts/docs-audit/affected-docs.mjs --json 80f9f7e6ba5d2097a4cb32ca696908dcf9678102 |
Fixes #21955
Clause-②: no
What changed
The per-driver half of
redactableConfigKeysinpackages/spec/src/data/datasource-credential-redaction.tsnow resolves a driver's identity throughresolveDriverId. That is the resolver its sibling helperpassthroughSecretPathsalready uses, and the one the write door's contract lookup (getDriverConfigSchema) uses. There is no second identity resolver: the fix replaces one lookup line with the sibling's two-line shape.redactedConfigKeysnames it.service-datasourcesource line moves.getDatasource(),restoreRedactedConfigand the credential migration all reach the fix through the spec export (measured below).Measured reach, before the fix (BASE
76fec88b16, spec dist built from BASE)A unit-level harness of
service-datasource'sDatasourceAdminService, with an in-memory record store, ran every spelling the alias table and the resolver's folding accept. The harness is scratch and is not committed.DatasourceSchema.safeParseand bycreateDatasource, and was judged against the builtin contract (validateDriverConfig(...).known === true).getDatasource, behindGET /api/v1/datasources/:name): for each accepted spelling other than the canonical one, the still-writable credential key came back inconfigandredactedConfigKeyswas[]. The canonical spelling withheld it.datasourceredactor, used by the/metaitem and list reads): the same answer as the item read for every spelling.listDatasources): it serves noconfigat all, by its contract (DatasourceSummary), so it carries no exposure for any spelling.getDatasource, the metadata redactor,restoreRedactedConfigandplanCredentialMigrationeach threw an unhandledTypeError. So the item read failed, an edit with a config patch failed, and the migration planner failed.listDatasourceswas unaffected.After the fix, the same harness reads every accepted spelling as withheld and named, and every crafted id as read without a throw.
Exposure stays admin-only: the datasource read doors require the admin capability.
Pins
packages/spec/src/data/datasource-credential-redaction.test.ts, a new block. Every spelling is derived fromDRIVER_ID_ALIASESplus the resolver's own folding (as-is, upper, capitalised, padded), never listed by hand.service-datasource(declared on [PM seat] domain:services — ⏳ vacant (signed off 2026-10-06 by session_011K3zqE8Pv1Evw5hc8tZCnN) #6021):datasource-config-redaction.test.ts: the admin item read withholds the key under each accepted spelling. An untouched Save restores the stored value under each accepted spelling, so redaction never turns a save into deletion. A crafted id is read with its credentials withheld, and an untouched Save keeps them.datasource-credential-migration.test.ts: the planner reads the same list under each accepted spelling. A bindable row names the still-writable key as residue, byte-equal to the canonical spelling's plan. A row holding only that key is refused with it named.Reverse verification (fix committed first, then reverted with
scripts/ablation-replace.mjs, then restored)src, so no build leg applies. Predicted three red, with the premise and the control staying green. ObservedTests 3 failed | 41 passed (44): the byte-equal set, the withheld key, and the crafted id. Restore proof: blobb543590328efequals HEAD, andgit diff HEADis empty.service-datasourcepins. These resolve@objectstack/spec/datathroughdist, so each leg rebuilt spec.ablation-dist-preflightfound the reverted line in 8 built files. Predicted five red. ObservedTests 5 failed | 61 passed (66).--absentreported the marker absent from all 228 built files and the tree clean against HEAD. ThenTests 66 passed (66).Clause-② (measured)
no. The fix narrows what the read path serves, not what@objectstack/specaccepts.DatasourceSchema.safeParsegave the same answer before and after for every spelling and every crafted id in the harness.check:api-surface:public API surface + factory signatures unchanged.check:authorable-surfaceandcheck:export-originsare green.The changeset is
@objectstack/specpatch.service-datasourcetakes none, because no source line in it moved.Tests and gates (at HEAD
cbea11408e)pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2:Test Files 619 passed (619),Tests 18476 passed | 1 todo.pnpm --filter @objectstack/service-datasource exec vitest run --maxWorkers=2:Test Files 41 passed (41),Tests 748 passed (748).@objectstack/metadata-protocol, which reaches the datasource redactor:protocol.metadata-redaction.test.tsandstored-metadata-body-family.pin.test.tsgaveTests 56 passed (56).pnpm --filter @objectstack/service-datasource typecheckandpnpm --filter @objectstack/spec typecheck(includingcheck:test-typecheck) both exit 0.tsc --listFilesconfirms that both editedservice-datasourcetest files are in the program.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 87 derived, 86 run with exit 0, 1 NOT MEASURED, 0 unrun (--ranreconciliation).pnpm check:dual-build-cjs-loads, reason: PREREQUISITE NOT MET (exit 3). It reads every package's builtdist/, and this worktree built only the closure it needed. Declared to CI..tsfiles. Each resolves to a config undereslint --print-config, so none is ignored.eslint --no-inline-config --format jsonlinted 4 files with 0 errors and 0 warnings.eslint.config.mjsenables no type-aware linting (zeroproject/projectServiceentries, and none in the resolvedparserOptions). So this diff cannot move any untouched file's verdict.pnpm lintstays CI's.origin/mainwas re-fetched before this PR opened (80f9f7e6ba). Nothing that landed since BASE touches these files, so there was no merge.Acceptance notes
STILL_WRITABLE_CREDENTIAL_KEYSandPASSTHROUGH_SECRET_PATHSare typed as string-keyed records. Typing them by the builtin id union would make a raw-string index a compile error, closing this defect class attsc. That is not done here, to keep the fix to the sibling's shape. Carrier: the at-tier contract review of this PR.Generated by Claude Code