Skip to content

[Decision] Should mapDataError's DUPLICATE_RECORD arm keep its name === 'DuplicateRecordError' envelope gate, or key on the code alone like its relay siblings? #15070

Description

@os-trump

Escalated by the domain:cli seat out of #14704 (PR #15065). ⭐ The dev refused a clause of its own dispatch and escalated rather than guessing, and the refusal is correct — I verified the load-bearing measurement myself before writing this card.

Why this card exists

#14704's dispatch — and the triage comment it inherited the clause from — told the implementer to converge DUPLICATE_RECORD onto the shared two-read message rule "rather than keeping its local guard". That instruction rested on a premise, and the premise is false.

Triage's stated reason: the guard exists "specifically so a sandbox-thrown registered code keeps the unwrap door's sentence". The effect is right. The mechanism is not what that sentence implies:

packages/runtime/src/sandbox/quickjs-runner.ts:1568
  constructor(message, innerMessage?, info?) {
    super(message);
    this.name = 'SandboxError';        // ← UNCONDITIONAL, the line after super()
    if (info?.code)   this.code   = info.code;      // ← conditional
    if (info?.fields) this.fields = info.fields;    // ← conditional
    …
  }

⇒ A sandbox body throwing the registered code arrives with code === 'DUPLICATE_RECORD' and name === 'SandboxError'. No sandboxed producer can reach that arm at all, by construction. Verified on origin/main with a positive control on the same pathspec.

Two consequences, and they are why this could not be settled inside #14704:

  • Option C is provably non-viable. Keeping the gate and applying the two-read rule inside the arm gives a check that evaluates never — green forever, protecting nothing, indistinguishable from a real guard to the next reader. AGENTS.md names a phantom check as a defect in its own right.
  • ⚠️ The dispatch's clause was not a message-source convergence at all. structuredCodeAnswer holds two classes of arm, and error-response.ts's own docblock says the difference is load-bearing: relay arms pass error.message through; this is a replace arm — it substitutes a curated sentence, re-spells the wire code to UNIQUE_VIOLATION, and moves the producer's sentence into developerMessage. For a replace arm there is no producer sentence being sourced, so dropping the gate converges which producers the arm claims, not where its sentence comes from. Different question, published wire cost.

The options

A — keep the envelope gate (what PR #15065 ships). #14389 §5 stands unchanged. A sandbox or plugin producer speaking DUPLICATE_RECORD keeps its own sentence and its own code. If "one rule, one place" should be enforced mechanically, it is enforced by a structural pin — the arm is provably unreachable for a sandbox producer because SandboxError fixes name — not by widening the gate. The omission is disclosed in the new pin file's §6 allowlist rather than left silent.

B — drop the name gate; the arm keys on code alone and its developerMessage reads the shared rule. Reverses #14389 §5 and turns its two pinned cases red on purpose. Wire cost: a sandbox body throwing DUPLICATE_RECORD loses its sentence to the curated one and its code to UNIQUE_VIOLATION; a plugin merely declaring the code loses the same two. ⚠️ Needs its own contract review, and touches a code whose wire spelling is already under an open decision, #14723.

C — keep the gate and apply the rule inside it. Measured non-viable, above.

四维分析

长期健全性 —— 指向 A,而且其余三维不构成反对。 structuredCodeAnswer 里并存两类臂:relay(转发作者句子)与 replace(替换成平台句子)。两读规则收敛的是句子来源,这个概念只对 relay 臂有定义;对 replace 臂而言根本没有「被取用的作者句子」。所以 B 不是把规则收敛,而是把闸门换掉 —— 换的是「这条臂认领哪些生产者」。在一张讲消息来源的卡里回答这个问题,正是 Prime Directive #12 所警告的「第二份事实上的契约」;而且它在没有任何更高记录的情况下推翻一份 2026-09-02 已过契约复审的裁决,这超出 dev 座位、也超出本 PM 座位的权限。

真实业务需要 —— 未测到,指向 A。 B 会新捕获的那两类生产者,今天拿到的正是 #14389 为它们刻意选择的答案。仓内没有生产者、没有报告、没有卡片要求「用平台句子替换作者句子」。⇒ B 是无拉力的行为扩张。

让 AI 写的应用不易出错 —— 强烈指向 A。 应用作者在 hook 里抛出这个已注册的 code,A 下他拿回自己的措辞;B 下他的句子被静默替换、code 被改写成 UNIQUE_VIOLATION。这是消费方重写作者的声明,正是 contract-first 禁止的方向。⚠️ 更糟的是,B 之后行为取决于一个跨过 VM 边界的对象的 class —— 作者既看不见也控制不了它。

创业阶段范围纪律 —— 指向 A。 A 的成本为零、不增加任何面;B 是带已发布 wire 代价的行为扩张,落在一个拼写本身已在开放决策 #14723的 code 上。

建议:A。 ⛔ 若维护者仍要 B,它是一张独立决策卡 + 自己的契约复审,⛔ 不能作为任何 PR 的搭车项。

What is NOT blocked by this

#14704's actual defect — ten code-gated arms shipping the QuickJS debug wrapper as erroris closed and green in PR #15065, which implements option A (i.e. leaves DUPLICATE_RECORD untouched). Landing that PR changes nothing about this question in either direction, so this card does not hold it up.

Refs

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions