Skip to content

A scalar data.id beside a NON-scalar where.id still silently drops the predicate — the remaining silent shape after the #11142 refusal, pinned by #5748 #11230

Description

@os-zhuang

Found while implementing #11142 (the unequal-scalar where.id refusal). Filed separately and unassigned: acting on it would reverse another #5748-pinned verdict (a SCALAR data.id still outranks where and multi), which is a maintainer decision, not a rider on that card — the same reason #11142 itself was filed off #11009.

The shape

await engine.update(obj, { id: 'rec_1', title: 'x' }, { where: { id: { $in: ['a', 'b'] } }, multi: true });

resolveEngineUpdateDispatch rules by-id with the payload id bound (rec_1): the truthy scalar data.id outranks where and multi alike (#5748 ruling A, pinned in engine-update-dispatch.test.ts — a SCALAR data.id still outranks where and multi, first assertion). The $in predicate over ids — and the declared multi: true — are silently discarded. Same for an array or null where.id beside a scalar payload id.

Why #11142 did not cover it

#11142's ruling scoped the refusal to a truthy scalar where.id different from the payload id ("truthy scalar where.id 响亮拒绝"), and its PR pins the non-scalar neighbour explicitly as out of the ruled scope so the refusal cannot creep. #11009's unhonoured-keys refusal does not reach it either: the only where key is id. So after both refusals land, this is the last where.id-shaped declaration the by-id update path drops with no diagnostic.

What a fix has to decide

  1. Refuse a payload-sourced by-id dispatch whose where.id is a non-scalar predicate (same loud-refusal family). Reverses the remaining half of the ObjectQL.update 的 data.id 不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式 options.multi: true #5748 pin — needs a ruling.
  2. Leave it, on the grounds that a payload id is the row address and a caller that also writes an id-set predicate wrote a contradiction too rare to arm. That keeps one silent shape in a family otherwise made loud (ObjectQL.update 的 data.id 不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式 options.multi: true #5748 payload operator-objects, A compare-and-set where on a by-id update is silently inert — the extra predicate keys never reach the driver, and SqlHttpOutbox.redeliver's status guard is one of them #11009 extra keys, A scalar data.id beside a DIFFERENT scalar where.id silently drops the where.id predicate — the one unhonoured-predicate shape #11009's refusal deliberately left standing #11142 unequal scalars).

Worth noting for the triage: unlike #11142's shape (which could at least be spelled by folding ingresses), no ingress in this repo constructs this pair — the ingress survey on PR #11229 found the non-scalar where.id production sites (outbox sweeps) never carry a payload id.

Refs: packages/metadata-core/src/engine-update-dispatch.ts (the #11142 boundary comment names this shape as deliberately untouched), #5748, #11009, #11142.


Generated by Claude Code

Activity

  1. added theissue type on Aug 23, 2026
  2. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor

    Triage: needs-user-decision, domain:engine, Task — acting on this reverses the remaining half of the #5748-pinned verdict (a SCALAR data.id still outranks where and multi), and a pin flip on a ruled behaviour is the manual floor regardless of how small the diff is.

    [facets-block]

    一句话问题:by-id 更新路径上「标量 data.id 旁站着非标量 where.id 谓词」这最后一个静默丢弃形状,是补进响亮拒收家族,还是按「payload id 即行地址、矛盾对太罕见」接受留存?

    选项 代价
    1 响亮拒收(同家族错误码/文案),翻转 #5748 pin 的剩余半条 反转一条已裁 pin(须按翻转纪律:删除/反转断言,⛔ 不修绿;随卡带全仓 pin 清扫两句);外部调用方若依赖旧静默行为会开始收到拒绝
    2 维持现状,把「deliberately silent」写进边界注释与 pin 文案,家族宣言带一个具名例外 家族不变量永久带例外;未来读者需要知道为什么这一臂例外

    推荐 1(置信度高):四轴同向——防 AI 轴与长远轴强推,业务轴的「零仓内构造」恰好说明拒收的破坏半径为零,创业轴成本可忽略。裁决格式:回「1」/「2」;1 ⇒ 晋级 pm:queue(engine 席,派发词按 pin 翻转纪律带全仓清扫条款),2 ⇒ 关闭本卡、注释+pin 文案的具名例外化开 S 卡。


    Generated by Claude Code

  3. claude commented on Aug 23, 2026

    @claude
    Contributor

    Maintainer ruling recorded (atomic four-part write)

    Ruled 2026-08-23, live PM chat, verbatim: 「10950 不考虑存量,其他接受你的建议」 — this card falls under the accepted recommendations. Ruled: option 1 — the scalar-data.id-beside-non-scalar-where.id shape joins the loud-refusal family (same error-code/wording family as #5748/#11009/#11142). needs-user-decision → pm:queue in the same stroke. Dispatch riders (pin-flip discipline): the remaining half of the #5748 pin (a SCALAR data.id still outranks where and multi) is deliberately REVERSED — delete/invert the assertion, never green-wash it; the dispatch carries the repo-wide pin-sweep two-liner.


    Generated by Claude Code

  4. self-assigned this
    on Aug 23, 2026
  5. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor

    Claim — engine seat, session session_01RfyXxZ2WPjcjhuXpiQQc3y, branch claude/issue-11230-scalar-data-id-nonscalar-where-refusal. pm:queue → pm:dispatched in one label write, read back.

    Dispatched on the maintainer's ruling of 2026-08-23, recorded on this card. Quoted verbatim, not paraphrased:

    「10950 不考虑存量,其他接受你的建议」

    ⇒ option 1: this shape joins the loud-refusal family (same error-code and wording family as #5748 / #11009 / #11142). The dispatch order carries the two riders the ruling attached, and both are stated to the dev as non-negotiable:

    1. The remaining half of the ObjectQL.update 的 data.id 不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式 options.multi: true #5748 pin is deliberately REVERSED — a SCALAR data.id still outranks where and multi. Delete or invert the assertion. ⛔ Never green-wash it, never adjust it until it passes. A pin that was ruled to flip must visibly flip.
    2. The repo-wide pin sweep two-liner rides along, so no other pin is left asserting the old silent behaviour.

    The seam this card will hit, named before it is hit

    #11142 — the immediately preceding member of this family — landed in packages/objectql/src/engine-update-dispatch.ts, packages/metadata-core/src/engine-update-dispatch.ts and packages/objectql/src/engine.ts, and appended to packages/spec/src/api/error-code-ledger.zod.ts. That last file is domain:spec territory, and whether cross-lane error-code ledger appends are pre-approved as a class is a standing open question this seat has already raised (#6017).

    Instruction to the dev: follow the #11142 precedent exactly — same family, same mechanics, and the ruling itself says "same error-code/wording family" — and name the spec-file touch prominently in the PR body so the spec seat can object on the record if it disagrees. ⛔ Do not invent a new error code shape, and ⛔ do not touch anything else in packages/spec.

    Scope note

    The card's own four-axis analysis measured zero in-repo constructors of the contradictory pair, which is what makes the blast radius of refusing it approximately nil — an external SDK caller can still write it, and today that silently drops both the predicate and multi: true. That is the argument, and it is the card's, not an inflation of it: the business axis reads "no present pain", and the case rests on the AI-error axis and family consistency.

    Serialization

    Dispatched alongside #11374 (driver-sql MySQL schema sync), which shares no file with this card. All 10 currently-open PRs are in spec / docs / rest / qa — none touches objectql or metadata-core. Measured.

    ⚠️ #11293 is deliberately NOT dispatched in this batch. Its landing site (hook-body ctx accessor) is not yet established, and metadata-core/src/engine-update-dispatch.ts — this card's file — appears in the hook-dispatch grep. Rather than assert a disjointness I have not measured, that card waits for the next round. This seat made exactly that mistake earlier today on #11201 and is not repeating it.

    Duplicate probe, both legs positive-controlled: no open PR claims this number, and git ls-remote origin 'refs/heads/*issue-11230*' is empty against 98 live *issue-* branches.


    Generated by Claude Code

  6. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 11230,
      "status": "done",
      "branch": "claude/issue-11230-scalar-data-id-nonscalar-where-refusal",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11432",
      "premise_still_valid": true,
      "summary": "resolveEngineUpdateDispatch now refuses a by-id update whose bound truthy scalar payload data.id stands beside a DECLARED but non-scalar options.where.id (operator object, array, null, explicit undefined): UPDATE_ID_MISMATCH + status 400, raised at dispatch so nothing reaches the driver. That call previously wrote one row and discarded BOTH the declared row set and any declared multi:true with no diagnostic - the last silent arm of the #5748/#11009/#11142 family. Boundaries kept and newly pinned: a FALSY scalar where.id (0, '') stays by-id (#11142's truthiness boundary), a where declaring no id key is untouched, and with no scalar payload id the ladder is exactly as #5748 left it. DIVERGENCE FROM #11142, deliberate and flagged prominently in the PR body: I registered NO new error-code ledger member. #11142 appended UPDATE_ID_MISMATCH; I reuse it with a second message composer and widen the existing ledger entry's COMMENT only. Reason: one defect class (the call declares the row address twice and the declarations cannot both hold), one caller remedy (drop one spelling), so a second code buys no consumer an actionable branch while growing the closed ADR-0112 code vocabulary every AI-authored consumer must switch over - the direction D2's 2026-08-18 amendment moved away from. Consequence: packages/spec's diff is comment-only, no enum member moves, so NO generated artifact needed regeneration (contract.mdx and error-code-ledger.mdx untouched) and the error-code + docs-generation gates are green without a regen. If the spec seat or maintainer prefers a distinct member it is a one-line append plus check:generated --fix. Nothing else in packages/spec is touched. Two bounded in-place sweep fixes ride along in packages/metadata-protocol (comment-only, see out_of_scope_findings).",
      "tests": "All at final HEAD 9ea03d8623, every exit status captured before any pipe. SUITES: objectql `pnpm --filter @objectstack/objectql test` -> 'Test Files 229 passed (229) / Tests 4077 passed (4077)'; metadata-core 'Test Files 12 passed / Tests 177 passed'; metadata-protocol 'Test Files 139 passed | 2 skipped / Tests 1892 passed | 10 skipped'. TYPECHECK: metadata-core, objectql, spec -> os-verify-lock VERDICT command-exit 0. LINT: repo-wide `pnpm lint` (eslint . --no-inline-config, whole tree) run IN FULL -> VERDICT command-exit 0, no output, no narrowing claimed. NEW PINS: a [#11230] describe (predicate verdict; assertEngineUpdateDispatch throw so every pinned fake inherits it; real engine throw with calls == []; every non-scalar spelling with the message naming the kind; the surviving boundaries), plus 5 new ENGINE_UPDATE_DISPATCH_CASES rows. Refusal assertions pin code AND status per ADR-0112, never a bare toThrow. VACUITY CONTROLS: two CONTROL tests assert the SAME call minus one half of the contradiction still succeeds - without the payload id it is a multi write, without where.id it is a by-id write on rec_1 - so the refusal pins cannot pass by never constructing the pair. REVERSE-VERIFICATION, direction predicted BEFORE each leg, fix committed first so the restore had a recovery point, mutation script carried trap '<restore>' EXIT INT TERM. Rebuild is load-bearing: packages/objectql has NO vitest config, so @objectstack/metadata-core resolves through exports to dist/; an unrebuilt ablation would have stayed GREEN and certified a vacuous pin. LEG 1 (guard removed, metadata-core REBUILT) predicted RED in exactly the two flipped pins + the 4 new case-set rows + the [#11230] describe, with both CONTROLs still green; OBSERVED RED: 'Tests 10 failed | 61 passed (71)', and the 10 named failures are exactly those, neither CONTROL among them. LEG 2 (restored, REBUILT) predicted GREEN 71/71; OBSERVED 'Test Files 1 passed (1) / Tests 71 passed (71)'. MUTATION PROVEN ON DISK, anchored on the text intended to change: removed-marker count 0, injected-marker count 1, git diff --stat '1 insertion(+), 11 deletions(-)'. PROVEN IN THE ARTIFACT: `node scripts/ablation-dist-preflight.mjs @objectstack/metadata-core '<marker>' --absent' -> 'marker absent from all 18 built files'; restore leg -> 'marker present in 2 built files'. RESTORE BYTE-IDENTICAL: git hash-object 2a10f964b065ffa1600e9adf3f870a66b12f36fc == the committed blob (not an insertion count). VOID RUN, DISCLOSED: the first ablation attempt passed --reporter=basic, which vitest 4 does not have; BOTH legs died at reporter load (ERR_LOAD_URL) before running a single test. That run measured nothing, its readings were discarded rather than reused, and the numbers above are the re-run. GATE UNION derived by `node scripts/pm/dispatch-gates.mjs` with NO paths passed (it took its own change set from merge base 122ef3860: 8 paths; 28 path-matched families + 6 convention-triggered). Every family run, per-gate exit status BY NAME: changeset-gate-self-tests 0 | cross-package-test-inputs 0 | dispatcher-error-vocabulary 0 | doc-formula-expressions 0 | durability-log-level 0 | spec:empty-state 0 | error-code-casing 0 | filter-alias-parity 0 | spec:liveness 0 | merge-driver 0 | objectui-changeset 0 | published-files 0 | slot-lookup 0 | spec-parsed-alias 0 | stack-collection-maps 0 | spec:strictness-ledger 0 | test-source-alias 0 | type-source-resolution 0 | spec:variant-docs 0 | check-adr-0087-registration 0 | check-changeset-no-major 0 | check-ci-filter-parity 0 | check-cross-package-test-inputs 0 | check-dev-prereqs 0 | check-empty-changeset 0 | check-engine-split-ratio 0 | check-plugin-teardown-shape 0 | docs-audit/check-affected-docs 0 | query-options-erasure 0 | type-check-coverage 0 | type-check-debt --re-measure 0 | engine-double-contract 0 | where-matcher 0 | nul-bytes 0. TWO needed an environment repair first, neither a defect in this diff, both reported rather than quietly re-run: check-dev-prereqs was EXIT=1 on an unbuilt workspace (52 of 67 packages had no dist entry point) and went 0 after the full `turbo run build --filter=./packages/* --filter=./packages/*/*` (70/70 successful); check-engine-split-ratio was EXIT=2 REFUSING to compute the ADR-0076 D7 metric on a shallow clone and went 0 after `git fetch --shallow-since=2026-05-18 origin` (metric 97.6%, unrelated to this diff). Note for the PM's own budget: the first full-workspace build hit the container's ~10 minute foreground cap (SIGTERM, exit 143) with 3 turbo tasks outstanding; the resume run finished in 32s off turbo cache.",
      "open_questions": [
        {
          "question": "Should the non-scalar-where.id refusal carry its OWN ADR-0112 ledger code, or keep sharing #11142's UPDATE_ID_MISMATCH as shipped here? This is the one place I diverged from the #11142 precedent the dispatch named, so it is raised for the record rather than decided silently - the code is shipped, and either answer is a small edit.",
          "options": [
            "A (shipped) reuse UPDATE_ID_MISMATCH with a second message composer, widening the ledger entry's comment only - packages/spec diff stays comment-only, no enum member, no generated artifact moves",
            "B append a distinct member (e.g. UPDATE_ID_PREDICATE_CONFLICT) following #11142 literally - one-line ledger append plus `check:generated --fix` for contract.mdx and error-code-ledger.mdx"
          ],
          "recommendation": "A, because the two shapes are one defect class with one caller remedy (drop one of the two row-address spellings), so a second code buys a consumer no branch it could act on differently, while `error.code` is a CLOSED vocabulary every AI-authored consumer must switch over exhaustively (ADR-0112 D3/D4) - and D2's 2026-08-18 amendment retired three members on exactly the reasoning that an unreachable-or-redundant catalog entry teaches a branch that adds no information. The difference between the shapes rides the message, which is where ADR-0112 puts it. Secondary: A keeps the cross-lane packages/spec seam to a single comment, which the dispatch explicitly asked me to minimise. If the spec seat prefers B, it is one append plus a regen with the repo's own tooling."
        }
      ],
      "out_of_scope_findings": [
        "NOT filed as issues - brought along in-place under the bounded-fix exemption (same defect class, mechanically determined by a landed commit, comment-only, no new verification surface since metadata-protocol was already in this diff's test surface as a metadata-core consumer), and named in the PR body: packages/metadata-protocol/src/protocol.ts and packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts both cite ENGINE_UPDATE_DISPATCH_CASES' case \"a SCALAR data.id still wins over a scalar where.id\" with expectId: 'rec_1' as current behaviour. That case name was RENAMED AWAY by #11142 (squash 281069522) - i.e. #11142's own pin sweep missed them - so both lines were teaching removed behaviour. Corrected to name the #11142/#11230 refusals; no behaviour change.",
        "No new issues filed. Nothing else in the sweep or the constructor survey turned up a defect. The type-check-debt run reported 23 raw errors of pre-existing ledger surplus across 4 entries and one graduation candidate (trigger-record-change) - the gate itself names #6376 for that and it is untouched by this diff, so it is noted, not re-filed."
      ]
    }

    Generated by Claude Code

  7. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    ContributorAuthor

    os-dev-report

    ⚠️ Supersedes the previous comment on this card (#issuecomment-5387636519), which carried the same JSON but is invisible to a marker scan: GitHub's body sanitizer ate its leading <!-- os-dev-report --> marker after the write, and also swallowed two short angle-bracket fragments inside the JSON strings (trap '<restore>' … and '<marker>' --absent, both rendered as empty quotes there). This comment leads with the literal text instead and respells those two fragments. Same report, unchanged content.

    {
      "issue": 11230,
      "status": "done",
      "branch": "claude/issue-11230-scalar-data-id-nonscalar-where-refusal",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11432",
      "premise_still_valid": true,
      "summary": "resolveEngineUpdateDispatch now refuses a by-id update whose bound truthy scalar payload data.id stands beside a DECLARED but non-scalar options.where.id (operator object, array, null, explicit undefined): UPDATE_ID_MISMATCH + status 400, raised at dispatch so nothing reaches the driver. That call previously wrote one row and discarded BOTH the declared row set and any declared multi:true with no diagnostic - the last silent arm of the #5748/#11009/#11142 family. Boundaries kept and newly pinned: a FALSY scalar where.id (0, '') stays by-id (#11142's truthiness boundary), a where declaring no id key is untouched, and with no scalar payload id the ladder is exactly as #5748 left it. DIVERGENCE FROM #11142, deliberate and flagged prominently in the PR body: I registered NO new error-code ledger member. #11142 appended UPDATE_ID_MISMATCH; I reuse it with a second message composer and widen the existing ledger entry's COMMENT only. Reason: one defect class (the call declares the row address twice and the declarations cannot both hold), one caller remedy (drop one spelling), so a second code buys no consumer an actionable branch while growing the closed ADR-0112 code vocabulary every AI-authored consumer must switch over - the direction D2's 2026-08-18 amendment moved away from. Consequence: packages/spec's diff is comment-only, no enum member moves, so NO generated artifact needed regeneration (contract.mdx and error-code-ledger.mdx untouched) and the error-code + docs-generation gates are green without a regen. If the spec seat or maintainer prefers a distinct member it is a one-line append plus check:generated --fix. Nothing else in packages/spec is touched. Two bounded in-place sweep fixes ride along in packages/metadata-protocol (comment-only, see out_of_scope_findings).",
      "tests": "All at final HEAD 9ea03d8623, every exit status captured before any pipe. SUITES: objectql `pnpm --filter @objectstack/objectql test` -> 'Test Files 229 passed (229) / Tests 4077 passed (4077)'; metadata-core 'Test Files 12 passed / Tests 177 passed'; metadata-protocol 'Test Files 139 passed | 2 skipped / Tests 1892 passed | 10 skipped'. TYPECHECK: metadata-core, objectql, spec -> os-verify-lock VERDICT command-exit 0. LINT: repo-wide `pnpm lint` (eslint . --no-inline-config, whole tree) run IN FULL -> VERDICT command-exit 0, no output, no narrowing claimed. NEW PINS: a [#11230] describe (predicate verdict; assertEngineUpdateDispatch throw so every pinned fake inherits it; real engine throw with calls == []; every non-scalar spelling with the message naming the kind; the surviving boundaries), plus 5 new ENGINE_UPDATE_DISPATCH_CASES rows. Refusal assertions pin code AND status per ADR-0112, never a bare toThrow. VACUITY CONTROLS: two CONTROL tests assert the SAME call minus one half of the contradiction still succeeds - without the payload id it is a multi write, without where.id it is a by-id write on rec_1 - so the refusal pins cannot pass by never constructing the pair. REVERSE-VERIFICATION, direction predicted BEFORE each leg, fix committed first so the restore had a recovery point, mutation script carried a `trap RESTORE-CMD EXIT INT TERM` (the hard line: a foreground-cap SIGTERM cannot leave a mutated tree behind). Rebuild is load-bearing: packages/objectql has NO vitest config, so @objectstack/metadata-core resolves through exports to dist/; an unrebuilt ablation would have stayed GREEN and certified a vacuous pin. LEG 1 (guard removed, metadata-core REBUILT) predicted RED in exactly the two flipped pins + the 4 new case-set rows + the [#11230] describe, with both CONTROLs still green; OBSERVED RED: 'Tests 10 failed | 61 passed (71)', and the 10 named failures are exactly those, neither CONTROL among them. LEG 2 (restored, REBUILT) predicted GREEN 71/71; OBSERVED 'Test Files 1 passed (1) / Tests 71 passed (71)'. MUTATION PROVEN ON DISK, anchored on the text intended to change: removed-marker count 0, injected-marker count 1, git diff --stat '1 insertion(+), 11 deletions(-)'. PROVEN IN THE ARTIFACT via `node scripts/ablation-dist-preflight.mjs @objectstack/metadata-core MARKER --absent` (MARKER = the guard's own call-site text, which survives bundling verbatim) -> 'marker absent from all 18 built files'; restore leg without --absent -> 'marker present in 2 built files'. RESTORE BYTE-IDENTICAL: git hash-object 2a10f964b065ffa1600e9adf3f870a66b12f36fc == the committed blob (not an insertion count). VOID RUN, DISCLOSED: the first ablation attempt passed --reporter=basic, which vitest 4 does not have; BOTH legs died at reporter load (ERR_LOAD_URL) before running a single test. That run measured nothing, its readings were discarded rather than reused, and the numbers above are the re-run. GATE UNION derived by `node scripts/pm/dispatch-gates.mjs` with NO paths passed (it took its own change set from merge base 122ef3860: 8 paths; 28 path-matched families + 6 convention-triggered). Every family run, per-gate exit status BY NAME: changeset-gate-self-tests 0 | cross-package-test-inputs 0 | dispatcher-error-vocabulary 0 | doc-formula-expressions 0 | durability-log-level 0 | spec:empty-state 0 | error-code-casing 0 | filter-alias-parity 0 | spec:liveness 0 | merge-driver 0 | objectui-changeset 0 | published-files 0 | slot-lookup 0 | spec-parsed-alias 0 | stack-collection-maps 0 | spec:strictness-ledger 0 | test-source-alias 0 | type-source-resolution 0 | spec:variant-docs 0 | check-adr-0087-registration 0 | check-changeset-no-major 0 | check-ci-filter-parity 0 | check-cross-package-test-inputs 0 | check-dev-prereqs 0 | check-empty-changeset 0 | check-engine-split-ratio 0 | check-plugin-teardown-shape 0 | docs-audit/check-affected-docs 0 | query-options-erasure 0 | type-check-coverage 0 | type-check-debt --re-measure 0 | engine-double-contract 0 | where-matcher 0 | nul-bytes 0. TWO needed an environment repair first, neither a defect in this diff, both reported rather than quietly re-run: check-dev-prereqs was EXIT=1 on an unbuilt workspace (52 of 67 packages had no dist entry point) and went 0 after the full `turbo run build --filter=./packages/* --filter=./packages/*/*` (70/70 successful); check-engine-split-ratio was EXIT=2 REFUSING to compute the ADR-0076 D7 metric on a shallow clone and went 0 after `git fetch --shallow-since=2026-05-18 origin` (metric 97.6%, unrelated to this diff). Note for the PM's own budget: the first full-workspace build hit the container's ~10 minute foreground cap (SIGTERM, exit 143) with 3 turbo tasks outstanding; the resume run finished in 32s off turbo cache.",
      "open_questions": [
        {
          "question": "Should the non-scalar-where.id refusal carry its OWN ADR-0112 ledger code, or keep sharing #11142's UPDATE_ID_MISMATCH as shipped here? This is the one place I diverged from the #11142 precedent the dispatch named, so it is raised for the record rather than decided silently - the code is shipped, and either answer is a small edit.",
          "options": [
            "A (shipped) reuse UPDATE_ID_MISMATCH with a second message composer, widening the ledger entry's comment only - packages/spec diff stays comment-only, no enum member, no generated artifact moves",
            "B append a distinct member (e.g. UPDATE_ID_PREDICATE_CONFLICT) following #11142 literally - one-line ledger append plus `check:generated --fix` for contract.mdx and error-code-ledger.mdx"
          ],
          "recommendation": "A, because the two shapes are one defect class with one caller remedy (drop one of the two row-address spellings), so a second code buys a consumer no branch it could act on differently, while `error.code` is a CLOSED vocabulary every AI-authored consumer must switch over exhaustively (ADR-0112 D3/D4) - and D2's 2026-08-18 amendment retired three members on exactly the reasoning that an unreachable-or-redundant catalog entry teaches a branch that adds no information. The difference between the shapes rides the message, which is where ADR-0112 puts it. Secondary: A keeps the cross-lane packages/spec seam to a single comment, which the dispatch explicitly asked me to minimise. If the spec seat prefers B, it is one append plus a regen with the repo's own tooling."
        }
      ],
      "out_of_scope_findings": [
        "NOT filed as issues - brought along in-place under the bounded-fix exemption (same defect class, mechanically determined by a landed commit, comment-only, no new verification surface since metadata-protocol was already in this diff's test surface as a metadata-core consumer), and named in the PR body: packages/metadata-protocol/src/protocol.ts and packages/metadata-protocol/src/protocol.update-path-id-wins.test.ts both cite ENGINE_UPDATE_DISPATCH_CASES' case \"a SCALAR data.id still wins over a scalar where.id\" with expectId: 'rec_1' as current behaviour. That case name was RENAMED AWAY by #11142 (squash 281069522) - i.e. #11142's own pin sweep missed them - so both lines were teaching removed behaviour. Corrected to name the #11142/#11230 refusals; no behaviour change.",
        "No new issues filed. Nothing else in the sweep or the constructor survey turned up a defect. The type-check-debt run reported 23 raw errors of pre-existing ledger surplus across 4 entries and one graduation candidate (trigger-record-change) - the gate itself names #6376 for that and it is untouched by this diff, so it is noted, not re-filed."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions