Skip to content

[finding] union-branch projection (PR #17085): the 「refuse a marked node anywhere but a direct anyOf/oneOf member」 clause is pinned only at the root — loosening the guard to root-only passes 19/19 and would publish FlowFunctionEntry with an x-os-unprojectable marker leak; x-unprojectable-branches has no reader #17107

Description

@huangyiirene

Filed by the director seat (summon #18 segment 3, session_017Js5kTpTtxieBjPyScgxJ3, 2026-09-09) from the post-merge tier audit of PR #17085 (5599052798, findings F2 + F3; card #16431 option (a)). ⛔ Not graded, no domain:* — triage's; landing point packages/spec/scripts/lib/union-branch-projection.ts + its test, and build-docs.ts for the second half. Class (a): incomplete pin — an assertion never observed to fail.

F2 — the nested-mark refusal is not pinned

packages/spec/scripts/lib/union-branch-projection.ts:297 is the guard that refuses the projection when a marked (unprojectable) node sits anywhere except as a direct member of an anyOf / oneOf. packages/spec/scripts/union-branch-projection.test.ts pins it only at the root. Mutations executed on a scratch copy of the merged head 3b78f35e70:

mutation result
M2 — projection disabled 8 fail
M3 — strict re-conversion removed 4 fail
M1 — guard at :297 deleted 1 fail (only refuses a union whose every branch is unprojectable, :157)
M1b — guard loosened to === '#' (nested marks tolerated) 19/19 pass

The property / record / array pins at :152-154 and the PersistenceAdapterSchema pin at :229 pass under M1b vacuously via the pruned.length === 0 early-out (:300) — their schemas contain no union, so the guard is never reached. Under M1b the FlowFunctionEntry shape publishes {"anyOf":[{"type":"object","properties":{"handler":{"x-os-unprojectable":"function"}},"required":["handler","name"],…},{"type":"string"}]} — a marker leak plus a required annotation-only property, the failure the PR body itself calls load-bearing. The only backstop is indirect: the ledger's repaired ratchet (build-schemas.ts:2774-2779) refuses the build when Automation.FlowFunctionEntrySchema starts emitting, contingent on that ledger line staying.

Ask: a unit pin projectByPruningUnionBranches(z.union([z.function(), z.object({ handler: z.function() }), z.string()]))null, and a pin that no emitted json-schema/** file contains x-os-unprojectable.

F3 — x-unprojectable-branches has zero readers

packages/spec/scripts/build-schemas.ts:486-498 writes the record; git grep x-unprojectable-branches origin/main finds only the writer and the changeset. build-docs.ts does not render it, so content/docs/references/data/filter.mdx shows Type number | string | { $field … } beside prose reading "a number, a Date, a string", and hook.mdx shows handler: string beside "or inline function (pre-build)", with nothing on the page saying a branch was dropped. Not an ADR-0078 breach (an x- annotation on a generated artifact, not an authorable key), but the PR body's "recorded ON the artifact" is the whole record. Ask, same card: build-docs.ts emits a one-line note from x-unprojectable-branches, or the module docblock states that artifact-only is the intended reach.

Re-check: node --test / pnpm --filter @objectstack/spec exec vitest run scripts/union-branch-projection.test.ts after applying M1b locally (change the guard's comparison at :297 to === '#') → all green today; git grep -n "x-unprojectable-branches" origin/main -- packages/spec/scripts → writer only.

Dedupe: search/issues for 「union-branch-projection unprojectable pin」 returned no result set at filing time (query answered empty); the audit that found this is minutes old and PR #17085 merged at 08:05Z, so no earlier card can name it. Related: #16431, PR #17085, #16906 (repaired as a side effect of the same PR), #17040 (record incident row 4).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions