diff --git a/.changeset/28821-connector-refusal-location-prose.md b/.changeset/28821-connector-refusal-location-prose.md new file mode 100644 index 00000000000..f26ff925185 --- /dev/null +++ b/.changeset/28821-connector-refusal-location-prose.md @@ -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. diff --git a/packages/spec/src/integration/connector-entry-undeclared-keys.test.ts b/packages/spec/src/integration/connector-entry-undeclared-keys.test.ts index a4c8fe91efa..07cbb0a6ef1 100644 --- a/packages/spec/src/integration/connector-entry-undeclared-keys.test.ts +++ b/packages/spec/src/integration/connector-entry-undeclared-keys.test.ts @@ -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 diff --git a/packages/spec/src/integration/connector.zod.ts b/packages/spec/src/integration/connector.zod.ts index eebb0a8518c..877c2a1f7d2 100644 --- a/packages/spec/src/integration/connector.zod.ts +++ b/packages/spec/src/integration/connector.zod.ts @@ -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 @@ -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. */ diff --git a/packages/spec/src/migrations/entries/semantic/18.connector-entry-undeclared-key-refused.ts b/packages/spec/src/migrations/entries/semantic/18.connector-entry-undeclared-key-refused.ts index 9eecc3808ca..a86db29ab93 100644 --- a/packages/spec/src/migrations/entries/semantic/18.connector-entry-undeclared-key-refused.ts +++ b/packages/spec/src/migrations/entries/semantic/18.connector-entry-undeclared-key-refused.ts @@ -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 ' +