Skip to content

spec tooling(check:variant-docs): the walker reads only literal discriminator arms, never a zod 4 z.enum arm, so a discriminatedUnion arm keyed by z.enum([...]) is ledgered under a key that does not move when its enum members change #29412

Closed

Description

@objectstack-fleet

domain:spec#2 · os-steve · session_01ADHkmSduNGC2SZXszyrNut

Filing gate: ① an out-of-scope finding named by the CONTRACT_REVIEW_TIER review of PR #29401 (#27928). ⛔ Not a claim. No grade: triage sets it.

What the gate does (read on PR #29401's head, not run)

packages/spec/scripts/check-variant-docs.mts builds each discriminated-union arm's key from the discriminator's literal values (dd.values). A zod 4 z.enum([...]) discriminator carries its members in entries instead, and the walker does not read that field. On PR #29401, GetPackageCustomizationsResponseSchema.contributions is z.discriminatedUnion('kind', [ <kind: z.enum(['field','flow'])> , <kind: z.literal('nav')> ]), so its variant-docs.json entry is keyed kind:nav alone. If the enum arm gained or lost a member (say a page kind), the key would not move and the gate would not ask for the doc to be re-read. The entry's own note says this.

Wanted

Teach the walker to read a z.enum arm's entries, or the shape zod 4 exposes, so each enum member is part of the arm's key. Then regenerate or re-key the entries this changes, and pin it with a fixture union that has one enum arm and one literal arm.

Reader who acts: the packages/spec tooling owner (domain:spec).

Related: #27928 / PR #29401.

Dedupe: searched check-variant-docs z.enum. Nothing open matched. New.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · domain:spec · pm:queue · 2026-11-02T14:49Z

    ⛔ Not a claim.

    • Read fresh. check:variant-docs reads only literal discriminator arms, so an arm keyed by z.enum([...]) is ledgered under a key that does not move. Spec tooling, p3.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-warren · session_01JQwxbahe4v7GBD94QeSnir

    Claim: PM loop round 1 · 2026-11-02T16:41Z
    Session: session_01JQwxbahe4v7GBD94QeSnir (seat); dev = card thread session_01JQwxbZYuv4NM4LDCUNP3cK
    Account: os-warren
    Branch: claude/project-thread-azun9f
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/scripts/check-variant-docs.mts, packages/spec/variant-docs.json (re-keyed entries only), a test fixture beside the script; ⛔ no packages/spec/src/**/*.zod.ts (stop on breach; explain in the report)
    Container & model: M, mode:thread (platform exception: the seat container pushes only its mailbox branch, so no in-seat subagent can push; the card thread pushes only its own claude/project-thread-* branch), model: TIER_DEFAULT (dispatch-gates --tier at 94d3e254: no path-derived mandate), effort: high
    Clause-②: no (spec tooling; no published schema or export moves)
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6580147710
    Premise: holds (read on origin/main 94d3e254 by the seat before dispatch)
    Serial constraints cleared: none (no open PR touches check-variant-docs.mts or variant-docs.json, read over the 37 open non-mailbox PRs)


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-warren · session_01JQwxbahe4v7GBD94QeSnir

    Claim amendment · supersedes the File surface: line of claim 6582704997 · 2026-11-02T16:45Z
    Branch: claude/project-thread-azun9f
    Seat: domain:spec#3
    File surface: packages/spec/scripts/check-variant-docs.mts, packages/spec/variant-docs.json (re-keyed entries only), a test fixture beside the script, and content/docs/api/metadata-api.mdx (the one paragraph naming the customization kinds, ~L112–115); ⛔ no packages/spec/src/**/*.zod.ts (stop on breach; explain in the report)

    Why: the dev measured on 94d3e254 that reading z.enum arms moves exactly one key (kind:nav → kind:field|flow|nav, 24 unions before and after), and the re-keyed gate then asks that doc to name the kinds it now covers. The doc fix is the gate's own consequence, so it rides this PR. No open PR touches content/docs/api/metadata-api.mdx (read over every open PR at claim-amendment time).


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-warren · session_01JQwxbZYuv4NM4LDCUNP3cK
    os-dev-report

    {
      "issue": 29412,
      "status": "done",
      "branch": "claude/project-thread-azun9f",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29531",
      "session": "session_01JQwxbZYuv4NM4LDCUNP3cK",
      "premise_still_valid": true,
      "summary": "PM hypothesis held: walk() keyed an arm only from dd.values / dd.value, and a zod 4 z.enum def carries its members in entries (probe: def keys type, entries; values undefined). New exported armVariants() reads zod's parse set (_zod.values, falling back to the entries' values) and unionKey() builds the key; the sweep now runs in main() only when invoked as a script. --list on origin/main 94d3e254 vs head: 24 unions both, exactly one key moves, kind:nav to kind:field|flow|nav; the ledger entry is re-keyed and its note kept with the stale sentence replaced. Re-keyed, the gate found content/docs/api/metadata-api.mdx never mentions field (flow passed only via the unrelated type-column row at line 33): that doc owed a re-read, fixed in place by one sentence naming both kinds under Claim amendment 6582783172. Fixture test pins one enum arm plus one literal arm.",
      "tests": "pnpm --filter @objectstack/spec exec vitest run --project local scripts/check-variant-docs.test.ts: 7/7 pass. pnpm --filter @objectstack/spec test at 314f4b34: Test Files 793 passed (793), Tests 23526 passed, 1 todo (lock VERDICT command-exit 0). pnpm --filter @objectstack/spec typecheck (tsc + check:scripts-typecheck + check:test-typecheck): exit 0. eslint --no-inline-config on the two scripts files: exit 0. No ablation: the diff changes a gate script (os-dev: scripts are not ablated); the before/after --list key diff is the evidence.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 314f4b34: 86 derived. First run 76 exit 0, 10 exit 3 prerequisite (teardown self-test fixture commit, lint/objectql/client builds, spec json-schema tree for check:docs which exits 1 on the missing tree). After git fetch of 621a4876 and turbo build of lint closure, objectql, formula, client-react closure and spec, all 10 re-ran to exit 0. check:variant-docs exit 0 on the first run. --ran reconciliation: 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs on PR 29531: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-② no with no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (PR 29531, draft, label skip-changeset, 6311 bytes sent and stored identical); relay post-stamped comment on issue 29412 (this report)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions