fix: walk contentSchema, await the error envelope, correct the README claim

Copilot review round 7.

- Add `contentSchema` to the traversal. It holds the schema the decoded
  string content must satisfy, so a bare `true` or a remote `$ref` under
  it is a real finding all three clients were walking past. Also admit
  the three content keywords to `CONSTRAINING_KEYWORDS` on the same
  documented grounds as `format` — annotation-only by vocabulary, but
  plainly an attempt to constrain, so reporting such a schema as
  accepting anything would be noise. The comment now names that
  category rather than singling `format` out.

- Make `handleError` async and await the envelope write. Round 6 made
  the `--strict` report await its write and stopped one line short: the
  envelope emitted for the very same throw still raced `process.exit()`,
  so the machine-readable line a CI caller parses could be lost on a
  pipe. The launcher's comment claiming the envelope was "well inside
  the pipe buffer" was wrong reasoning and is replaced — fitting in the
  buffer says nothing about whether the write was performed. Its call
  site now awaits; the bin's `.catch(handleError)` needed no change.

- The CLI README called its sample block the "complete output" when the
  error envelope follows it on every non-zero exit. Now scoped to "the
  complete report", with the envelope named and linked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UL9retfvAXRgvi6EWk4SY4
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-25 03:26:39 -04:00
commit 31bdd532a6

View file

@ -92,6 +92,10 @@ const SUBSCHEMA_KEYWORDS = [
"unevaluatedProperties",
"additionalItems",
"unevaluatedItems",
// 2019-09/2020-12: the schema the *decoded* string content must satisfy.
// A real schema position, so a bare `true` or a remote `$ref` under it is a
// real finding.
"contentSchema",
] as const;
/** Keywords whose value is an array of subschemas. */
@ -135,10 +139,11 @@ const SUBSCHEMA_MAP_KEYWORDS = [
* `$schema`, `$anchor`, `$vocabulary`: they hold or name subschemas for
* *other* schemas to reference, and constrain the current instance not at all.
*
* `format` is included even though 2020-12's default vocabulary treats it as
* an annotation rather than an assertion. A schema declaring one is plainly an
* attempt to constrain, and reporting it as accepting anything would be noise
* rather than a finding an author can act on.
* One category is admitted despite being annotation-only in 2020-12's default
* vocabulary: `format`, and the three content keywords (`contentMediaType`,
* `contentEncoding`, `contentSchema`). A schema declaring any of them is
* plainly an attempt to constrain, and reporting it as accepting anything
* would be noise rather than a finding an author can act on.
*
* Defining the rule this way, rather than as "has no `type`", is also
* deliberate: `{"properties": {…}}` with no `type` does constrain its input,
@ -160,6 +165,9 @@ const CONSTRAINING_KEYWORDS: ReadonlySet<string> = new Set([
"minLength",
"pattern",
"format",
"contentMediaType",
"contentEncoding",
"contentSchema",
"maxItems",
"minItems",
"uniqueItems",