@stalfh233/omc-cli 0.0.0-stage → 0.4.10
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.
- package/LICENSE +21 -0
- package/README.md +293 -3
- package/coverage/m1-coverage-manifest-v1.json +1278 -0
- package/dist/approval-token.js +102 -0
- package/dist/args.js +25 -0
- package/dist/artifacts.js +119 -0
- package/dist/bench/call-face-eval.js +256 -0
- package/dist/bench/context-attribution.js +151 -0
- package/dist/bench/discovery-cost-eval.js +230 -0
- package/dist/bench/driver.js +81 -0
- package/dist/bench/evals.js +236 -0
- package/dist/bench/fake-http-server.js +65 -0
- package/dist/bench/instrument.js +87 -0
- package/dist/bench/intent-face-eval.js +343 -0
- package/dist/bench/run.js +166 -0
- package/dist/bench/scenario.js +343 -0
- package/dist/bench/types.js +76 -0
- package/dist/bi-wire.js +41 -0
- package/dist/bizservice-config.js +581 -0
- package/dist/call.js +153 -0
- package/dist/capability-absences.js +23 -0
- package/dist/capability-overview.js +492 -0
- package/dist/capability-shape.js +154 -0
- package/dist/cli-contract.js +70 -0
- package/dist/cli-output.js +73 -0
- package/dist/cli.js +1418 -0
- package/dist/code-rules.js +69 -0
- package/dist/command-transport.js +155 -0
- package/dist/config-store.js +195 -0
- package/dist/context.js +21 -0
- package/dist/contract-consistency.js +66 -0
- package/dist/contract-resources.js +62 -0
- package/dist/coverage-consistency.js +62 -0
- package/dist/coverage-registry.js +76 -0
- package/dist/coverage.js +130 -0
- package/dist/data-list-filter.js +114 -0
- package/dist/discovery.js +390 -0
- package/dist/endpoints.js +154 -0
- package/dist/environment-policy.js +26 -0
- package/dist/execution-metadata.js +1092 -0
- package/dist/fake/app.js +45 -0
- package/dist/fake/b2-registration.js +594 -0
- package/dist/fake/businessrule.js +242 -0
- package/dist/fake/datarule.js +72 -0
- package/dist/fake/dictionary.js +108 -0
- package/dist/fake/environment.js +30 -0
- package/dist/fake/field.js +167 -0
- package/dist/fake/form.js +101 -0
- package/dist/fake/index.js +121 -0
- package/dist/fake/list-view.js +289 -0
- package/dist/fake/model.js +136 -0
- package/dist/fake/online-js.js +18 -0
- package/dist/fake/report.js +253 -0
- package/dist/fake/routes.js +47 -0
- package/dist/fake/rule-lifecycle.js +37 -0
- package/dist/fake/runtime-data.js +367 -0
- package/dist/fake/state.js +67 -0
- package/dist/fake/workflow.js +364 -0
- package/dist/field-change.js +200 -0
- package/dist/field-families.js +896 -0
- package/dist/form-layout.js +111 -0
- package/dist/form-support.js +821 -0
- package/dist/goal-routes.js +468 -0
- package/dist/governed-execution.js +87 -0
- package/dist/human-summary.js +212 -0
- package/dist/identity.js +62 -0
- package/dist/intent/baseline.js +57 -0
- package/dist/intent/capabilities/bizservice.js +274 -0
- package/dist/intent/capabilities/businessrule.js +493 -0
- package/dist/intent/capabilities/datarule.js +187 -0
- package/dist/intent/capabilities/field.js +545 -0
- package/dist/intent/capabilities/form.js +136 -0
- package/dist/intent/capabilities/index.js +64 -0
- package/dist/intent/capabilities/listview.js +157 -0
- package/dist/intent/capabilities/model.js +100 -0
- package/dist/intent/capabilities/onlinejs.js +108 -0
- package/dist/intent/capabilities/report.js +355 -0
- package/dist/intent/capabilities/workflow.js +458 -0
- package/dist/intent/capability.js +6 -0
- package/dist/intent/cli.js +91 -0
- package/dist/intent/compare.js +56 -0
- package/dist/intent/compiler.js +79 -0
- package/dist/intent/dsl.js +129 -0
- package/dist/intent/plan-file.js +63 -0
- package/dist/intent/readback.js +65 -0
- package/dist/intent/schema.js +158 -0
- package/dist/intent/validation.js +30 -0
- package/dist/intent/yaml.js +315 -0
- package/dist/json-column.js +68 -0
- package/dist/lanes/app-contract.js +95 -0
- package/dist/lanes/app-coverage.js +16 -0
- package/dist/lanes/app.js +174 -0
- package/dist/lanes/apply-changes.js +231 -0
- package/dist/lanes/b2-registration-contract.js +292 -0
- package/dist/lanes/b2-registration-coverage.js +48 -0
- package/dist/lanes/b2-registration.js +1187 -0
- package/dist/lanes/businessrule-contract.js +232 -0
- package/dist/lanes/businessrule-coverage.js +16 -0
- package/dist/lanes/businessrule.js +221 -0
- package/dist/lanes/contract-support.js +65 -0
- package/dist/lanes/coverage-declaration.js +9 -0
- package/dist/lanes/datarule-contract.js +155 -0
- package/dist/lanes/datarule-coverage.js +19 -0
- package/dist/lanes/datarule-protocol.js +308 -0
- package/dist/lanes/datarule.js +818 -0
- package/dist/lanes/dictionary-contract.js +78 -0
- package/dist/lanes/dictionary-coverage.js +24 -0
- package/dist/lanes/dictionary.js +235 -0
- package/dist/lanes/environment-contract.js +70 -0
- package/dist/lanes/environment-coverage.js +14 -0
- package/dist/lanes/environment.js +163 -0
- package/dist/lanes/field-contract.js +223 -0
- package/dist/lanes/field-coverage.js +27 -0
- package/dist/lanes/field.js +374 -0
- package/dist/lanes/form-contract.js +97 -0
- package/dist/lanes/form-coverage.js +16 -0
- package/dist/lanes/form.js +185 -0
- package/dist/lanes/lane-ids.js +34 -0
- package/dist/lanes/list-view-contract.js +175 -0
- package/dist/lanes/list-view-coverage.js +20 -0
- package/dist/lanes/list-view-shapes.js +1207 -0
- package/dist/lanes/list-view.js +578 -0
- package/dist/lanes/meta-contract.js +85 -0
- package/dist/lanes/meta.js +255 -0
- package/dist/lanes/model-contract.js +168 -0
- package/dist/lanes/model-coverage.js +20 -0
- package/dist/lanes/model.js +986 -0
- package/dist/lanes/online-js-contract.js +99 -0
- package/dist/lanes/online-js-coverage.js +28 -0
- package/dist/lanes/online-js.js +127 -0
- package/dist/lanes/report-contract.js +144 -0
- package/dist/lanes/report-coverage.js +21 -0
- package/dist/lanes/report.js +476 -0
- package/dist/lanes/rule-graph.js +1846 -0
- package/dist/lanes/rule-lifecycle-contract.js +92 -0
- package/dist/lanes/rule-lifecycle-coverage.js +20 -0
- package/dist/lanes/rule-lifecycle.js +176 -0
- package/dist/lanes/runtime-data-contract.js +264 -0
- package/dist/lanes/runtime-data-coverage.js +25 -0
- package/dist/lanes/runtime-data.js +1054 -0
- package/dist/lanes/workflow-contract.js +223 -0
- package/dist/lanes/workflow-coverage.js +40 -0
- package/dist/lanes/workflow.js +1813 -0
- package/dist/online-js-layout.js +58 -0
- package/dist/online-js-source.js +276 -0
- package/dist/package-tool.js +51 -0
- package/dist/package.js +73 -0
- package/dist/plan.js +73 -0
- package/dist/read.js +144 -0
- package/dist/redact.js +28 -0
- package/dist/rule-support.js +587 -0
- package/dist/runtime-support.js +134 -0
- package/dist/server.js +92 -0
- package/dist/session-manager.js +30 -0
- package/dist/session.js +149 -0
- package/dist/skills.js +112 -0
- package/dist/support.js +98 -0
- package/dist/tool-types.js +127 -0
- package/dist/tools.js +59 -0
- package/dist/usage-log.js +197 -0
- package/dist/wire.js +287 -0
- package/dist/workflow-support.js +99 -0
- package/dist/write-lease.js +26 -0
- package/dist/write-lock.js +109 -0
- package/dist/write.js +176 -0
- package/dist/zip.js +156 -0
- package/docs/tool-surface-map.md +42 -0
- package/package.json +71 -6
- package/skills/omc-acceptance-criteria.md +47 -0
- package/skills/omc-business-configuration.md +257 -0
- package/skills/omc-capabilities.md +197 -0
- package/skills/omc-capability-scouting.md +55 -0
- package/skills/omc-config-draft-review.md +171 -0
- package/skills/omc-five-piece-flow.md +35 -0
- package/skills/omc-glossary.md +86 -0
- package/skills/omc-refusals.md +110 -0
- package/skills/omc-requirement-analysis.md +161 -0
- package/skills/omc-requirement-vocabulary.md +51 -0
- package/skills/omc-start-here.md +82 -0
- package/skills/omc-tool-selection.md +119 -0
- package/skills/omc-write-hazards.md +87 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { loadCoverageManifest } from "./coverage.js";
|
|
2
|
+
import { INTENT_CAPABILITIES } from "./intent/capabilities/index.js";
|
|
3
|
+
import { LANES } from "./lanes/lane-ids.js";
|
|
4
|
+
import { listSkills } from "./skills.js";
|
|
5
|
+
export const DISCOVERY_AUTHORITY = Object.freeze({
|
|
6
|
+
authoritative: "package",
|
|
7
|
+
route: "omc help omc-start-here",
|
|
8
|
+
readiness: "omc call discover_environment --env <env> --args '{}'",
|
|
9
|
+
distributedMirror: "a cpdocs snapshot may mirror this package; its manifest commit and file hashes are the authority for that copy",
|
|
10
|
+
capabilityProjection: "omc capabilities",
|
|
11
|
+
executableBundle: "omc capability <id> --executable",
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* The 13 Configuration capabilities. Declaration order is presentation order.
|
|
15
|
+
* The four intent-covered surfaces omit `requires`/`operations` where the
|
|
16
|
+
* intent registry is authoritative.
|
|
17
|
+
*/
|
|
18
|
+
const SURFACES = [
|
|
19
|
+
{
|
|
20
|
+
id: "environment",
|
|
21
|
+
title: "环境治理",
|
|
22
|
+
lane: LANES.environment,
|
|
23
|
+
description: "环境发现、指纹与版本兼容性;写入前的环境真相。",
|
|
24
|
+
operations: ["discover", "fingerprint"],
|
|
25
|
+
requires: [],
|
|
26
|
+
intent: { level: "none", note: "治理面,不进 intent;用 omc setup / discover_environment。" },
|
|
27
|
+
notes: "只读;写入开关与生产授权在 omc setup。",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "app",
|
|
31
|
+
title: "应用",
|
|
32
|
+
lane: LANES.app,
|
|
33
|
+
description: "应用包(app)生命周期:列出、创建、更新(分组移动)、admin 删除。",
|
|
34
|
+
operations: ["create", "update", "delete"],
|
|
35
|
+
requires: [],
|
|
36
|
+
intent: { level: "none", note: "未覆盖;用 omc call app.*。" },
|
|
37
|
+
notes: "环境已带默认 applicationCode,建模不必先建应用。",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "dictionary",
|
|
41
|
+
title: "数据字典",
|
|
42
|
+
lane: LANES.dictionary,
|
|
43
|
+
description: "数据字典及字典记录:键值型/树型、启停、字段选项来源。",
|
|
44
|
+
operations: ["list", "get", "records", "save", "status", "type", "names", "parent", "classes", "usage"],
|
|
45
|
+
requires: [],
|
|
46
|
+
intent: { level: "none", note: "未覆盖 intent;用 omc call dictionary.* 与 field.* 的 optionsSet。" },
|
|
47
|
+
notes: "选择字段支持 custom、dictionary、businessModel 三种 optionsSet 来源;关联单选/多选支持 searchFormula 与 conditions JSON 筛选。",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "model",
|
|
51
|
+
title: "业务模型",
|
|
52
|
+
lane: LANES.model,
|
|
53
|
+
description: "模型生命周期:创建、改名、物理删除、存在性。",
|
|
54
|
+
operations: ["create", "update", "delete", "deleteMany", "setSummary"],
|
|
55
|
+
intent: { level: "partial", note: "create/update 与 delete(absent:true)已声明式;deleteMany/setSummary 未覆盖。" },
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "field",
|
|
59
|
+
title: "字段(含子表、计算字段)",
|
|
60
|
+
lane: LANES.field,
|
|
61
|
+
description: "字段族全链:create→publish→表单绑定;CHILD_TABLE 子列;FORMULA 计算(子表求和回主表、子表列同行运算)。",
|
|
62
|
+
operations: ["create", "update", "publish", "delete"],
|
|
63
|
+
intent: { level: "partial", note: "create/update(含 CHILD_TABLE subFields、FORMULA 计算)、publish 与 delete(absent:true)已声明式。" },
|
|
64
|
+
notes: "子表列是 schemaCode=父字段 code 上的普通字段;field.update 不接受 subFields。子列支持标量族 + WORK_SHEET 关联列(需 relativeCode + options)。**计算类已覆盖(默认走计算规则,非 FORMULA 字段)**:子表求和、子表列同行运算用「普通字段 + 计算规则」(`describe datarule.create --example field-compute-rule|field-compute-subtable-sum|field-compute-child-column`);计算由**门户前端**执行、API 不回填(写测试数据带预算值,正确性人工前台确认)。嵌套子表/MULT_WORK_SHEET/RELEVANCE_DATA 不覆盖。",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "form",
|
|
68
|
+
title: "默认表单",
|
|
69
|
+
lane: LANES.form,
|
|
70
|
+
description: "表单发布链:draft-first 布局、发布、publishedHtmlJson 绑定读回。",
|
|
71
|
+
operations: ["get", "draft", "publish"],
|
|
72
|
+
intent: { level: "partial", note: "draft/publish 已声明式;get 为读回面。" },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "listview",
|
|
76
|
+
title: "列表视图",
|
|
77
|
+
lane: LANES.listView,
|
|
78
|
+
description: "列表视图设计时表头与列生命周期。",
|
|
79
|
+
operations: ["create", "configure", "delete"],
|
|
80
|
+
intent: { level: "partial", note: "create/configure 与 delete(absent:true)已声明式。" },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "datarule",
|
|
84
|
+
title: "字段级控件规则",
|
|
85
|
+
lane: LANES.dataRule,
|
|
86
|
+
description: "字段级数据规则(bizdatarule)生命周期。",
|
|
87
|
+
operations: ["create", "update", "delete"],
|
|
88
|
+
requires: ["field"],
|
|
89
|
+
intent: { level: "partial", note: "声明式支持字段级规则 create/update 与 delete(absent:true);规则类型 1..5 与 options 按平台校验器闭环。" },
|
|
90
|
+
notes: "目标字段必须已发布。",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "businessrule",
|
|
94
|
+
title: "业务规则(图形引擎,含定时任务)",
|
|
95
|
+
lane: LANES.businessrule,
|
|
96
|
+
description: "图形规则(nodeGraph):触发规则上的动作/条件节点;含**定时任务 TIMING_TASK**(每天/周期)。",
|
|
97
|
+
operations: ["list", "get", "save"],
|
|
98
|
+
requires: ["field"],
|
|
99
|
+
intent: { level: "partial", note: "声明式支持数据事件规则与定时任务:紧凑 actions 或 nodeGraph 透传(BRANCH/BIZ_ACTION/定时图用透传);写 route.save + rule.enable/disable。" },
|
|
100
|
+
notes: "数据事件只分发到平台预置 code 行(Create/Update/Delete);自定义 ruleCode 需按钮/流程/定时/显式触发。**「每天定时执行」= 定时任务 TIMING_TASK(平台原生,非外部定时器、非在线 JS)**:`rule.save` 传含 SCHEDULER 节点 + `schedulerSetting`(`bizRuleType:2`)的 nodeGraph,见 `omc describe rule.save --example rule-timing-task`。保存≠可见,需 rule.enable;绑定 BizMethod 前先有已发布业务集成方法。",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "rule-lifecycle",
|
|
104
|
+
title: "规则生命周期",
|
|
105
|
+
lane: LANES.ruleLifecycle,
|
|
106
|
+
description: "业务规则的启用、停用、删除。",
|
|
107
|
+
operations: ["enable", "disable", "delete"],
|
|
108
|
+
requires: ["businessrule"],
|
|
109
|
+
intent: { level: "none", note: "未覆盖;用 omc call rule.enable / rule.disable / rule.delete。" },
|
|
110
|
+
notes: "enabled 是运行时真相;status 可能滞后(平台怪癖)。",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "workflow",
|
|
114
|
+
title: "流程模板",
|
|
115
|
+
lane: LANES.workflow,
|
|
116
|
+
description: "流程模板生命周期:get_draft/get_published、create、update、publish、bindRule、delete、workitem.list。",
|
|
117
|
+
operations: ["create", "update", "publish", "bindRule", "delete"],
|
|
118
|
+
requires: ["model"],
|
|
119
|
+
intent: { level: "partial", note: "声明式支持 workflow.create/update/publish 与 bindRules(事件处理器规则绑定,merge/ensure-present);runtime workitem(提交/审批)仍走 escape hatch。" },
|
|
120
|
+
notes: "greenfield 首版;publish/bindRule 不得在 ValidateMessage[] 拒绝时报告成功。",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "onlinejs",
|
|
124
|
+
title: "表单在线 JS",
|
|
125
|
+
lane: LANES.onlineJs,
|
|
126
|
+
description: "表单在线 JS:compose 六元素骨架、draftHtmlJson customScript、发布与 SHA-256 读回。",
|
|
127
|
+
operations: ["compose"],
|
|
128
|
+
requires: ["form"],
|
|
129
|
+
intent: { level: "partial", note: "声明式支持 onlinejs.compose 的 source/replace/publish。" },
|
|
130
|
+
notes: "仅表单在线 JS(BizSheet 子能力);后端的在线开发(/api/api/programming Java 源码)用二开扩展包,不在本 CLI 范围。",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "bizservice",
|
|
134
|
+
title: "业务集成",
|
|
135
|
+
lane: LANES.b2,
|
|
136
|
+
description: "业务服务与方法注册(RESTFUL/DATA_SQL/DATA_TABLE/SAP/KAFKA/SOAP)、导入导出、设计器测试。",
|
|
137
|
+
operations: ["register", "update", "delete", "export", "import", "check", "test"],
|
|
138
|
+
requires: [],
|
|
139
|
+
intent: { level: "partial", note: "声明式支持新建 service.register(可带一个 method)与既有 service/method 的 update(结构化 config、AES 加密、patch 语义);删除、给既有服务加方法、分类/连接池/IE 仍走 escape hatch。" },
|
|
140
|
+
notes: "方法 configJson 走 AES-128-ECB;RESTFUL/DATA_SQL 绑定前须带适配器必需配置(否则是注册壳)。",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "report",
|
|
144
|
+
title: "报表",
|
|
145
|
+
lane: LANES.report,
|
|
146
|
+
description: "报表页 + BI 图表:报表树/元数据读取、报表页建改删、BI dashboard 配置、图表增改删、图表数据查询、数据集读取/关联。",
|
|
147
|
+
operations: ["tree", "get", "chartdata", "datasource", "create", "update", "delete", "configure"],
|
|
148
|
+
intent: { level: "partial", note: "顶层 reports[] 声明式 create/update + dashboard configure(global/attributes/charts diff,removeUnlistedCharts 删未声明图表);delete 走 omc call report.delete,datasource.link/chartdata 走 omc call。" },
|
|
149
|
+
notes: "报表页走平台通道 /api/api/report/*,BI dashboard/图表/数据走 /dashboard/v1/*(无 errcode 信封;body 必带 config:{token,reportCode,appCode},缺则写操作静默 no-op)。BI 外层 corpId 是真实查询键(错值→dashboard.not.exist,空→corpid.empty),默认 \"a\",可用 corpId 覆盖。",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "data",
|
|
153
|
+
title: "运行数据",
|
|
154
|
+
lane: LANES.runtimeData,
|
|
155
|
+
description: "运行数据条目的列出、加载、保存、更新、删除、提交。",
|
|
156
|
+
operations: ["list", "load", "save", "update", "delete", "submit"],
|
|
157
|
+
requires: ["field"],
|
|
158
|
+
intent: { level: "none", note: "未覆盖;用 omc call data.*。" },
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
const COVERAGE_RANK = { executable: 4, partial: 3, "static-only": 2, blocked: 1, unsupported: 0 };
|
|
162
|
+
const CAPABILITY_ALIASES = Object.freeze({
|
|
163
|
+
environment: ["environment-governance"],
|
|
164
|
+
app: ["apps", "app-management"],
|
|
165
|
+
dictionary: ["dictionaries", "dictionary-management"],
|
|
166
|
+
model: ["models", "model-management"],
|
|
167
|
+
field: ["fields", "field-families"],
|
|
168
|
+
form: ["forms", "form-publish-chain"],
|
|
169
|
+
listview: ["listviews", "list-view"],
|
|
170
|
+
datarule: ["data-rules", "G1-datarule"],
|
|
171
|
+
businessrule: ["business-rules", "B1-businessrule"],
|
|
172
|
+
"rule-lifecycle": ["rule-lifecycles", "S25-rule-lifecycle"],
|
|
173
|
+
workflow: ["workflows", "W7-S29-greenfield"],
|
|
174
|
+
onlinejs: ["online-js"],
|
|
175
|
+
bizservice: ["bizservices", "B2-registration"],
|
|
176
|
+
report: ["reports", "R1-report"],
|
|
177
|
+
data: ["runtime-data", "M2-runtime-data"],
|
|
178
|
+
});
|
|
179
|
+
/**
|
|
180
|
+
* Requirement cues per capability (round 3): the Chinese phrases a requirement
|
|
181
|
+
* is likely to use for this capability. Authored next to the capability id so
|
|
182
|
+
* `omc next` has one precise routing surface instead of matching against long
|
|
183
|
+
* prose. Mirrors the human-readable cue table in
|
|
184
|
+
* `skills/omc-requirement-vocabulary.md`; this registry is the runtime
|
|
185
|
+
* authority, the topic is documentation.
|
|
186
|
+
*/
|
|
187
|
+
export const CAPABILITY_CUES = Object.freeze({
|
|
188
|
+
environment: ["环境", "指纹", "版本", "连接", "登录", "就绪", "发现"],
|
|
189
|
+
app: ["应用", "门户", "分组"],
|
|
190
|
+
dictionary: ["字典", "选项来源", "选项", "键值", "树型"],
|
|
191
|
+
model: ["模型", "对象", "主数据", "物理删除", "汇总表", "统计表", "汇总对象", "月度汇总表", "汇总模型"],
|
|
192
|
+
field: ["字段", "属性", "子表", "明细", "下拉", "单选", "多选", "附件", "人员", "部门", "日期", "数值", "文本", "关联", "公式", "计算", "计算字段", "求和", "汇总", "合计", "回主表", "超链接", "地址", "表单层", "必填", "只读", "条件必填", "恒必填", "条件只读", "恒只读", "表单必填", "表单只读"],
|
|
193
|
+
form: ["表单", "页面", "布局", "控件", "只读", "显示"],
|
|
194
|
+
listview: ["列表", "视图", "查询列", "筛选", "表头", "甘特"],
|
|
195
|
+
datarule: ["字段级规则", "控件规则", "校验规则", "计算拼接", "显示规则", "规则", "type1", "type2", "type3"],
|
|
196
|
+
// NOTE: bare "自动" was a cue (round 10 bug: "自动识别"/"自动发邮件" matched
|
|
197
|
+
// businessrule and routed an UNSUPPORTED ask onto the write ladder). A business
|
|
198
|
+
// rule is named by 业务规则/触发器/规则图, not by "automatic".
|
|
199
|
+
businessrule: ["业务规则", "触发器", "规则图", "校验", "事件", "按钮", "定时任务", "定时触发", "定时器", "每天", "每日", "周期执行", "调度", "SCHEDULER", "TIMING_TASK"],
|
|
200
|
+
"rule-lifecycle": ["启用规则", "停用规则", "删除规则", "规则启停"],
|
|
201
|
+
workflow: ["流程", "工作流", "审批", "节点", "待办", "流转", "参与人", "审批人", "角色", "部门负责人", "审批角色"],
|
|
202
|
+
onlinejs: ["在线js", "脚本", "联动", "onload", "onvalidate"],
|
|
203
|
+
bizservice: ["集成", "接口", "服务", "回调", "对接", "外部系统", "datasql", "restful"],
|
|
204
|
+
report: ["报表", "图表", "看板", "bi", "dashboard"],
|
|
205
|
+
data: ["运行数据", "提交数据", "保存记录", "查记录"],
|
|
206
|
+
});
|
|
207
|
+
/**
|
|
208
|
+
* Boundary cues — authored alongside CAPABILITY_CUES (same runtime authority).
|
|
209
|
+
*
|
|
210
|
+
* `next` was pure positive keyword matching, so an ask that names an
|
|
211
|
+
* OUT-OF-COVERAGE face still matched a nearby capability and got the write
|
|
212
|
+
* ladder. Round 10 (sandbox): "OCR 单据说平台能不能自动识别" routed to
|
|
213
|
+
* `businessrule`/write:true — exactly the "invent a capability for a blocked
|
|
214
|
+
* face" anti-pattern. These cues mirror `omc-requirement-analysis`'s boundary
|
|
215
|
+
* table (and `omc-requirement-vocabulary.md`) and are checked FIRST: a boundary
|
|
216
|
+
* hit returns a scout/boundary route (write:false) that points at
|
|
217
|
+
* `omc-capability-scouting` + `capabilities`, never a build ladder.
|
|
218
|
+
*
|
|
219
|
+
* Keep cues SPECIFIC (multi-char, unambiguous): "发布" alone is a supported
|
|
220
|
+
* workflow action, so the release boundary is "上线"/"生产发布" etc.
|
|
221
|
+
*/
|
|
222
|
+
export const BOUNDARY_CUES = Object.freeze({
|
|
223
|
+
"ocr-e-signature": {
|
|
224
|
+
status: "unsupported",
|
|
225
|
+
cues: ["ocr", "电子签章", "电子签名", "电子签", "发票识别", "识别发票", "票据识别", "单据识别", "图像识别"],
|
|
226
|
+
note: "OCR / 电子签章不在平台配置覆盖内(boundary-unsupported、unsupported);不要用业务规则/在线 JS 伪造",
|
|
227
|
+
},
|
|
228
|
+
"release-prod": {
|
|
229
|
+
status: "unsupported",
|
|
230
|
+
cues: ["上线", "生产发布", "生产环境", "发布环境", "环境发布", "应用发布", "发布上线", "正式发布"],
|
|
231
|
+
note: "应用发布 / 环境上线 / 生产发布不在 CLI 配置边界内(boundary-unsupported、unsupported);CLI 只做配置,不做环境发布",
|
|
232
|
+
},
|
|
233
|
+
"inline-coding": {
|
|
234
|
+
status: "unsupported",
|
|
235
|
+
cues: ["在线开发", "在线编码", "内联代码", "内联编码", "execute_code", "online_develop", "java源码", "后端源码", "二开编码"],
|
|
236
|
+
note: "在线开发(/api/api/programming Java 源码)/ EXECUTE_CODE / ONLINE_DEVELOP 不在 CLI 内(boundary-unsupported、unsupported);用二开扩展包",
|
|
237
|
+
},
|
|
238
|
+
"nested-child-table": {
|
|
239
|
+
status: "blocked",
|
|
240
|
+
cues: ["子表套子表", "嵌套子表", "多级子表"],
|
|
241
|
+
note: "嵌套子表(子表套子表)在 W6-S27 为 blocked;一级子表可用,嵌套不可",
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
/** The first boundary category whose cue appears in the goal, if any. */
|
|
245
|
+
export function boundaryOf(goal) {
|
|
246
|
+
const lower = goal.toLowerCase();
|
|
247
|
+
for (const [id, entry] of Object.entries(BOUNDARY_CUES)) {
|
|
248
|
+
const cue = entry.cues.find((candidate) => lower.includes(candidate.toLowerCase()));
|
|
249
|
+
if (cue !== undefined)
|
|
250
|
+
return { id, status: entry.status, note: entry.note, cue };
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
const CAPABILITY_TOPICS = Object.freeze({
|
|
255
|
+
environment: ["omc-start-here", "omc-write-hazards"],
|
|
256
|
+
app: ["omc-business-configuration", "omc-write-hazards"],
|
|
257
|
+
dictionary: ["omc-business-configuration", "omc-tool-selection"],
|
|
258
|
+
model: ["omc-business-configuration", "omc-config-draft-review"],
|
|
259
|
+
field: ["omc-business-configuration", "omc-tool-selection"],
|
|
260
|
+
form: ["omc-business-configuration", "omc-config-draft-review"],
|
|
261
|
+
listview: ["omc-business-configuration", "omc-tool-selection"],
|
|
262
|
+
datarule: ["omc-business-configuration", "omc-refusals"],
|
|
263
|
+
businessrule: ["omc-business-configuration", "omc-refusals"],
|
|
264
|
+
"rule-lifecycle": ["omc-business-configuration", "omc-refusals"],
|
|
265
|
+
workflow: ["omc-business-configuration", "omc-refusals"],
|
|
266
|
+
onlinejs: ["omc-business-configuration", "omc-write-hazards"],
|
|
267
|
+
bizservice: ["omc-business-configuration", "omc-write-hazards"],
|
|
268
|
+
report: ["omc-business-configuration", "omc-write-hazards"],
|
|
269
|
+
data: ["omc-business-configuration", "omc-refusals"],
|
|
270
|
+
});
|
|
271
|
+
/** Strongest coverage status across the lane's manifest items. */
|
|
272
|
+
function coverageOf(manifest, lane) {
|
|
273
|
+
const items = manifest.items.filter((item) => item.lane === lane);
|
|
274
|
+
if (items.length === 0)
|
|
275
|
+
return "unregistered";
|
|
276
|
+
return items.map((item) => item.status).sort((a, b) => COVERAGE_RANK[b] - COVERAGE_RANK[a])[0];
|
|
277
|
+
}
|
|
278
|
+
function toolsOf(registry, lane) {
|
|
279
|
+
return registry.filter((tool) => tool.lane === lane).map((tool) => tool.name);
|
|
280
|
+
}
|
|
281
|
+
/** The intent registry is authoritative for the capabilities it covers. */
|
|
282
|
+
function intentCapability(id) {
|
|
283
|
+
return INTENT_CAPABILITIES.find((capability) => capability.id === id);
|
|
284
|
+
}
|
|
285
|
+
/** Build every Configuration capability node, derived from the live sources. */
|
|
286
|
+
export function buildCapabilityOverview(registry) {
|
|
287
|
+
const manifest = loadCoverageManifest();
|
|
288
|
+
const nodes = SURFACES.map((surface) => {
|
|
289
|
+
const intentCapabilityOfSurface = intentCapability(surface.id);
|
|
290
|
+
// Two derived slices, one authored slice:
|
|
291
|
+
// - `requires` is the ordering truth and comes from the intent registry for
|
|
292
|
+
// every capability the intent face carries.
|
|
293
|
+
// - `operations` is the capability's FULL lane operation set (declarative
|
|
294
|
+
// subset + escape hatch), authored once here because the wire tools do
|
|
295
|
+
// not expose an ordered operation list; `intent.level/note` describe which
|
|
296
|
+
// subset is declarative. A test asserts the intent registry's operations
|
|
297
|
+
// stay a subset, so the two cannot silently diverge.
|
|
298
|
+
const requires = surface.requires ?? intentCapabilityOfSurface?.requires ?? [];
|
|
299
|
+
const operations = surface.operations;
|
|
300
|
+
return {
|
|
301
|
+
id: surface.id,
|
|
302
|
+
nickname: surface.id,
|
|
303
|
+
title: surface.title,
|
|
304
|
+
lane: surface.lane,
|
|
305
|
+
aliases: CAPABILITY_ALIASES[surface.id] ?? [],
|
|
306
|
+
description: surface.description,
|
|
307
|
+
coverage: coverageOf(manifest, surface.lane),
|
|
308
|
+
tools: toolsOf(registry, surface.lane),
|
|
309
|
+
operations,
|
|
310
|
+
intent: surface.intent,
|
|
311
|
+
requires,
|
|
312
|
+
precedes: [],
|
|
313
|
+
...(surface.notes ? { notes: surface.notes } : {}),
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
317
|
+
for (const node of nodes) {
|
|
318
|
+
for (const requirement of node.requires) {
|
|
319
|
+
if (!byId.has(requirement))
|
|
320
|
+
throw new Error(`capability overview: "${node.id}" requires unknown capability "${requirement}"`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Derive the inverse edges so the graph reads both ways.
|
|
324
|
+
const precedes = new Map(nodes.map((node) => [node.id, []]));
|
|
325
|
+
for (const node of nodes)
|
|
326
|
+
for (const requirement of node.requires)
|
|
327
|
+
precedes.get(requirement).push(node.id);
|
|
328
|
+
return nodes.map((node) => ({ ...node, precedes: precedes.get(node.id) }));
|
|
329
|
+
}
|
|
330
|
+
/** One full capability node by id. */
|
|
331
|
+
export function capabilityDetail(registry, id) {
|
|
332
|
+
return buildCapabilityOverview(registry).find((node) => node.id === id);
|
|
333
|
+
}
|
|
334
|
+
export function resolveCapabilityId(id) {
|
|
335
|
+
if (SURFACES.some((surface) => surface.id === id))
|
|
336
|
+
return id;
|
|
337
|
+
return Object.entries(CAPABILITY_ALIASES).find(([, aliases]) => aliases.includes(id))?.[0];
|
|
338
|
+
}
|
|
339
|
+
function dependencyOrder(node, nodes) {
|
|
340
|
+
const byId = new Map(nodes.map((entry) => [entry.id, entry]));
|
|
341
|
+
const order = [];
|
|
342
|
+
const seen = new Set();
|
|
343
|
+
const visit = (id) => {
|
|
344
|
+
if (seen.has(id))
|
|
345
|
+
return;
|
|
346
|
+
seen.add(id);
|
|
347
|
+
for (const requirement of byId.get(id)?.requires ?? [])
|
|
348
|
+
visit(requirement);
|
|
349
|
+
order.push(id);
|
|
350
|
+
};
|
|
351
|
+
visit(node.id);
|
|
352
|
+
return order;
|
|
353
|
+
}
|
|
354
|
+
function routeForNode(node) {
|
|
355
|
+
const aliases = CAPABILITY_ALIASES[node.id] ?? [];
|
|
356
|
+
const relatedTopics = CAPABILITY_TOPICS[node.id] ?? [];
|
|
357
|
+
return {
|
|
358
|
+
nickname: node.nickname,
|
|
359
|
+
id: node.id,
|
|
360
|
+
title: node.title,
|
|
361
|
+
lane: node.lane,
|
|
362
|
+
aliases,
|
|
363
|
+
tools: node.tools,
|
|
364
|
+
commands: {
|
|
365
|
+
overview: DISCOVERY_AUTHORITY.capabilityProjection,
|
|
366
|
+
detail: `omc capability ${node.id}`,
|
|
367
|
+
executable: `omc capability ${node.id} --executable`,
|
|
368
|
+
contract: `omc contract ${node.lane}`,
|
|
369
|
+
readiness: DISCOVERY_AUTHORITY.readiness,
|
|
370
|
+
},
|
|
371
|
+
relatedTopics,
|
|
372
|
+
topicCommands: relatedTopics.map((topic) => `omc help ${topic}`),
|
|
373
|
+
nextCommands: [
|
|
374
|
+
DISCOVERY_AUTHORITY.readiness,
|
|
375
|
+
...node.requires.map((requirement) => `omc capability ${requirement} --executable`),
|
|
376
|
+
`omc capability ${node.id} --executable`,
|
|
377
|
+
],
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
export function capabilityRouteIndex(registry) {
|
|
381
|
+
const nodes = buildCapabilityOverview(registry);
|
|
382
|
+
const routes = nodes.map(routeForNode);
|
|
383
|
+
return {
|
|
384
|
+
routes,
|
|
385
|
+
nodeById: new Map(nodes.map((node) => [node.id, node])),
|
|
386
|
+
routeByLane: new Map(routes.map((route) => [route.lane, route])),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
export function capabilityRoutes(registry) {
|
|
390
|
+
return capabilityRouteIndex(registry).routes;
|
|
391
|
+
}
|
|
392
|
+
export function capabilityRouteForLane(registry, lane) {
|
|
393
|
+
return capabilityRouteIndex(registry).routeByLane.get(lane);
|
|
394
|
+
}
|
|
395
|
+
export function capabilityRouteForTool(registry, toolName) {
|
|
396
|
+
const tool = registry.find((entry) => entry.name === toolName);
|
|
397
|
+
return tool?.lane === undefined ? undefined : capabilityRouteForLane(registry, tool.lane);
|
|
398
|
+
}
|
|
399
|
+
export function executableCapabilityBundle(registry, id) {
|
|
400
|
+
const resolvedId = resolveCapabilityId(id);
|
|
401
|
+
if (!resolvedId)
|
|
402
|
+
return undefined;
|
|
403
|
+
const nodes = buildCapabilityOverview(registry);
|
|
404
|
+
const node = nodes.find((entry) => entry.id === resolvedId);
|
|
405
|
+
if (!node)
|
|
406
|
+
return undefined;
|
|
407
|
+
const tools = registry.filter((tool) => tool.lane === node.lane);
|
|
408
|
+
// Round 13: the per-tool entry used to store the same recipe set three ways —
|
|
409
|
+
// `executionMetadata.recipes`, `recipes`, and the derived `readback` /
|
|
410
|
+
// `refusalRecovery` slices. That triplication made `capability <id>
|
|
411
|
+
// --executable` 34 KB (field) with 31 KB in `orderedTools` alone, read once
|
|
412
|
+
// and re-sent every later turn. `recipes` is the single copy: readback and
|
|
413
|
+
// refusal recovery are fields of each recipe, and `executionMetadata` held
|
|
414
|
+
// nothing but recipes + relatedTools (now folded into `relatedTools`).
|
|
415
|
+
const orderedTools = tools.map((tool) => {
|
|
416
|
+
const metadata = tool.executionMetadata;
|
|
417
|
+
const recipes = metadata?.recipes ?? [];
|
|
418
|
+
return {
|
|
419
|
+
name: tool.name,
|
|
420
|
+
lane: node.lane,
|
|
421
|
+
schemaRef: `omc describe ${tool.name}`,
|
|
422
|
+
relatedTools: metadata?.relatedTools ?? [],
|
|
423
|
+
recipeRefs: recipes.map((recipe) => recipe.id),
|
|
424
|
+
recipes,
|
|
425
|
+
};
|
|
426
|
+
});
|
|
427
|
+
const availableTopics = new Set(listSkills().map((topic) => topic.name));
|
|
428
|
+
const relatedTopics = (CAPABILITY_TOPICS[node.id] ?? []).filter((topic) => availableTopics.has(topic));
|
|
429
|
+
const routing = { ...routeForNode(node), relatedTopics, topicCommands: relatedTopics.map((topic) => `omc help ${topic}`) };
|
|
430
|
+
return {
|
|
431
|
+
...node,
|
|
432
|
+
dependencyOrder: dependencyOrder(node, nodes),
|
|
433
|
+
coverageBoundary: {
|
|
434
|
+
status: node.coverage,
|
|
435
|
+
lane: node.lane,
|
|
436
|
+
intent: node.intent,
|
|
437
|
+
notes: node.notes ?? null,
|
|
438
|
+
},
|
|
439
|
+
orderedTools,
|
|
440
|
+
governance: {
|
|
441
|
+
readOnly: true,
|
|
442
|
+
static: true,
|
|
443
|
+
environmentContact: false,
|
|
444
|
+
writeGate: "tool execution still requires environment writable permission and production confirmation when applicable",
|
|
445
|
+
dryRun: { supported: true, command: "omc call <tool> --dry-run --args '<json>'" },
|
|
446
|
+
output: { defaultMaxBytes: 64 * 1024, oversized: "artifact reference with artifactId and sha256" },
|
|
447
|
+
},
|
|
448
|
+
relatedTopics,
|
|
449
|
+
routing,
|
|
450
|
+
nextCommands: routing.nextCommands,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* A compact projection for `omc capabilities` — no prose, no wire detail.
|
|
455
|
+
*
|
|
456
|
+
* Round 13: it used to give every one of the 14 capabilities its own `commands`
|
|
457
|
+
* block (3.2 KB of mechanical strings: `omc capability <id>` etc.) and a
|
|
458
|
+
* `nickname` that was always equal to `id` (153 B). Both are derivable from
|
|
459
|
+
* `id`/`lane`, so they move to one top-level `commands` template block and the
|
|
460
|
+
* node keeps only `id`. The capability id/lane/tools/order/coverage truth is
|
|
461
|
+
* unchanged; only the repetition is gone.
|
|
462
|
+
*/
|
|
463
|
+
export function compactCapabilities(registry) {
|
|
464
|
+
const index = capabilityRouteIndex(registry);
|
|
465
|
+
return {
|
|
466
|
+
status: "ok",
|
|
467
|
+
discovery: DISCOVERY_AUTHORITY,
|
|
468
|
+
// Size hint (dogfood R1): `capabilities` is ~6 KB, not a "safe small" face.
|
|
469
|
+
// Keep it a one-line pointer so it does not perturb the discovery-budget
|
|
470
|
+
// baseline more than a hair.
|
|
471
|
+
outputHint: "~6KB 非安全小档;next 已给路线时可省,单能力用 omc capability <id>",
|
|
472
|
+
commands: {
|
|
473
|
+
detail: "omc capability <id>",
|
|
474
|
+
executable: "omc capability <id> --executable",
|
|
475
|
+
contract: "omc contract <lane>",
|
|
476
|
+
readiness: DISCOVERY_AUTHORITY.readiness,
|
|
477
|
+
},
|
|
478
|
+
capabilities: index.routes.map((route) => ({
|
|
479
|
+
id: route.id,
|
|
480
|
+
title: route.title,
|
|
481
|
+
lane: route.lane,
|
|
482
|
+
aliases: route.aliases,
|
|
483
|
+
tools: route.tools,
|
|
484
|
+
relatedTopics: route.relatedTopics,
|
|
485
|
+
requires: index.nodeById.get(route.id)?.requires ?? [],
|
|
486
|
+
precedes: index.nodeById.get(route.id)?.precedes ?? [],
|
|
487
|
+
intent: index.nodeById.get(route.id)?.intent.level ?? "none",
|
|
488
|
+
coverage: index.nodeById.get(route.id)?.coverage ?? "unregistered",
|
|
489
|
+
})),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
//# sourceMappingURL=capability-overview.js.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const CAPABILITY_CONTROL_TYPES = Object.freeze({
|
|
2
|
+
0: 1,
|
|
3
|
+
1: 2,
|
|
4
|
+
2: 4,
|
|
5
|
+
3: 3,
|
|
6
|
+
4: 8,
|
|
7
|
+
5: 62,
|
|
8
|
+
6: 9,
|
|
9
|
+
8: 201,
|
|
10
|
+
9: 80,
|
|
11
|
+
10: 14,
|
|
12
|
+
11: 81,
|
|
13
|
+
12: 5,
|
|
14
|
+
13: 6,
|
|
15
|
+
14: 7,
|
|
16
|
+
15: 19,
|
|
17
|
+
16: 82,
|
|
18
|
+
17: 17,
|
|
19
|
+
28: 28,
|
|
20
|
+
50: 50,
|
|
21
|
+
51: 51,
|
|
22
|
+
60: 60,
|
|
23
|
+
61: 61,
|
|
24
|
+
100: 89,
|
|
25
|
+
});
|
|
26
|
+
const commonEvidence = [
|
|
27
|
+
"8.6.26 BizPropertyType/BizFormPropertyType",
|
|
28
|
+
"desensitized-config-shapes.md §3.2",
|
|
29
|
+
"frontend-dist/admin/js/4280.*.js designer bundle",
|
|
30
|
+
];
|
|
31
|
+
const shape = (propertyType, field, control, evidence = commonEvidence) => ({ propertyType, controlType: CAPABILITY_CONTROL_TYPES[propertyType], field, control, evidence });
|
|
32
|
+
const face = (requiredKeys, defaults, evidence = commonEvidence, nonEmptyKeys) => ({ requiredKeys, defaults, evidence, ...(nonEmptyKeys ? { nonEmptyKeys } : {}) });
|
|
33
|
+
export const FIELD_CAPABILITY_SHAPES = Object.freeze({
|
|
34
|
+
0: shape(0, face([], { dictionaryData: "" }), face(["defaultValue", "maxLength"], { defaultValue: null, placeholder: "请输入", maxLength: 200, currentMaxLength: 2000000, noRepeat: false, enableEncrypt: false })),
|
|
35
|
+
1: shape(1, face([], { dictionaryData: "" }), face(["defaultValue", "maxLength"], { defaultValue: null, textAreaType: "longText", maxLength: 2000, placeholder: "请输入" })),
|
|
36
|
+
2: shape(2, face([], { dictionaryData: "" }), face(["defaultValue", "format"], { defaultValue: null, placeholder: "请输入", computeFormula: "", format1: "decimal", format: "decimal", verifyFormula: "", isFlag: false })),
|
|
37
|
+
3: shape(3, face(["format"], { dictionaryData: "", format: "YYYY-MM-DD" }), face(["format", "format1"], { defaultValue: null, dataItemDefault: "", format1: "YYYY-MM-DD", format: "YYYY-MM-DD", minDate: "", maxDate: "", verifyFormula: "" })),
|
|
38
|
+
4: shape(4, face(["defaultValue"], { defaultValue: false }), face(["defaultValue"], { defaultValue: true })),
|
|
39
|
+
6: shape(6, face([], {}), face(["displayTemplate", "limitNumber", "fileNumber", "batch"], { displayTemplate: true, limitNumber: "unlimited", fileNumber: 1, batch: true, displayFormula: "", requiredFormula: "", readonlyFormula: false, limit: "200M", onUpload: "", onDelete: "" }), ["cp-sources/frontend-dist/admin/js/12404.1675d362.js attachment schema", "cp-sources/frontend-dist/admin/js/24187.6e75bd30.js uploadOptions/baseUploadOptions"]),
|
|
40
|
+
// CHILD_TABLE(8) — scalar child table. Lifted from blocked on 2026-09-18:
|
|
41
|
+
// the wire shape is live-accepted (omc-core `childtable-field-contract.ts`,
|
|
42
|
+
// live record `childtable-execute-20260908070635135.md`, 2026-09-08) and the
|
|
43
|
+
// Sheet201 form control is live-proven (omc-core
|
|
44
|
+
// `childtable-sheet201-contract.ts`). The field row carries no `options`; its
|
|
45
|
+
// definition is the `subSchema` ({code:<parent field code>, properties:[...]})
|
|
46
|
+
// and the form control is Sheet(201) with `columns`. Nested CHILD_TABLE (a
|
|
47
|
+
// child of a child) remains out of coverage and is refused in
|
|
48
|
+
// `assertFieldFamilyAllowed`.
|
|
49
|
+
8: shape(8, face([], {}), face([], {}), ["omc-core childtable-field-contract.ts (live 2026-09-08)", "omc-core childtable-sheet201-contract.ts", "8.6.26 BizPropertyType CHILD_TABLE=8 / BizFormPropertyType Sheet=201"]),
|
|
50
|
+
9: shape(9, face([], { dictionaryData: "" }), face([], { schemaCode: "", displayField: "name", relativePropertyMatchCode: "name", queryCode: "", linkMode: true, showStyle: "", selectMode: "popup", isAuthorize: true, treeView: false, treeStartFromRoot: false, mappings: "", searchFormula: "", conditions: "" })),
|
|
51
|
+
10: shape(10, face(["defaultValue"], { defaultValue: "" }), face(["locationType", "addressDetail", "showEmpty", "emptyValue", "autoGetLocation"], { locationType: "pp-cc-aa", addressDetail: "true", showEmpty: "false", emptyValue: "请选择", autoGetLocation: "false" }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 ADDRESS", "cp-sources/frontend-dist/admin/js/12404.1675d362.js address schema", ".scratch/omc-app-management/evidence/field-families-fwb-20260914.md:113-119 (fwb field row {\"defaultValue\":\"\",\"virtualField\":false})"]),
|
|
52
|
+
11: shape(11, face([], { dictionaryData: "" }), face([], { schemaCode: "", displayField: "name", relativePropertyMatchCode: "name", queryCode: "", linkMode: true, showStyle: "", selectMode: "popup", isAuthorize: true, treeView: false, treeStartFromRoot: false, mappings: "", searchFormula: "", conditions: "" })),
|
|
53
|
+
// Ticket 108 determination: selection families (12/13/14/15) emit NO
|
|
54
|
+
// dictionaryData — designer-produced rows carry none of it (fwb §12-15
|
|
55
|
+
// outer key unions) and live 8.6.26 proves the platform merely
|
|
56
|
+
// accepts-and-persists the extra key without requiring it. The other
|
|
57
|
+
// text/date/relation families keep the designer-carried empty-string
|
|
58
|
+
// default; ATTACHMENT(6) stays intentionally empty (system rows often
|
|
59
|
+
// have no options). The wider 2026-09-14 key unions are multi-field
|
|
60
|
+
// unions of designer rows — platform auto-fill territory, never required
|
|
61
|
+
// for creation (minimal default + caller passthrough is the boundary).
|
|
62
|
+
12: shape(12, face([], {}), face([], { defaultValue: "", options: "", hasLabel: false })),
|
|
63
|
+
13: shape(13, face([], {}), face([], { defaultValue: "", options: "", hasLabel: false, direction: "horizontal", displaySetting: "showSelected" })),
|
|
64
|
+
14: shape(14, face([], {}), face([], { displayEmpty: true, emptyValue: "请选择", multi: false })),
|
|
65
|
+
15: shape(15, face([], {}), face([], { displayEmpty: true, emptyValue: "请选择", multi: true })),
|
|
66
|
+
16: shape(16, face([], { dictionaryData: "" }), face([], {})),
|
|
67
|
+
17: shape(17, face(["defaultValue", "dictionaryData", "maxLength"], { defaultValue: "", dictionaryData: "", maxLength: 200 }), face(["defaultValue", "placeholder", "maxLength"], { defaultValue: "", placeholder: "请输入", maxLength: 200 }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 HYPER_LINK", "cp-sources/frontend-dist/admin/js/12404.1675d362.js URL schema"]),
|
|
68
|
+
28: shape(28, face(["format"], { defaultValue: "", format: "HH:mm" }), face(["format", "format1"], { defaultValue: "none", dataItemDefault: "", format: "HH:mm", format1: "HH:mm", syncFormate: true }), [".scratch/omc-app-management/research/desensitized-config-shapes.md §3.2 TIME", "cp-sources/frontend-dist/admin/js/46948.4825f4e2.js Time1760153573819"]),
|
|
69
|
+
// Org-selector families. Evidence: the yst/fwb published-face dumps
|
|
70
|
+
// (origin/research/yst-examples, 2656 rows of types 5/50/51/60/61) show the
|
|
71
|
+
// FIELD row (bizproperty) options as null or `{defaultValue, mappings,
|
|
72
|
+
// virtualField, noRepeat}` — deptVisible/multi NEVER appear there. The
|
|
73
|
+
// deptVisible/multi discriminator is FORM-CONTROL-face only (source:
|
|
74
|
+
// DataModelInitUtils.updateSelectionType / BizPropertyTypeUpdateUtils.
|
|
75
|
+
// disposeUpdateType read `control.options`; ticket 69 already writes them on
|
|
76
|
+
// the control face). Field faces below mirror the observed publish-safe
|
|
77
|
+
// shapes (single: defaultValue ""; multi: defaultValue []).
|
|
78
|
+
5: shape(5, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["defaultValue", "deptVisible", "multi"], { defaultValue: [], defaultValueType: "", deptVisible: "all", multi: "true" })),
|
|
79
|
+
50: shape(50, face([], { defaultValue: "", mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "user", multi: "false" })),
|
|
80
|
+
51: shape(51, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "user", multi: "true" })),
|
|
81
|
+
60: shape(60, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "org", multi: "false" })),
|
|
82
|
+
61: shape(61, face([], { defaultValue: [], mappings: "", virtualField: false }), face(["deptVisible", "multi"], { deptVisible: "org", multi: "true" })),
|
|
83
|
+
100: shape(100, face([], {}), face([], { defaultValue: null })),
|
|
84
|
+
});
|
|
85
|
+
export const BLOCKED_CAPABILITY_SHAPES = Object.freeze({});
|
|
86
|
+
/**
|
|
87
|
+
* Ticket 101 — control codes observed in real forms but with NO field-family
|
|
88
|
+
* emission path. PHONE(24) and PHOTO(10) are authoritative 8.6.26 control
|
|
89
|
+
* codes (BizFormPropertyType.java:23-51), but BizPropertyType.java:27-54 has
|
|
90
|
+
* no PHONE/PHOTO entry, so no accepted field family can scaffold them and no
|
|
91
|
+
* options shape is evidenced for a write. Declared blocked so the absence
|
|
92
|
+
* reads as policy (fail-closed), not omission; caller-provided explicit
|
|
93
|
+
* layouts are unaffected. Adding an emission path requires new family
|
|
94
|
+
* evidence AND consciously lifting this declaration.
|
|
95
|
+
*/
|
|
96
|
+
export const BLOCKED_CONTROL_CODES = Object.freeze({
|
|
97
|
+
24: "PHONE control has no BizPropertyType mapping (BizPropertyType.java:27-54) — no field family emits it; creation not offered until a family + options shape are evidenced",
|
|
98
|
+
10: "PHOTO control has no BizPropertyType mapping (BizPropertyType.java:27-54) — no field family emits it; creation not offered until a family + options shape are evidenced",
|
|
99
|
+
});
|
|
100
|
+
export const CAPABILITY_SHAPE_ACCEPTED_TYPES = Object.freeze(Object.keys(FIELD_CAPABILITY_SHAPES).map(Number));
|
|
101
|
+
export function capabilityShapeFor(type) {
|
|
102
|
+
return FIELD_CAPABILITY_SHAPES[type] ?? BLOCKED_CAPABILITY_SHAPES[type];
|
|
103
|
+
}
|
|
104
|
+
export function capabilityShapeError(type, faceName, options) {
|
|
105
|
+
const current = capabilityShapeFor(type);
|
|
106
|
+
if (!current)
|
|
107
|
+
return `propertyType ${type} has no capability shape`;
|
|
108
|
+
if (current.blocked)
|
|
109
|
+
return `${current.citation ?? "blocked capability"} (propertyType ${type})`;
|
|
110
|
+
const face = current[faceName];
|
|
111
|
+
const missing = face.requiredKeys.filter((key) => options[key] === undefined || ((face.nonEmptyKeys ?? []).includes(key) && options[key] === ""));
|
|
112
|
+
return missing.length > 0 ? `capability shape ${faceName} missing required keys for propertyType ${type}: ${missing.join(", ")}` : undefined;
|
|
113
|
+
}
|
|
114
|
+
export function assertCapabilityShapeContract() {
|
|
115
|
+
for (const type of CAPABILITY_SHAPE_ACCEPTED_TYPES) {
|
|
116
|
+
const current = FIELD_CAPABILITY_SHAPES[type];
|
|
117
|
+
if (!current)
|
|
118
|
+
throw new Error(`capability shape missing for propertyType ${type}`);
|
|
119
|
+
if (current.blocked && !current.citation)
|
|
120
|
+
throw new Error(`blocked capability shape ${type} requires citation`);
|
|
121
|
+
if (current.controlType !== CAPABILITY_CONTROL_TYPES[type])
|
|
122
|
+
throw new Error(`capability shape control type mismatch for ${type}`);
|
|
123
|
+
for (const key of current.field.requiredKeys)
|
|
124
|
+
if (!(key in current.field.defaults))
|
|
125
|
+
throw new Error(`field shape ${type} missing default for ${key}`);
|
|
126
|
+
for (const key of current.control.requiredKeys)
|
|
127
|
+
if (!(key in current.control.defaults))
|
|
128
|
+
throw new Error(`control shape ${type} missing default for ${key}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export function assertCapabilityFace(type, faceName, options) {
|
|
132
|
+
const current = capabilityShapeFor(type);
|
|
133
|
+
if (!current)
|
|
134
|
+
throw new Error(`capability shape missing for propertyType ${type}`);
|
|
135
|
+
if (current.blocked)
|
|
136
|
+
throw new Error(`propertyType ${type} is blocked: ${current.citation ?? "missing evidence"}`);
|
|
137
|
+
const face = current[faceName];
|
|
138
|
+
const missing = face.requiredKeys.filter((key) => options[key] === undefined || ((face.nonEmptyKeys ?? []).includes(key) && options[key] === ""));
|
|
139
|
+
if (missing.length > 0)
|
|
140
|
+
throw new Error(`capability shape ${faceName} missing required keys for propertyType ${type}: ${missing.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
export function deriveFieldOptions(type, explicit) {
|
|
143
|
+
const current = capabilityShapeFor(type);
|
|
144
|
+
if (!current || current.blocked)
|
|
145
|
+
throw new Error(`propertyType ${type} is blocked or has no capability shape`);
|
|
146
|
+
return { ...current.field.defaults, ...(explicit ?? {}) };
|
|
147
|
+
}
|
|
148
|
+
export function deriveControlOptions(type, explicit) {
|
|
149
|
+
const current = capabilityShapeFor(type);
|
|
150
|
+
if (!current || current.blocked)
|
|
151
|
+
throw new Error(`propertyType ${type} is blocked or has no capability shape`);
|
|
152
|
+
return { ...current.control.defaults, ...(explicit ?? {}) };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=capability-shape.js.map
|