@stalfh233/omc-cli 0.6.12 → 0.6.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/coverage/m1-coverage-manifest-v1.json +2 -0
  2. package/dist/approval-flow.js +84 -3
  3. package/dist/batch-call.js +32 -2
  4. package/dist/bench/call-face-eval.js +2 -2
  5. package/dist/call.js +35 -2
  6. package/dist/capability-overview.js +21 -7
  7. package/dist/cli-contract.js +8 -2
  8. package/dist/cli.js +313 -72
  9. package/dist/describe-cache.js +8 -4
  10. package/dist/discovery.js +4 -0
  11. package/dist/environment-policy.js +20 -0
  12. package/dist/field-families.js +62 -4
  13. package/dist/form-support.js +293 -6
  14. package/dist/goal-routes.js +137 -22
  15. package/dist/identity.js +63 -11
  16. package/dist/lanes/app-contract.js +4 -4
  17. package/dist/lanes/app.js +20 -2
  18. package/dist/lanes/dictionary-contract.js +16 -5
  19. package/dist/lanes/dictionary-coverage.js +1 -0
  20. package/dist/lanes/dictionary.js +200 -8
  21. package/dist/lanes/environment-contract.js +18 -6
  22. package/dist/lanes/environment.js +129 -16
  23. package/dist/lanes/field-contract.js +3 -2
  24. package/dist/lanes/field.js +785 -63
  25. package/dist/lanes/form.js +52 -6
  26. package/dist/lanes/list-view-contract.js +3 -3
  27. package/dist/lanes/list-view.js +143 -12
  28. package/dist/lanes/meta.js +36 -10
  29. package/dist/lanes/model-contract.js +7 -6
  30. package/dist/lanes/model.js +154 -10
  31. package/dist/lanes/organization.js +67 -7
  32. package/dist/lanes/runtime-data-contract.js +4 -2
  33. package/dist/lanes/runtime-data.js +395 -41
  34. package/dist/lanes/workflow-contract.js +2 -2
  35. package/dist/lanes/workflow.js +381 -11
  36. package/dist/model-scaffold.js +11 -3
  37. package/dist/path-projection.js +181 -31
  38. package/dist/read-shapes.js +31 -11
  39. package/dist/recipes/b2-registration.js +1 -1
  40. package/dist/recipes/field.js +16 -3
  41. package/dist/recipes/form.js +4 -3
  42. package/dist/recipes/runtime-data.js +58 -8
  43. package/dist/recipes/workflow.js +86 -1
  44. package/dist/refusal-guidance.js +18 -6
  45. package/dist/rollback-spec.js +41 -2
  46. package/dist/rollback.js +49 -11
  47. package/dist/script.js +72 -12
  48. package/dist/skills.js +91 -3
  49. package/dist/tools.js +113 -1
  50. package/dist/wire.js +13 -1
  51. package/dist/write-lock.js +92 -4
  52. package/dist/write.js +129 -16
  53. package/package.json +3 -3
  54. package/skills/omc-business-configuration.md +1 -1
  55. package/skills/omc-capabilities.md +5 -4
  56. package/skills/omc-playbook.md +41 -0
  57. package/skills/omc-refusals.md +58 -6
  58. package/skills/omc-tool-selection.md +2 -0
  59. package/skills/omc-write-hazards.md +3 -2
@@ -70,6 +70,23 @@ bisect by deleting/disabling the suspect formula field, which is reversible).
70
70
  `omc` now answers `runtime-write-db-exception` and lists the model's formula
71
71
  fields; the fix is to aggregate a MAIN-table scalar column instead.
72
72
 
73
+ ### 3c. Workflow validate `401013` — duplicate / conflicting edge
74
+
75
+ `workflow.validate` returns `errCode:401013` with `errContent:"节点名A&节点名B"`
76
+ and often `errMsg:null` (r31c F21). Meaning: **the same pre→post pair has more
77
+ than one edge**, typically an unconditional edge plus a condition edge, or two
78
+ condition edges that the platform treats as a duplicate. Fix:
79
+
80
+ 1. Condition edge and else edge must target **different** `postNode`s.
81
+ 2. Else uses `utilizeElse:true` (not a second unconditional edge on the same pair).
82
+ 3. Copy `omc describe workflow.update --example workflow-conditional-two-level-approval`
83
+ for the fork-then-join shape (金额>阈值两级 / 否则一级).
84
+
85
+ Related: `workflow.validate` currently does **not** warn about dead-end graphs
86
+ (a node with only conditional outbounds and no else — r31c F23). That check
87
+ belongs in `src/lanes/workflow.ts` (forbidden for this change set); rely on the
88
+ recipe's else edge until it lands.
89
+
73
90
  ## 4. Readback mismatch — the write landed but does not match
74
91
 
75
92
  `*-readback-failed`, `*-readback-mismatch`, `cleanup-absence-readback`,
@@ -90,8 +107,17 @@ approved draft, and decide with the human. A second write may duplicate.
90
107
 
91
108
  ## 6. Idempotency & replay
92
109
 
93
- - A repeated `requestId` with identical args returns `replay:"already-completed"`
94
- — the first write stands; treat as success, do not re-execute.
110
+ - First write with a new `requestId` returns `idempotency.replay:"none"` (no
111
+ `duplicate` key). Constants live in code as `IDEMPOTENCY_REPLAY` /
112
+ `IDEMPOTENCY_DUPLICATE` — do not invent parallel spellings.
113
+ - A repeated `requestId` with **identical** args returns
114
+ `replay:"already-completed"` + `duplicate:"same-request-replay-no-op"` (or
115
+ `already-absent-no-op` / `already-published-no-op` for delete/publish) — the
116
+ first write stands; treat as success, do not re-execute.
117
+ - Same `requestId` with **different** args is refused:
118
+ `blocker:"request-id-args-mismatch"` + `argsDigestChanged:true` + pasteable
119
+ `nextCommand` (`request.status` or a fresh `--request-id`). It does **not**
120
+ silently write a second row.
95
121
  - `intent-plan-tampered` — the plan file changed after planning. Re-run
96
122
  `intent plan`.
97
123
  - `intent-baseline-drifted` — the environment moved after planning. Re-run
@@ -101,11 +127,15 @@ approved draft, and decide with the human. A second write may duplicate.
101
127
  ## 7. Transport uncertainty — outcome unknown
102
128
 
103
129
  `status:"uncertain"` with `frozen:true` (`cli-timeout`, `batch-timeout-budget`,
104
- `wire-transport:*`). The write **may** be in flight.
130
+ `wire-transport:*`). The write **may** be in flight. On `cli-timeout` the
131
+ envelope carries `retryable:false`, `lockHeldBy` / `retryAfter`, and a pasteable
132
+ `nextCommand` for `omc call request.status --env <env> --args '{"requestId":"..."}'`.
105
133
 
106
- 1. Read back the affected resource (or `artifact get <id>` / the `requestId`).
107
- 2. Only after the outcome is established, decide whether to retry.
108
- 3. Never blind-retry — that risks a second write.
134
+ 1. Run that `request.status` (or read back the affected resource by business key).
135
+ 2. Only after the outcome is established, decide whether to retry — and only
136
+ after the lock holder exits (`retryAfter`).
137
+ 3. Never blind-retry while `retryable:false` / lock held — that risks
138
+ `write-lock-held` or a second write.
109
139
 
110
140
  `write-lock-held` — another write holds the environment lock (`owner`/`recovery`
111
141
  included). Retry with `--wait <ms>` on `omc call` / `omc intent apply`: queueing is
@@ -127,6 +157,28 @@ concurrency, not approval.
127
157
  - `dictionary-target-required`, `intent-plan-invalid` — a required selector or
128
158
  shape is missing; see `describe <tool>`.
129
159
 
160
+ ## 9b. 权限拒绝(40009 / 发起白名单 / 管理员绕过)
161
+
162
+ - `40009` / `workflow-start-permission-denied` — 「流程发起权限不足」。三个同码原因:
163
+ ① 发起人没命中 `h_workflow_permission`(部门/祖先/角色/本人);② 流程未发布;
164
+ ③ 发起人部门记录丢了(`getRootDeptId` 空 → 白名单短路)。先
165
+ `omc call workflow.permissions` 看配置行,再 `workflow.get_published` 看是否已发布。
166
+ - **管理员绕过**:平台 `h_perm_admin` 的 ADMIN/SYS_MNG(= `login().administrator`,
167
+ **不是**登录 API 的 `isAdmin`)会绕过发起白名单。`data.submit` 成功时若
168
+ `actor.isAdmin=true` 且带 `bypassReason:"platform-admin bypasses start-permission whitelist"`,
169
+ **配置成功 ≠ 对普通用户生效**。
170
+ - **怎么验证真生效**:用**非管理员**账号另建环境(`omc env add`)再
171
+ `data.submit`;或看 `workflow.permissions` 的 `effectiveFor.reason`。不要用管理员
172
+ 发起成功当作白名单证据。
173
+ - `workflow-start-permission-empty` — 权限表 0 行:除平台管理员外无人能发起;
174
+ 用 `workflow.addPermission` 授给部门/角色/用户。
175
+ - `workflow-bound-to-listview`(303024)— 列表视图还绑着该流程。回执带
176
+ `boundListviews[{code,name,actionCode,associationCode,fieldCodes}]` 与可粘贴
177
+ `nextCommand`(`listview.configure`:`actions:[]` + `replaceActions:true` + 现有
178
+ `fieldCodes`,无需再挖 `listview.get`)。先解绑再 `workflow.delete`;整体清理用
179
+ `omc model-purge --code <schema>`。注意:默认 `listview.get` 摘要面不露
180
+ `queryActions`,绑定细节以本拒绝回执为准。
181
+
130
182
  ## 10. 投影路径(--jq / --extract)
131
183
 
132
184
  收**路径选择器**,不是 jq 表达式(错误回执的 `hint` 与本节同源)。
@@ -49,6 +49,8 @@ description: Choose the right OMC CLI tool for a goal. Use when unsure whether a
49
49
 
50
50
  **纯读不必先发现**:已知读哪一面就直接 `omc call <读工具>`(上表),省掉 `next`/`discover_environment`;只有选不出工具时才用 `next`。发现仪式(`next`/`capabilities`/`discover_environment`)是写前与选不出工具时的路径。
51
51
 
52
+ **行级数据权限 / 可见范围 / 申请人只看自己**:不在工具覆盖内(没有按登录人动态过滤的读/写面)。`workflow.permissions` 只设谁能发起;`listview.configure` 的 conditions 是全局过滤,不是按登录人。详见 `omc help omc-capabilities --section "边界"`。
53
+
52
54
  ## High layer — orchestrated CLI calls
53
55
 
54
56
  | Goal | Tool |
@@ -41,7 +41,7 @@ omc help omc-write-hazards
41
41
  - 有映射时传非空 JSON 数组字符串,并在 draft/published 两面读回。
42
42
  - **人员字段形状(issue#10,8.6.41+1.1.25 双验)**:save/update 只认对象数组 `[{id,type,name}]`(type:用户 3/部门 1)——字符串/字符串数组 50000;提交面两种形状都 50000。可靠链路=save 对象数组落库 → 提交**省略人员字段**。OMC 双闸已修正(提交带 allowStaffOnSubmit 放行)。单选(9)=id 串;多选(11)=分号串。
43
43
  - **`mappings` 由表单运行时在用户选择时套用**,服务端 `data.save` **不会**自动回填映射目标字段。API 侧要自己写被映射字段,否则读回为空——别把它误判成“映射配置失败”。
44
- - **关联映射的事实源在字段行 `options.mappings`,不在表单控件属性**(真机 2026-10-14 实证:改表单 attributes 里的映射会被平台按字段重算覆盖、回读不生效)。改映射走:`field.update` 按完整 options 提交(只改 mappings,其余键原值保留)→ `field.publish` → `form.draft`(**rebuild:false**,保留现有布局与在线JS)重存 → `form.publish` → 回读核对。**别用 cascadeRepublish 刷带布局/在线JS 的表单**:rebuild 会清空设计器公式(已做写前拒绝)。
44
+ - **关联映射的事实源在字段行 `options.mappings`,不在表单控件属性**(真机 2026-10-14 实证:改表单 attributes 里的映射会被平台按字段重算覆盖、回读不生效)。改映射走:`field.update`→`field.publish`→`form.draft(rebuild:false)`→`form.publish`。**别用 cascadeRepublish 刷带布局/在线JS 的表单**。字典-backed 的 `data.save` 不校验取值(非法码也会落库;要拦请配数据规则)。
45
45
  - **运行时写值的最小形状**(组合出错只会得到一个不透明的 `50000`/NPE,逐一最小化再合):单选关联(9) → id 字符串 `"<id>"`;**多选关联(11) → 分号串 `"<id1>;<id2>"`**;**人员/组织(50/51/60/61) → `[{"id":"<id>","type":3,"name":"<名>"}]`,`type` 必填且为整数**(缺/非整数 → `BizField.parseFromClient` 拆箱 NPE→50000;用户=3、部门=1);日期(3) → 字符串 `"2026-09-30 10:00:00"`;子表(8) → 行对象数组 `[{"<子列code>":<值>, ...}]`。先各写一条最小行通过,再组合。
46
46
  其中三类已有**写前拒绝**(r17b 2026-10-26 起):日期收到毫秒时间戳(平台接受但落库 NULL)、日期字符串不匹配字段声明 format(50000)、单值关联收到数组(50000)——blocker `submit-date-shape` / `submit-relation-shape`。
47
47
  - **DATE(3) 字段的 `options.format` 必须是平台大写枚举**(`YYYY-MM-DD`、`YYYY-MM-DD HH:mm:ss`、`YYYY年MM月DD日`…),**不是 Java 的 `yyyy-MM-dd`**:平台 `DateBizField` 按字面比较,Java 写法能创建字段但**首次运行时 save 稳定 `50000`(`不支持的format`)**(2026-09-30 R5 实测)。`field.create`/`intent` 会就地拒绝非枚举值。
@@ -109,7 +109,7 @@ omc call field.publish --env local --args '{"schemaCode":"expense"}'
109
109
 
110
110
  ## 写调用纪律
111
111
 
112
- - **同一 `<env>` 的写调用必须串行发起**:`write-lock-held` 不只在"并行重放失败请求"时出现,也会在**并行发起两个无关写**时触发(实测:并行两条 `dictionary.save`、并行 `field.update`+`field.publish` 各失败一次)。读可并行,写不要。确实要并发进程时用 `--wait <ms>`(`omc call` / `omc intent apply` 都支持)让 CLI 内部排队,别把重试循环写在 shell 里。
112
+ - **同一 `<env>` 的写调用必须串行发起**:`write-lock-held` 不只在"并行重放失败请求"时出现,也会在**并行发起两个无关写**时触发(实测:并行两条 `dictionary.save`、并行 `field.update`+`field.publish` 各失败一次)。读可并行,写不要。确实要并发进程时用 `--wait <ms>`(`omc call` / `omc intent apply` 都支持)让 CLI 内部排队,别把重试循环写在 shell 里。并发语义总表(写锁 / 同记录 patch / `version` 非乐观锁 / 翻页无快照 / 删除幂等)见 `omc help concurrency`。
113
113
  - **`data.delete` 批量形态 1.1.25 假成功**(r30 2026-10-31 真机):`bizObjectIds` 数组回 `DELIVERED`+`absent:true`+`modelResidue.total` 递减但行仍在——OMC 已写前拒绝 `data-delete-batch-fake-success`。纪律不变:删除后用 `data.load exists:false`(或重查 `total`)复核,`absent:true` 不是删除成功的证明。
114
114
 
115
115
  ## 审批提交的真实形状(r18b 2026-10-26 真机,1.1.25)
@@ -216,6 +216,7 @@ omc call field.publish --env local --args '{"schemaCode":"expense"}'
216
216
 
217
217
  - **`btnConfigList` 局部行=整行替换**:只传 `{code, remark}` 会把该按钮的 `enabled` 静默置 `false`、`commentPermission` 置 `null`("驳回意见必填"丢失),同提交未提及的其他属性也被重置。改前先 `workflow.get_published`/`get_draft` 取整行,改目标属性后**整条回写**;`propertyPermissions` 同粒度。
218
218
  - **`SINGLE_PARTICIPANT` 多角色只取第一个解析成员**(MULTI 则全员会签)——"配置没生效"常是它,回读确认解析人。
219
+ - **条件流小节点(r31c)**:同一对 `pre→post` 不能既有无条件边又有条件边(平台 `401013`,errContent 常是「节点名A&节点名B」)。条件边与 else(`utilizeElse:true`)必须指向**不同** postNode。两级/一级分叉再汇合抄 `omc describe workflow.update --example workflow-conditional-two-level-approval`。`workflow.validate` 目前不查断头图(只有条件出边、无 else 的节点)——靠配方自带 else。
219
220
 
220
221
  ## 跨表回写的真实语义(1.1.25 源码 + 真机 2026-10-20)
221
222