Skip to content

NotifyConfigSchema sourceObject/sourceId describes say "Requires ..." while the schema deliberately accepts the half pair (executor drops it silently) #7085

Description

@os-project-manager

Finding from the axis-① .describe() sweep (describe claims vs measured acceptance face — the #6762 class). Recorded unassigned; suggest domain:spec-surface for routing. Sibling of the severity finding on the same schema (filed separately — different field, different claim type).

Anchor

packages/spec/src/automation/io-node-config.zod.ts, NotifyConfigSchema:

sourceObject: z.string().optional()
  .describe('Object name of the record the notification links to (writes sys_notification.source_object). Requires sourceId.'),
sourceId: z.string().optional()
  .describe('Record id the notification links to (writes sys_notification.source_id). Requires sourceObject.'),

Described claim

"Requires sourceId." / "Requires sourceObject." — to a schema reader, "requires" says the half-specified pair is refused at the gate.

Measured acceptance face

Probed on origin/main @ 2f3e79351 (tsx safeParse, sources via git archive; controls both sides):

control-accept full pair       : ACCEPTED
control-reject unknown key     : rejected [unrecognized_keys]
sourceObject WITHOUT sourceId  : ACCEPTED
sourceId WITHOUT sourceObject  : ACCEPTED

And the acceptance is deliberate — the same file's module JSDoc, a few lines above, records the opposite of what the describes say:

sourceObject/sourceId only take effect as a PAIR — a half-specified click-through target is dropped so the inbox never renders a dead link. The schema keeps both optional rather than refining, because the executor tolerates (drops) the half-specified shape rather than rejecting it.

So the contract is "tolerated and silently dropped", and the describe says "required". The two sentences sit in one file and cannot both be true.

Why it matters for an authoring reader (ADR-0033)

Only the .describe() reaches the published reference — content/docs/references/automation/io-node-config.mdx renders "Requires sourceId." verbatim in the property table, while the JSDoc paragraph carrying the real semantics does not travel (#6762 measured exactly this asymmetry: gen:docs renders .describe() and the module docblock, never the property JSDoc). A reader of the docs concludes a half pair will error and can be leaned on as validation; in fact it parses green, publishes green, and the click-through link silently never renders — the invisible-failure shape this repo's own guidance entries repeatedly warn about (#2675, #4923).

Suggested shape, if triage wants it fixed

Make the describes state the documented tolerance instead of a phantom requirement, e.g. "Only takes effect together with sourceId — a half-specified pair is dropped at execute time (no link is rendered)." No acceptance change; regenerate io-node-config.mdx. (Adding the refine instead would contradict the recorded executor contract and is a bigger decision.)

Refs

#6762 (class specimen), #2675 (pair-only click-through), #4923 (alias-conversion tolerance on this schema), ADR-0033.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions