Repository navigation
Commit 7b293c4
feat(spec): a save of a connector's served form drops a leftover undeclared auth key and names it (#29456)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9CDSh9Q8hhMV3sRgRxkeLrC)_
Part of #28640
Clause-②: yes (widening)
Metadata-protocol: none
Ruling-ref: 6566912252
Skills-docs: none — no authorable key, accepted value or metadata type
changes; the change is a read redactor's result type and the save
answer's advisories, which no skill teaches
## 维护者速读
- 改了什么:连接器旧数据里 `auth` 未声明键上的明文(如 `auth.token`),现在普通「读取 →
保存」一次就会从库里清掉,保存结果的 `advisories` 逐个点名被清掉的键。
- 为什么改:裁决 ②B(6566912252)。这些键门口早已拒收、运行时也从不读,带回去只是让明文一直留在库里。
- 风险与回滚:只影响连接器 `auth` 未声明键这一类;其他所有隐藏位置(请求头表、datasource 等)照旧带回。回滚即还原本 PR。
-
先看:`packages/metadata-protocol/src/metadata-redaction.ts`(`planCarryForward`
跳过不带回的位置)、`packages/spec/src/integration/connector-credential-redaction.ts`(标记)、`packages/spec/src/kernel/metadata-type-redaction.ts`(新成员与契约说明)。
- 两分钟验证:`pnpm --filter @objectstack/metadata-protocol exec vitest run
src/protocol.metadata-redaction.test.ts -t "28640"`,预期 6 条全绿。
- 需要你做的:批准新的公开名 `notCarriedForwardKeys` 与提示规则名 `withheld-key-dropped`(见
Acceptance notes),其余席位落地。
## What changes
- **`MetadataRedactionResult` gains `notCarriedForwardKeys?: string[]`**
(`@objectstack/spec/kernel`). It names the positions among
`redactedKeys` that are withheld AND not carried forward. It is a
**subset** of `redactedKeys`, not a disjoint list: `redactedKeys` still
names every withheld position, so every existing reader that asks "was
anything withheld?" through `redactedKeys` (`redactStoredMetadataBody`,
`redactMetadataItem`, `metadata-manager`'s served-document walk)
withholds exactly what it did before. Optional, and absent means none,
so the `datasource` built-in, the schema-derived redactors and every
registered redactor (`service-automation`'s flow redactor included) keep
carrying every withheld position forward with no edit. The interface
docblock says what a read exit and what a save's carry-forward do with
each list.
- **The connector redactor marks its undeclared `auth` keys**
(`redactConnectorItem`, item 1 of its module note). Each such key is
named in both lists. A cleared value there (`null`, `''`, an empty map)
is withheld too now, the way items 7 and 8 already withhold cleared
undeclared keys: every instance-auth kind is a strict shape that refuses
the key whatever it holds, so serving `auth.token: null` back made a
plain read then save fail with `unrecognized_keys`. Every other withheld
connector position (the four maps, `authentication`, credential URLs,
undeclared top-level, `retryConfig` and `actions` keys, a non-object
`auth`) stays carried forward.
- **The carry-forward skips those positions and reports what it
dropped** (`metadata-protocol`). `planCarryForward` never grafts a
not-carried-forward path; it records each one the stored row holds and
the incoming body does not hold. A value the incoming body itself holds
there is the author's word, judged at the door, and not a drop.
`carryForwardRedactedValues` and `redactedPathsCarriedForward` keep
their signatures; a new package-internal
`carryForwardRedactedValuesReportingDrops` returns the body and the
dropped paths. `carryForwardRedactedCredentials` (the private save-door
body) now answers `{ item, dropped }`.
- **The save answer names each drop on `advisories`**, the existing
channel a 2xx save carries its non-gating findings on (no second
channel). One `warning` per dropped position: rule
`withheld-key-dropped`, `where` = `connector "NAME"`, `path` = where the
value sat at rest in the advisory spelling (`connectors[0].auth.token`),
never the value. The key is still omitted when there is nothing to
report. A draft save drains and reports too.
`SaveMetaItemResponseSchema.advisories`'s description says so, and
`content/docs/references/api/protocol.mdx` is regenerated from it.
## Why
Ruling ②B (6566912252, the maintainer's 「同意」): "The redactor can mark a
position "withheld and not carried forward"; the class covers undeclared
keys under `auth` … A normal save of the served form therefore drops
them instead of re-grafting them, and the save result names the dropped
keys in its notices, so a drop at rest is loud, never silent."
## Tests
New, all against rows written below the door (direct seed / stored
body), as a pre-strict row would be:
- `metadata-protocol/src/protocol.metadata-redaction.test.ts` › `[#28640
②B] a save of the served connector form drains an undeclared auth key`
(6): read serves without `auth.token` → save of the served form stores
none and `advisories` names `connectors[0].auth.token` (value absent
from the answer); CONTROL the header map beside it round-trips
unchanged; the drain converges (second save: no `advisories` key); a
draft save drains and reports; a cleared `auth.token: null` row now
saves; CONTROL a datasource row's carry-forward is unchanged and its
answer carries no `advisories`.
-
`metadata-protocol/src/metadata-redaction.connector-undeclared-keys.test.ts`
› `an undeclared auth key is not carried forward` (5): drop + report
while the undeclared root key and the header map are still carried; the
runtime gate's `redactedPathsCarriedForward` no longer names it; the
author's own value at that position (a switched `auth` kind that
declares the key) is not a drop; removing `auth` drops and reports;
CONTROL no drop on a row without one.
- `spec/src/integration/connector-credential-redaction.test.ts` ›
`withheld and not carried forward` (8): both lists name every undeclared
`auth` key; unknown `type`; the three cleared forms are withheld and the
served form then passes `DeclarativeConnectorEntrySchema` (it fails
before redaction); CONTROL every other withheld position leaves the
member absent; CONTROL a non-object `auth` is still carried; only the
`auth` key is marked beside the other positions.
- `spec/src/kernel/metadata-type-redaction.test.ts` ›
`notCarriedForwardKeys` (4): the built-in names it; a row whose ONLY
withheld position is not carried forward is still redacted by the
stored-row seam (pins subset-not-disjoint); CONTROL the datasource
built-in and the derived webhook redactor never set it; a registered
redactor without it type-checks.
Forced edits to existing tests: none.
## Acceptance notes
- **Contract review:** per the ruling this is a
`MetadataRedactionResult` contract change (spec + metadata-protocol) and
takes a `CONTRACT_REVIEW_TIER` record; that record is the reviewing
seat's, not this PR's.
- **Two new public names await the maintainer:** the member
`notCarriedForwardKeys` and the advisory rule id `withheld-key-dropped`.
Alternatives considered for the member: `droppedOnSaveKeys`,
`drainedKeys`.
- **Deviation from the card delta's expected surface:** the delta said
undeclared `auth` keys land "in the new list, not in `redactedKeys`".
They land in both (subset). A disjoint list fails open: three existing
readers decide "nothing withheld" from `redactedKeys.length === 0`
(`redactStoredMetadataBody` and `redactMetadataItem`, and
`packages/metadata/src/metadata-manager.ts` `redactServedDocument`), so
a connector row whose only withheld position was an undeclared `auth`
key would have been served in cleartext by every one of them, and by any
third-party reader. The subset keeps all of them correct with no edit;
only the carry-forward reads the new member. The ruling's words ("the
redactor can mark a position") fit either.
- **Cleared undeclared `auth` keys are now withheld too** (they were
served as written). Not in the delta; included because the save of their
served form was a 422 before this PR and the ruling wants that save to
drain them.
- **File surface beyond the claim's four files:**
`packages/metadata-protocol/src/protocol.ts` (the save door threads
`dropped` into `advisories`: 3 hunks) and
`packages/spec/src/api/protocol.zod.ts` (the `advisories` description).
- **Docs:** no hand-written page describes the connector read redaction
or the save's carry-forward (`content/docs/automation/connectors.mdx`
already sends credentials to `auth.credentialRef`), so the regenerated
`protocol.mdx` and the changeset are the documentation change.
- **Not in scope, unchanged:** a non-object `auth` (item 4) and every
`authentication.KEY` (item 2) are still carried forward;
`sys_metadata_history` snapshots keep the old body (served redacted by
the stored-row seam); ①A (#29341) and the ③A export marker (#29342).
## Verification
Readings on head `04ba73c2207187c08cb3566eab228eab9658976d`, tree
`objectstack-ai/objectstack`.
All on the merged head (main at `7f277a86` merged in), each heavy run
under `scripts/pm/os-verify-lock.sh` (`VERDICT command-exit 0` on every
one).
| Run | Reading |
| --- | --- |
| `turbo run build --filter='./packages/**'` | 78/78 tasks |
| `@objectstack/spec` focused (redaction + protocol tests) | 3 files,
333/333 |
| `@objectstack/metadata-protocol` focused (5 redaction / carry-forward
files) | 5 files, 113/113 |
| `@objectstack/spec` test (full) | 791 files, 23463 passed + 1 todo |
| `@objectstack/spec` typecheck (incl. scripts + test typecheck) | exit
0 |
| `@objectstack/metadata-protocol` test (full) | 304 files passed + 4
skipped; 22166 passed + 26 skipped |
| `@objectstack/metadata-protocol` typecheck (`src/**/*`, tests
included) | exit 0 |
| `@objectstack/objectql` save / publish / advisory answer conformance |
3 files, 45/45 |
| `@objectstack/metadata` redaction bookkeeping + service | 2 files,
81/81; typecheck exit 0 |
| `@objectstack/service-automation` flow / connector credential files |
4 files, 72/72; typecheck exit 0 |
| `@objectstack/runtime` flow credential files | 2 files, 13/13;
typecheck exit 0 |
| ESLint `--no-inline-config` on every changed `.ts` | exit 0 |
| `pnpm --filter @objectstack/spec check:generated` | clean after the
post-merge regen (the one prerequisite red was a pre-build dist) |
| `check-changeset-no-major.mjs --event` on this body | exit 0 (`yes
(widening)` / `none`, patch) |
**Ablations** (pre-merge head, restored by blob hash after each):
disabling the carry-forward's not-carried-forward skip turns 6 tests
red; deleting the connector redactor's `notCarriedForwardKeys` push
turns 7 red.
<details><summary>Derived gates — <code>dispatch-gates.mjs --ran</code>:
106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN; every one exit
0</summary>
```
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:durability-log-level :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:filter-alias-parity :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
</details>
## Human Minimum
Each item is one rule from
[CONTRIBUTING.md › Human
Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum),
which links the AGENTS.md text that states it.
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
The new member is a type on the existing redaction contract; the
connector edit extends the pure redactor `packages/spec` already hosts,
and the carry-forward that acts on the mark lives in
`metadata-protocol`.
- [x] 2. Every name I added uses the casing for its kind
(`notCarriedForwardKeys` camelCase member; `withheld-key-dropped`
kebab-case rule id, like the gate's own rule ids).
- [x] 3. No formatter ran; ESLint (the root `pnpm lint` command,
`--no-inline-config`) is clean on every changed `.ts` file.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed (`content/docs/references/api/protocol.mdx`,
regenerated again on the merged tree).
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the `## 维护者速读` digest
is the maintainer's requested Chinese summary).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmS7ggoatyXpxRfHq4w)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent de708b3 commit 7b293c4
12 files changed
Lines changed: 584 additions & 39 deletions
File tree
- .changeset
- content/docs/references/api
- packages
- metadata-protocol/src
- spec/src
- api
- integration
- kernel
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2874 | 2874 | | |
2875 | 2875 | | |
2876 | 2876 | | |
2877 | | - | |
| 2877 | + | |
2878 | 2878 | | |
2879 | 2879 | | |
2880 | 2880 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
12 | 17 | | |
13 | 18 | | |
14 | 19 | | |
| |||
84 | 89 | | |
85 | 90 | | |
86 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
52 | 58 | | |
53 | 59 | | |
54 | 60 | | |
| |||
448 | 454 | | |
449 | 455 | | |
450 | 456 | | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
451 | 462 | | |
452 | 463 | | |
453 | 464 | | |
| |||
520 | 531 | | |
521 | 532 | | |
522 | 533 | | |
523 | | - | |
524 | | - | |
525 | | - | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
526 | 567 | | |
527 | 568 | | |
528 | 569 | | |
| |||
559 | 600 | | |
560 | 601 | | |
561 | 602 | | |
562 | | - | |
563 | | - | |
564 | | - | |
565 | | - | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
566 | 607 | | |
567 | | - | |
| 608 | + | |
568 | 609 | | |
569 | 610 | | |
570 | 611 | | |
571 | 612 | | |
572 | 613 | | |
573 | 614 | | |
574 | | - | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
575 | 623 | | |
576 | 624 | | |
577 | 625 | | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
578 | 636 | | |
579 | 637 | | |
580 | 638 | | |
| |||
621 | 679 | | |
622 | 680 | | |
623 | 681 | | |
624 | | - | |
| 682 | + | |
625 | 683 | | |
626 | 684 | | |
627 | 685 | | |
| |||
0 commit comments