Skip to content

34 backfilled contracts carry a legal_owner, and is_backfilled says on the same row that no review ever happened #53

Open

Description

@zhuangjianguo

Found while measuring #47. ⚠️ This is a semantics question, not a defect report: one of the two readings below is right and the fixture currently asserts the other without saying so.

Measured

claude/issue-47-seed-legal-owner, from the compiled artifact:

contracts with a legal_owner            68
  of those, is_backfilled = true        34
  of those 34, review_started_at        null  (all 34)

So a third of the corpus carries a named lawyer on a row that also says, in its own columns, that nobody ever reviewed it.

The contradiction, in the declarations' own words

  • clm_contract.legal_owner, src/objects/contract.object.ts: description "The lawyer who accepted the review. Required before a submitted contract enters review."
  • clm_contract.is_backfilled, DESIGN.md §13 Q8: 补录的已签合同 — an already-executed contract entered after the fact, which starts active and skipped review and approval. src/data/plan-contracts.ts says the same at length: the backfilled population "claims nothing they do not have … no submitted_at, no review_started_at, no approved_at, no review, no deviation and no signature round".
  • F16 (executed_upload) is the action that produces such a contract in the running app, and it assigns no legal owner.

The fixture's rule is hasLegalOwner = pastSubmission && type.requiresLegalReview && status !== 'submitted', which does not consult backfilled. That is why the two claims sit on the same row.

The two readings, and what each costs

  • A. legal_owner means "the lawyer who accepted the review". Then a backfilled contract must not carry one, and hasLegalOwner gains && !backfilled. ⚠️ Cost, and it is real: DESIGN.md §10 draws the expiring corpus from the backfilled contracts, so those 34 rows are precisely F12 renewal_notice and F13 expiration_sweep's population. Both copy legal_owner beside owner_id, so every renewal and expiry notice would drop from two recipients to one — the opposite direction from The demo makes M3's reminder layer look broken: 54 of 120 contracts have no legal_owner, so four of six scheduled jobs notify nobody #47.
  • B. legal_owner means "the lawyer who owns this file in legal". Then the data is right and the field description is what is wrong, and it should say so — custodianship, with the review-acceptance sentence demoted to how the column is normally first written.

B is the cheaper answer and, for a records-keeping product, arguably the truer one: a backfilled contract still has somebody in legal responsible for it. But that is a product decision about what a column means, not a developer's, and either way something has to change so the row stops asserting both.

Not urgent, and ⛔ not a rider

Nothing measured is broken: no gate, no flow and no screen misbehaves. It is on the "places the product lies" list from #45 — a column that says one thing while the column next to it says the opposite — which is why it is filed rather than left in a PR body.

Related

#47 (where this was measured; it deals this column but deliberately does not change WHICH rows carry it) · #39 / PR #42 (F12/F13, and F16 which creates backfilled contracts) · #45 (the dogfood pass and its "places the product lies" category) · DESIGN.md §13 Q8

Activity

  1. zhuangjianguo commented on Sep 10, 2026

    @zhuangjianguo
    CollaboratorAuthor

    维护者速读

    分诊:收进决策箱并补上入箱必带的四棱块与速读。⛔ 本席不裁 —— 见下面「为什么不能自裁」。

    事情:三分之一的种子合同(34 份补录合同)上写着一位「法务负责人」,而同一行的另一列说这份合同从来没有被审查过。两句话不可能都对。

    为什么会这样:legal_owner 的字段说明写的是**「接受了审查的那位律师」。而补录合同(is_backfilled)按设计是已经签好、事后录入、跳过审查与审批**的 —— 它们没有 review_started_at、没有审查记录、没有签署轮次。种子在分配 legal_owner 时没有看 backfilled 这一列,于是两个声明落到了同一行上。

    你要做的:回一个字母,A 或 B。

    做法 客户感知到的后果
    A 认定 legal_owner 就是「接受审查的律师」⇒ 补录合同不该有,种子加一条 && !backfilled 数据变诚实。⚠️ 代价是真的:设计方案 §10 的「即将到期」语料正是从补录合同里抽的,而 F12 续签通知与 F13 到期扫描都会连带抄送 legal_owner ⇒ 每一条续签与到期通知从两个收件人掉到一个 —— 与 #47 刚做的事方向相反
    B 认定 legal_owner 是「法务这边归口负责这份档案的人」⇒ 数据是对的,错的是字段说明,改说明 一句话的改动,通知收件人不受影响。代价:承认那句已发布的字段说明从一开始就写窄了

    业务含义直译

    A = 「律师」这个栏位专指审过这份合同的人,没审过就该空着;B = 那是这份档案在法务的归口人,合同怎么进来的不影响谁负责它。

    四棱分析

    os-decision-facets

    ① 项目长远合理性 — 偏 B。对一个档案管理型产品来说,一份合同在它整个生命周期里都该有法务归口人,而不只是在审查那一刻。选 A 之后,补录合同在法务侧无人负责 —— 而那恰恰是会到期、会需要续签决策的那一批。那是个架构上的洞,不只是少个收件人。

    ② 实际业务拉动 — 偏 B,且这一棱有实测。F12/F13 今天就在抄送 legal_owner,#47 刚测过收件人从 5 人变 7 人。选 A 会让续签与到期通知倒退回单收件人,方向与刚落地的改进相反。

    ③ 防 AI 犯错 — ⚠️ 这一棱内部打架,必须说破。仓规是「协议为基准:spec 与代码不一致默认改代码对齐」,照字面指向 A(改数据去迁就已声明的说明)。但那条规矩预设了声明是对的;这张卡问的正是那句说明是不是从一开始就写窄了。往深一层看:B 之后 legal_owner 在 F12/F13 里被正确使用;A 之后那两个流程会去抄一个经常为空的列 —— 让下一个作者以为「抄了就有收件人」。所以字面规矩指 A,防错的实质指 B。⛔ 我不替你在这两者之间选。

    ④ 创业阶段不扩散 — 偏 B。B 改一句话。A 改种子逻辑、丢收件人,还要重新论证 §10 的到期语料从哪来。

    推荐:B,并要求同批把字段说明改成「法务归口人」并把「审查时首次写入」降格为一句用法说明。回退:选 A 就要一并接受 F12/F13 退回单收件人,并说明 §10 的到期语料改从哪里抽。

    ⛔ 本分析看不见什么:你们真实法务流程里,补录进来的旧合同有没有人接手。如果现实里就是没人管,A 才是诚实的;如果有归口人,B 是。这一个事实定这张卡,而它只在你那里。

    为什么这张不能自裁

    「说明书脱节」那条自裁通道的前提是实现没被裁错。这里恰恰相反:实现是对是错,正是待裁的那个问题 —— B 说数据对、说明错;A 说说明对、数据错。⛔ 通道够不到。而且它动的是已发布的字段说明,属公开契约语义。

    不阻塞任何在飞卡。

    关联

    #47(发现处,已合并 d34b225;它刻意只分配这一列、不改哪些行携带它)· #39 / PR #42(F12/F13 与创建补录合同的 F16)· #45(「产品说谎的地方」那一类)· DESIGN.md §13 Q8、§10


    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

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions