Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/28821-connector-refusal-location-prose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@objectstack/spec': patch
---

fix(spec): the connector docs say where an undeclared key's refusal is located: at the entry, with the key named in `keys`

Clause-②: no
Metadata-protocol: none

A connector entry refuses a top-level key it does not declare, through `DeclarativeConnectorEntrySchema` and `defineConnector()`. The TSDoc on both said the refusal was "located at the key". It is not. zod raises one `unrecognized_keys` issue at the entry itself: its path is empty when the entry is parsed alone and `connectors.N` inside a stack, and it lists every undeclared key in `keys` and in its message. An author or agent looking for an issue at the key's path found none.

The TSDoc now says this. The same correction is made in the reason of the protocol-18 upgrade note for undeclared connector keys (`connector-entry-undeclared-key-refused`), which is not released yet.

Prose only. The refusal, its code, its path, its message and what the connector schema accepts are unchanged.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
// An authored connector entry (`DeclarativeConnectorEntrySchema`, behind
// `defineStack({ connectors })`, `os validate` / `os compile`, the artifact load
// and `PUT /meta/connector/:name`) refuses every TOP-LEVEL key its shape does
// not declare, located at the key, where it used to parse green. Every refusal
// is pinned by its issue `code`, `path` and `keys`; every case has a CONTROL
// accept, so a refusal that fired on everything could not pass. The open
// not declare, in one issue located at the entry root, where it used to parse
// green. Every refusal is pinned by its issue `code`, `path` and `keys`; every
// case has a CONTROL accept, so a refusal that fired on everything could not
// pass. The open
// runtime carrier, `ConnectorSchema`, keeps its posture, pinned as a control.
//
// `defineConnector()`, the helper every documented `connectors:` entry is
Expand Down
16 changes: 11 additions & 5 deletions packages/spec/src/integration/connector.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,13 @@ const ENTRY_PROVIDER_CONFIG_KEY_GUIDANCE =
* parse), and every read exit serves that body. On an open shape an undeclared
* key therefore parsed green and was kept, while `defineStack`, `os validate`
* and `os compile` dropped it without a word. Closed, every door that parses
* this schema refuses it by name, located at the key: `defineConnector()`,
* this schema refuses it by name: `defineConnector()`,
* `defineStack({ connectors })`, `os validate` / `os compile`, the artifact load
* at boot and reload, and the save door with `422 INVALID_METADATA`. The ADR-0010 protection envelope
* at boot and reload, and the save door with `422 INVALID_METADATA`. The refusal
* is ONE zod `unrecognized_keys` issue located at the entry, not at the key: its
* path is the entry's own (empty when the entry is parsed alone, `connectors.N`
* inside a stack), and it names every undeclared key in its `keys` and in its
* message. The ADR-0010 protection envelope
* is declared in the shape, so a stamped, package-loaded entry still parses.
*
* The refusal is terminal ({@link strictObject}): a body with an undeclared key
Expand Down Expand Up @@ -1229,9 +1233,11 @@ const DefineConnectorSchema = lazySchema(() =>
* shorthand) — preferred over a bare `: Connector` literal.
*
* The parse is CLOSED: a top-level key the connector does not declare is
* refused by name, located at the key, with the prescription the authored
* entry gives (a credential-shaped key → `auth.credentialRef`, a provider
* setting → `providerConfig`, `timeout` → `requestTimeoutMs`). The def a plugin
* refused by name, in one `unrecognized_keys` issue located at the root (path
* `[]`, not the key's) that lists every such key in `keys`, with the
* prescription the authored entry gives (a credential-shaped key →
* `auth.credentialRef`, a provider setting → `providerConfig`, `timeout` →
* `requestTimeoutMs`). The def a plugin
* hands to `registerConnector` is parsed there by the open
* {@link ConnectorSchema}, whose posture this does not change.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const entry: SemanticMigration = {
reason:
'The authored entry was a plain object, so an undeclared top-level key parsed green at every door and ' +
'meant nothing to the runtime: no provider, the automation service or the dispatcher ever read one. It is ' +
'refused now, by name and located at the key, with a prescription for the two families an author most ' +
'refused now, by name, in one `unrecognized_keys` issue located at the entry (not at the key) that lists ' +
'every undeclared key, with a prescription for the two families an author most ' +
'often misplaces: a credential and a provider setting. There is deliberately no mechanical rewrite: an ' +
'undeclared key may be a misplaced provider setting whose right home depends on the provider, or a secret ' +
'whose only copy is the value written there, and where either goes next is a judgement about the ' +
Expand Down
Loading