fix: address Copilot review round 26 — a false property schema declines the branch

JSON Schema's boolean form is legal, but only `true` is harmless: it
constrains nothing, while `false` admits no value at all — so a field
declared with it can never be filled, and a required one makes the whole
branch unsatisfiable. Offering it contradicted every other faithfulness check
in the resolver, so such a branch is declined.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 09:42:50 -04:00
commit f31589a8f9
2 changed files with 25 additions and 10 deletions

View file

@ -764,9 +764,9 @@ describe("resolveRootUnion", () => {
}
});
it("offers a member carrying a boolean property schema", () => {
// JSON Schema's boolean form is legal and answers every keyword lookup
// with `undefined`, so it renders through the JSON fallback harmlessly.
it("offers a member carrying a `true` property schema", () => {
// `true` constrains nothing and answers every keyword lookup with
// `undefined`, so it renders through the JSON fallback harmlessly.
const { branches } = resolveRootUnion({
type: "object",
anyOf: [
@ -777,6 +777,19 @@ describe("resolveRootUnion", () => {
expect(branches).toHaveLength(2);
});
it("declines a member carrying a `false` property schema", () => {
// `false` admits no value at all, so the field can never be filled — and
// a required one makes the branch unsatisfiable.
const { branches } = resolveRootUnion({
type: "object",
anyOf: [
{ type: "object", properties: { nothing: false } },
{ type: "object", properties: { other: { type: "string" } } },
] as unknown[],
});
expect(branches).toEqual([]);
});
it("declines a member whose properties are not an object", () => {
// Members arrive as `unknown`, so this is reachable and must not throw.
expect(

View file

@ -180,15 +180,17 @@ function isOfferable(branch: RootUnionSchema): boolean {
const properties = Object.values(propertiesOf(branch));
return (
properties.length > 0 &&
// Every value has to be something a renderer can read. JSON Schema's
// boolean form is legal and harmless — `true`/`false` answer every keyword
// lookup with `undefined` — but a `null` or an array is not a schema at
// all, and the web form dereferences one on the way to choosing a widget.
// A branch carrying one is declined rather than handed on to crash a tool
// panel that would otherwise have rendered.
// Every value has to be something a renderer can read AND something a
// caller can satisfy. A `null` or an array is not a schema at all, and the
// web form dereferences one on the way to choosing a widget. JSON Schema's
// boolean form is legal, but only `true` is harmless — it constrains
// nothing and answers every keyword lookup with `undefined`, while `false`
// admits no value whatsoever, so a field declared with it can never be
// filled and a required one makes the whole branch unsatisfiable. Either
// way the branch is declined rather than offered as a callable shape.
properties.every(
(property) =>
typeof property === "boolean" ||
property === true ||
(typeof property === "object" &&
property !== null &&
!Array.isArray(property)),