Skip to content

[finding] A fourth customer-facing refusal population in packages/spec — messages built inside error: () => callbacks — carries 32 tracker ids and is structurally unreachable by check-doc-authoring Rule 3 #13002

Description

@os-trump

Measured 2026-08-28 by the dev seat implementing the #12522 strip, on that branch. Observation and a scoping question, not a defect claim — the population below was deliberately left alone because it sits outside that card's ruled buckets.

What was measured

Rule 3 of check-doc-authoring now recognises five positions (zod message:, positional validator messages, strictObject options, retiredKey(), .describe()), plus the hoisted-const spelling of each. Its climb terminates at an ArrowFunction — deliberately, since that was the boundary of the original rule.

But a share of this tree's refusal prose is built inside a function, not written at a recognised position:

error: (iss) => (
  isObjectInput(iss)
    ? '`params` is the parameter DEFINITION array …, not a values map. '
      + "For a `type:'api'` action's static request body … use `bodyExtra: { … }` instead (a tracker id rides here). "
      + 'Expected an array of ActionParam, received an object.'
    : undefined
),

An AST census over packages/spec/src (non-test .ts, string literals only) for literals whose climb reaches a function body before any recognised position:

count
id-bearing literals 28
tracker ids in them 32
files 8

Heaviest: data/driver/common.zod.ts (15), ui/component.zod.ts (5), ui/action.zod.ts (2), ui/dashboard.zod.ts (2); one each in data/driver/postgres.zod.ts, data/driver.zod.ts, data/object.zod.ts, ui/app.zod.ts.

Why this is a question and not just a backlog item

These are the same audience at the same moment as the population the #11052/#12124#12522 chain has now stripped three times over. error: () => … is how a zod error map is spelled; the string is printed at the refusing author verbatim, exactly like a message: string. By the ruling's own reasoning — "the ban follows the audience, not the file type" — they look like the same population.

Two things make widening a separate decision rather than a rider, which is why the dev seat did not take it:

  1. The [finding] Three ADJACENT customer-facing text populations in packages/spec still carry tracker ids — 539 literals the refusal-message strip deliberately did not touch #12522 census counted them in other (the ~2.3k bucket of conversion-registry summaries, conformance tables and test assertions) and its charter enumerated exactly three buckets. Stripping these would widen a ruling rather than apply it.
  2. Widening the rule to reach them is not a one-line change. messagePosition/customerTextPosition returns undefined at ArrowFunction on purpose: climbing through function bodies unconditionally would sweep in every string a helper happens to build, including values. The honest widening is narrower — recognise a function that is itself the value of a zod error option (or of error: inside a schema options object) and climb through that — and it needs its own self-test cases in the same edit, since an unrecognised spelling produces no flag, silently.

What holds today

Nothing. Rule 3 is green over all four recognised buckets (13,306 strings, 0 violations, every bucket populated), and these 28 literals sit outside every one of them. The gate is not wrong — it is scoped — but the scope boundary is invisible from its output, which is the shape worth recording.

The question for triage

Does the #11052 inheritance reach refusal prose built inside an error: () => callback — same audience, same moment, different spelling? If yes, the work is: widen customerTextPosition to recognise a zod error-option function body (with self-test cases), then strip the 32 ids under the same keep-the-ADR-id rule.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions