Skip to content

[Decision] §06 F16 说补录动作「档案岗**或**法务可用」,但实现只能给档案岗——权限是 AND,没有两边都持有的能力 #43

Closed

Description

@zhuangjianguo

Ruled: 6395823636 · letter A · 2026-10-25T09:14Z

Blocked-by: objectstack-ai/objectstack#25643 (flow-level requiredPermissions; merged 2026-10-24, not in 18.1.0 — unlocks when a published release carrying it is installed here)
Blocked-by: the hotclm @objectstack/* 17.7.0 → 18.1.0 upgrade PR (prepared in another session, not yet opened; the seat rewrites this line to its number when it opens)

维护者速读

事情:补录已签合同这个动作(F16「Backfill Executed Contract」),设计方案第 6 章写的是档案岗或法务都能用。实现出来只有档案岗(加管理员)能用,法务看不到这个按钮。

为什么:平台的 requiredPermissions 是 AND(要求全部持有),不是 OR。而档案岗和法务没有任何一项共同持有的能力,所以「或」这个语义在今天的平台上表达不出来。实现只好退而求其次,挂在 execute_contract 上(档案岗 + 管理员持有),法务因此丢了这一个按钮 —— 但法务的其他所有路径都不受影响。

发现于卡 09(#39 / PR #42,已合并)。当时没顺手改,因为三个补法里两个要动受管面或平台,不是开发能定的。

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

选项

做法 客户感知到的后果
A(推荐) 第 4 章新增一项能力(如 backfill_contract),同时授予档案岗与法务;动作改挂它 §06 F16 字面兑现,两个岗位都能补录。代价:改受管面 §04,多一项能力令牌
B 接受只有档案岗能补录,把 §06 F16 的「或法务」改掉 零新增。代价:收窄一项已声明的能力,法务补录要请档案岗代劳
C 向平台提「requiredPermissions 支持 OR 形式」的需求,等它落地 一次修好所有应用。代价:时间不可控,期间仍是 A 或 B 的现状

业务含义直译

A = 给「补录旧合同」这件事发一把专门的钥匙,法务和档案岗各拿一把;B = 承认补录是档案岗的活,法务要补录就走档案岗;C = 等平台把门锁改成「任一把钥匙都能开」。

四棱分析

os-decision-facets

① 项目长远合理性 — 偏 A。这是一个已声明但兑现不了的能力(§06 说「或」,系统做不到「或」),和这批卡反复在修的形状同一类。A 让实现追上声明;B 削声明去迁就实现,方向相反;C 是正确的长远形态但不在本仓手里。⚠️ 但 A 有一个代价要说破:为一个按钮新增一项能力令牌,是契约增生,和「缩小特例」的方向相反。

② 实际业务拉动 — 弱,且这一棱要求实测。判据是「今天谁撞上」。答案是:没有人,因为这个动作今天刚随卡 09 出生,还没有任何真实使用。法务是否真的需要亲手补录旧合同,是一个我看不见的业务事实——如果实际流程里补录本来就归档案岗,那 §06 那个「或」从一开始就写宽了,B 才是对的。

③ 防 AI 犯错 — 偏 A/B,一致反对现状。今天是最坏的:文档说两个岗位能用,系统只给一个,且没有任何报错 —— 法务打开页面就是看不到按钮,不会有人被告知为什么。下一个作者读 §06 会以为它坏了。A 与 B 都消除这个静默落差,区别只在往哪个方向对齐。C 单独选会让落差持续存在。

④ 创业阶段不扩散 — 明确偏 B。B 零新增;A 增一项能力令牌,而每一个已声明的键都是永久义务。⚠️ 按「零拉动默认 defer 或 remove」,②的零拉动读数直接把这一棱推向 B。

推荐:A,但置信度不高,且我要指出四棱在这张卡上是分裂的。 ①③ 指向 A,②④ 指向 B。按「长远合理性权重恒 ≥50%」推荐 A;但那条权重规则同时说明它「按缩小而非扩大特例读」,而 A 恰恰是扩大 —— 所以这里我不认为权重规则给了 A 一个干净的胜利。若你知道法务实际上不需要自己补录,请直接选 B,那比 A 更符合不扩散。

⛔ 本分析看不见什么:法务岗在真实客户流程里是否需要亲手补录旧合同。这一个事实就能定这张卡,而它只在你那里。

关联

#39 / PR #42(发现处,已合并)· DESIGN.md §06 F16、§04 权限矩阵(受管面)· src/actions/contract-backfill.actions.ts(动作头部已记录此限制)

Activity

  1. zhuangjianguo commented on Oct 25, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Ruling: batch #2 item 2 · letter A · maintainer 「同意」 2026-10-25T09:14:04Z

    裁定:A —— 新增能力 backfill_contract,档案岗与法务各持一把

    维护者在第 2 批决策箱回了「同意」(= 推荐项 A)。裁决文档:https://claude.ai/artifact/X4sZxhLrEYshLjkgYp4svr 。文档中明写「若法务不需亲手补录,请直接选 B」,维护者仍选 A,即确认法务需要亲手补录旧合同。

    鲜度门:正文 2026-09-09T17:06Z,无评论;main e4997a8 的 src/actions/contract-backfill.actions.ts:49(requiredPermissions: ['execute_contract'])与正文一致。录裁前重读,无变化。

    ⚠️ 前提变化(录在卡上,正文未改)

    18.1 升级线程实测:F16 的流程 executed_upload 是 autolaunched + runAs: 'system'(src/flows/executed-upload.flow.ts:132,138),18.x 的「提权自触发规则」拒绝非系统调用方启动这样的流程。升到 18.1 后连档案岗也补录不了,直到流程的启动门改掉。平台的正式答案是流程级 requiredPermissions(objectstack#25643,2026-10-24 合并,不在 18.1.0 包里)。F16 的门控无论如何都要在流程层重声明一次,本卡的新令牌正好填进那一行。

    裁后执行(给 lane 席位)

    1. §04(受管面):权限矩阵新增能力 backfill_contract,持有者:档案岗、法务、管理员。随设计文档 PR,Refs #43。
    2. 动作:contract-backfill.actions.ts 的 requiredPermissions 改为 ['backfill_contract'],三个 profile 加令牌,动作头部的限制注记删除。
    3. 流程:executed_upload 的启动门从 autolaunched+runAs:'system' 改为流程级 requiredPermissions: ['backfill_contract'](按 #25643 的形状)。
    4. 阻塞:Blocked-by 承载 objectstack#25643 的平台版本;且在 18.1 升级 PR 之后。期间不做应用侧绕路。
    5. ⛔ 选项 C(平台 OR 语义)明确不采纳:补录与登记执行本就是两个业务动作,正确建模是起一个名字。

    ⛔ 本席不动标签;lane 席位派发时转标签并带 Ruling-ref:。


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Out of the decision box — Ruling-ref: comment 6395823636 (batch #2 · maintainer 「同意」).

    • State: needs-user-decision → pm:blocked, two Blocked-by: lines: the release carrying objectstack#25643, after the 18.1 upgrade PR.
    • Then one card: backfill_contract token for records, legal and admin; the action's requiredPermissions and F16's flow start gate move to it; §04 matrix row goes with the design-document PR (Refs #43). No app-side workaround meanwhile.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Seat note · 2026-10-25T13:00Z — carried here from #53 (ruling B, 6396326020, step 3): whether the F16 backfill form gets an optional legal_owner (custodian lawyer) field is decided and built on this card, together with F16's own unblock. Today F16 writes no legal_owner; on a backfilled contract legal sets it by editing the record (help text since PR #115). State unchanged: pm:blocked, Blocked-by: objectstack-ai/objectstack#25643.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    Contributor

    Unblocked — 2026-10-27

    upstream state installable here
    objectstack-ai/objectstack#25643 closed fix commit f1ddace is an ancestor of tag @objectstack/spec@18.2.0

    main now pins @objectstack/* 18.2.0 (PR #155, merge 21f2319, push CI Validate success), so the fix is installable in this repo. The ruling recorded in the body stands; this card returns to the queue for dispatch. The dev re-measures the premise on main before writing code.

    The second Blocked-by: (the 18.1 upgrade) closed with PR #110.

    State: pm:blocked → pm:queue.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 17
    Ruling-ref: 6395823636
    Session: session_017nzWFRUo2U7evMkPdVniAn
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-43-backfill-capability
    Worktree: hotclm-issue-43
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: objectstack.config.ts (declare capability backfill_contract) · src/profiles/{records,legal,admin}.profile.ts (grant it) · src/actions/contract-backfill.actions.ts (requiredPermissions) · src/flows/executed-upload.flow.ts (flow-level requiredPermissions in place of autolaunched + runAs: 'system' as the gate) · translations — the §04 capability row rides the seat's design-doc PR #120; stop on breach; explain in the report
    Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; hand-derived for a sister repo), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: this app's backfill door (who may record an already-signed contract) | objectstack-ai/objectstack#25643 shipped in 18.2.0 | every records manager and lawyer backfilling legacy contracts
    Thread-read: 6437388552
    Premise: re-measured by the dev on origin/main 0159171 (@objectstack 18.2.0) with the declared demo staff (PR #171)
    Serial constraints cleared: #156 landed (PR #170, flows) and #28 landed (PR #171, config / README); round 17 dispatches #43, #160 and #41 together on disjoint files; in flight #169 (lifecycle actions). #19 (seeds) follows #41; #99, #161, #162 (launch wizard) follow.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
     "issue": 43,
     "status": "done",
     "branch": "claude/issue-43-backfill-capability",
     "pr": "https://github.com/objectstack-ai/hotclm/pull/177",
     "session": "session_017nzWFRUo2U7evMkPdVniAn",
     "premise_still_valid": true,
     "summary": "Ruling A carried out. backfill_contract is declared in objectstack.config.ts (execute_contract no longer claims the backfill) and granted by clm_records, clm_legal and clm_admin. The backfill action and the executed_upload flow both declare requiredPermissions ['backfill_contract']. The flow moved from autolaunched to type 'screen' and keeps runAs 'system'. A flow-level list alone could not open an autolaunched flow, because 18.2.0's flowStartRefusal asks the elevated self-triggered rule first, and no capability opens that rule. The spec's remedy for members is 'screen'; 'api' is a signed webhook that needs a secret. runAs system stays because the active-born insert, the section-04-locked stamps and the version filing need it, and created_by still names the caller. Seat addendum #99 item 7 is done: owner_id is a required action param and flow input, written on create_contract, with help text in both bundles; measured that the named owner is stored and sees the contract under 我的合同. Premise re-measured on 0159171: Records Manager and CLM Administrator got 403 'This caller may not start this flow', and counsel1 had no button.",
     "tests": "pnpm demo:zh on 18.2.0, port 3151, a fresh .objectstack for each run (before 0159171, after f1e7027), declared demo staff, set-user-manager 200 x3. Browser, zh-CN: records@, counsel1@ and clm.admin@ each backfilled through 补录已签合同 and got 200 completed. Each run created DPA-2026-0011/0012/0013 with status active, is_backfilled true, signed_at = executed_at = activated_at = 2025-06-01T12:00Z, and version 1 final_signed is_current. created_by is the backfiller; owner_id is BR2, BR1 and BR3 as picked. Before, records and admin got 403 'This caller may not start this flow', and counsel1 had no button. requester1 sees no button in either the zh-CN or the en toolbar or overflow menu. API as requester1, requester2 and finance: the action door answers 403 'requires capability [backfill_contract]' and the trigger route answers 403 'Starting this flow requires capability [backfill_contract]'; 0 rows written. #99 item 7: requester2 sees the contract in 我的合同 → 我发起的, and over the API owner_id is their id. The four refusals are read in zh-CN and en, each the app sentence alone, 200 status refused. future and blocked-party were read in the browser notice; missing and invalid were read on the trigger route, because the action door 400s them first; unknown party was read over the API. Run history: refused 21, completed 3, failed 0. Reverse check: the typo backfill_contrakt made validate print capability-reference-unknown at flows[11].requiredPermissions; the file was restored with git checkout HEAD, git diff HEAD was empty and the hash matched. No ablation, because no suite pins this metadata.",
     "gates": "On f1e7027, each exit code captured before any pipe. pnpm install 0; pnpm validate 0 (7 ⚠, the same set main prints); pnpm lint 0 (6 suggestions, 0 warnings, the same as origin/main 0159171, which I measured. common.md's 'exactly one warning on main' did not reproduce: main prints 0 warnings); pnpm typecheck 0; pnpm lint:i18n-gate 0 (0 missing keys); CLM_DEMO_SEED=1 pnpm validate 0. Boot WARN/⚠ lines are identical before and after. PR CI: not awaited (in_progress at report time).",
     "dogfood_battery": "n/a — hotclm app repo, no `!` in the title",
     "mcp_calls": "0",
     "api_writes": "2 relay writes — pr_create objectstack-ai/hotclm#177 (fleet-write run 40495089078, read-back identical, assignee zhuangjianguo) and this os-dev-report comment on #43 via post-stamped. Plus git push (not a REST write).",
     "governed_text_proposal": [
      "DESIGN.md §04, the action-gate line: after 「动作门控同时在 UI 与服务端生效(ADR-0066 D4):」 list `backfill_contract` after `execute_contract`, and add: 「`backfill_contract`(F16 补录已签合同)由 `clm_records`、`clm_legal`、`clm_admin` 持有,同时声明在动作与流程 `executed_upload` 的 `requiredPermissions` 上。」",
      "DESIGN.md §06 F16, the row's last clause: replace 「仅 `clm_records.access` 与 `clm_legal.access` 可用」 with 「持有 `backfill_contract`(档案、法务、管理员)者可用;对话框必填业务承办人 `owner_id`,`legal_owner` 仍由法务在记录上指定」"
     ],
     "open_questions": [
      {
       "question": "The seat note on #43 (6397961272, carried from #53) says the optional legal_owner on the F16 form is 'decided and built on this card'. Neither the dispatch nor the claim's file surface asks for it, so it is not built.",
       "options": [
        "A: leave as today (legal sets legal_owner by editing the record; help text since PR #115)",
        "B: add an optional legal_owner param + flow input in a follow-up on the same files"
       ],
       "recommendation": "A unless the maintainer wants it: the dispatch and the ruling are silent, and §03 keeps the two fields separate."
      }
     ],
     "out_of_scope_findings": [
      "carrier: none — noted, not filed. On 18.2.0 the list toolbar hides a flow action using only the action's own requiredPermissions, not the flow's inherited list, so this PR declares the list on both. The upstream acceptance notes of objectstack#25643 already record this as an observation.",
      "carrier: seat — the claim's PR title convention (title = issue title) would carry the decision card's '[Decision]' prefix; the PR uses the issue title without it."
     ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    Contributor

    Ruling-ref: 6395823636 (letter A)

    Landed — PR #177 · 2026-10-27

    reading value
    PR #177 MERGED (squash on main), merged at its ACCEPT head f1e7027; Fixes #43 closes this card
    authority maintainer, project thread, verbatim: 「你可以自己审核的就自己合并」

    Release: F16 补录已签合同 is now open to the records manager, legal and the admin through the new backfill_contract capability, refused at every door for everyone else, and the dialog asks for the business owner (owner_id). Seat call on the #53 note: legal_owner is not added to the form; legal assigns it on the record. State: pm:dispatched removed, assignee cleared.


    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

    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