@stalfh233/omc-cli 0.4.15 → 0.4.16

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 (184) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +297 -297
  3. package/coverage/m1-coverage-manifest-v1.json +1278 -1277
  4. package/dist/args.js +0 -0
  5. package/dist/artifacts.js +0 -0
  6. package/dist/bench/call-face-eval.js +0 -0
  7. package/dist/bench/context-attribution.js +0 -0
  8. package/dist/bench/discovery-cost-eval.js +0 -0
  9. package/dist/bench/driver.js +0 -0
  10. package/dist/bench/evals.js +0 -0
  11. package/dist/bench/fake-http-server.js +0 -0
  12. package/dist/bench/instrument.js +0 -0
  13. package/dist/bench/intent-face-eval.js +40 -40
  14. package/dist/bench/run.js +0 -0
  15. package/dist/bench/scenario.js +0 -0
  16. package/dist/bench/types.js +0 -0
  17. package/dist/bi-wire.js +0 -0
  18. package/dist/bizservice-config.js +38 -13
  19. package/dist/call.js +0 -0
  20. package/dist/capability-absences.js +0 -0
  21. package/dist/capability-overview.js +2 -2
  22. package/dist/capability-shape.js +0 -0
  23. package/dist/cli-contract.js +0 -0
  24. package/dist/cli-output.js +41 -15
  25. package/dist/cli.js +22 -2
  26. package/dist/code-rules.js +0 -0
  27. package/dist/command-transport.js +0 -0
  28. package/dist/config-store.js +0 -0
  29. package/dist/context.js +0 -0
  30. package/dist/contract-consistency.js +0 -0
  31. package/dist/contract-resources.js +0 -0
  32. package/dist/coverage-consistency.js +0 -0
  33. package/dist/coverage-registry.js +0 -0
  34. package/dist/coverage.js +0 -0
  35. package/dist/data-list-filter.js +0 -0
  36. package/dist/discovery.js +0 -0
  37. package/dist/endpoints.js +2 -0
  38. package/dist/environment-policy.js +0 -0
  39. package/dist/execution-metadata.js +4 -4
  40. package/dist/fake/app.js +0 -0
  41. package/dist/fake/b2-registration.js +26 -0
  42. package/dist/fake/businessrule.js +0 -0
  43. package/dist/fake/datarule.js +0 -0
  44. package/dist/fake/dictionary.js +0 -0
  45. package/dist/fake/environment.js +0 -0
  46. package/dist/fake/field.js +0 -0
  47. package/dist/fake/form.js +0 -0
  48. package/dist/fake/index.js +0 -0
  49. package/dist/fake/list-view.js +0 -0
  50. package/dist/fake/model.js +0 -0
  51. package/dist/fake/online-js.js +0 -0
  52. package/dist/fake/report.js +0 -0
  53. package/dist/fake/routes.js +0 -0
  54. package/dist/fake/rule-lifecycle.js +0 -0
  55. package/dist/fake/runtime-data.js +0 -0
  56. package/dist/fake/state.js +0 -0
  57. package/dist/fake/workflow.js +0 -0
  58. package/dist/field-change.js +0 -0
  59. package/dist/field-families.js +0 -0
  60. package/dist/form-layout.js +0 -0
  61. package/dist/form-support.js +12 -2
  62. package/dist/goal-routes.js +0 -0
  63. package/dist/governed-execution.js +0 -0
  64. package/dist/human-summary.js +0 -0
  65. package/dist/identity.js +0 -0
  66. package/dist/intent/baseline.js +0 -0
  67. package/dist/intent/capabilities/bizservice.js +0 -0
  68. package/dist/intent/capabilities/businessrule.js +0 -0
  69. package/dist/intent/capabilities/datarule.js +0 -0
  70. package/dist/intent/capabilities/field.js +0 -0
  71. package/dist/intent/capabilities/form.js +0 -0
  72. package/dist/intent/capabilities/index.js +0 -0
  73. package/dist/intent/capabilities/listview.js +0 -0
  74. package/dist/intent/capabilities/model.js +0 -0
  75. package/dist/intent/capabilities/onlinejs.js +0 -0
  76. package/dist/intent/capabilities/report.js +0 -0
  77. package/dist/intent/capabilities/workflow.js +0 -0
  78. package/dist/intent/capability.js +0 -0
  79. package/dist/intent/cli.js +0 -0
  80. package/dist/intent/compare.js +0 -0
  81. package/dist/intent/compiler.js +0 -0
  82. package/dist/intent/dsl.js +0 -0
  83. package/dist/intent/plan-file.js +0 -0
  84. package/dist/intent/readback.js +0 -0
  85. package/dist/intent/schema.js +0 -0
  86. package/dist/intent/validation.js +0 -0
  87. package/dist/intent/yaml.js +0 -0
  88. package/dist/json-column.js +0 -0
  89. package/dist/lanes/app-contract.js +0 -0
  90. package/dist/lanes/app-coverage.js +0 -0
  91. package/dist/lanes/app.js +0 -0
  92. package/dist/lanes/apply-changes.js +14 -1
  93. package/dist/lanes/b2-registration-contract.js +5 -0
  94. package/dist/lanes/b2-registration-coverage.js +1 -0
  95. package/dist/lanes/b2-registration.js +55 -6
  96. package/dist/lanes/businessrule-contract.js +0 -0
  97. package/dist/lanes/businessrule-coverage.js +0 -0
  98. package/dist/lanes/businessrule.js +0 -0
  99. package/dist/lanes/contract-support.js +0 -0
  100. package/dist/lanes/coverage-declaration.js +0 -0
  101. package/dist/lanes/datarule-contract.js +0 -0
  102. package/dist/lanes/datarule-coverage.js +0 -0
  103. package/dist/lanes/datarule-protocol.js +0 -0
  104. package/dist/lanes/datarule.js +0 -0
  105. package/dist/lanes/dictionary-contract.js +0 -0
  106. package/dist/lanes/dictionary-coverage.js +0 -0
  107. package/dist/lanes/dictionary.js +0 -0
  108. package/dist/lanes/environment-contract.js +0 -0
  109. package/dist/lanes/environment-coverage.js +0 -0
  110. package/dist/lanes/environment.js +0 -0
  111. package/dist/lanes/field-contract.js +3 -2
  112. package/dist/lanes/field-coverage.js +0 -0
  113. package/dist/lanes/field.js +268 -172
  114. package/dist/lanes/form-contract.js +2 -2
  115. package/dist/lanes/form-coverage.js +0 -0
  116. package/dist/lanes/form.js +4 -0
  117. package/dist/lanes/lane-ids.js +0 -0
  118. package/dist/lanes/list-view-contract.js +0 -0
  119. package/dist/lanes/list-view-coverage.js +0 -0
  120. package/dist/lanes/list-view-shapes.js +0 -0
  121. package/dist/lanes/list-view.js +0 -0
  122. package/dist/lanes/meta-contract.js +0 -0
  123. package/dist/lanes/meta.js +11 -1
  124. package/dist/lanes/model-contract.js +0 -0
  125. package/dist/lanes/model-coverage.js +0 -0
  126. package/dist/lanes/model.js +1 -1
  127. package/dist/lanes/online-js-contract.js +0 -0
  128. package/dist/lanes/online-js-coverage.js +0 -0
  129. package/dist/lanes/online-js.js +0 -0
  130. package/dist/lanes/report-contract.js +0 -0
  131. package/dist/lanes/report-coverage.js +0 -0
  132. package/dist/lanes/report.js +0 -0
  133. package/dist/lanes/rule-graph.js +0 -0
  134. package/dist/lanes/rule-lifecycle-contract.js +0 -0
  135. package/dist/lanes/rule-lifecycle-coverage.js +0 -0
  136. package/dist/lanes/rule-lifecycle.js +0 -0
  137. package/dist/lanes/runtime-data-contract.js +0 -0
  138. package/dist/lanes/runtime-data-coverage.js +0 -0
  139. package/dist/lanes/runtime-data.js +0 -0
  140. package/dist/lanes/workflow-contract.js +0 -0
  141. package/dist/lanes/workflow-coverage.js +0 -0
  142. package/dist/lanes/workflow.js +2 -0
  143. package/dist/omc-core-lazy.js +6 -0
  144. package/dist/online-js-layout.js +0 -0
  145. package/dist/online-js-source.js +28 -28
  146. package/dist/package-tool.js +0 -0
  147. package/dist/package.js +0 -0
  148. package/dist/plan.js +11 -1
  149. package/dist/read.js +0 -0
  150. package/dist/redact.js +0 -0
  151. package/dist/rule-support.js +0 -0
  152. package/dist/runtime-support.js +0 -0
  153. package/dist/service-doctor.js +28 -0
  154. package/dist/session-manager.js +0 -0
  155. package/dist/session.js +38 -12
  156. package/dist/skills.js +0 -0
  157. package/dist/sqlite-warning-filter.js +27 -0
  158. package/dist/support.js +0 -0
  159. package/dist/tool-types.js +0 -0
  160. package/dist/tools.js +0 -0
  161. package/dist/usage-log.js +4 -0
  162. package/dist/wire.js +0 -0
  163. package/dist/workflow-support.js +0 -0
  164. package/dist/write-lease.js +0 -0
  165. package/dist/write-lock.js +0 -0
  166. package/dist/write.js +24 -2
  167. package/dist/zip.js +0 -0
  168. package/docs/tool-surface-map.md +42 -42
  169. package/package.json +71 -71
  170. package/skills/omc-acceptance-criteria.md +47 -47
  171. package/skills/omc-business-configuration.md +258 -257
  172. package/skills/omc-capabilities.md +197 -197
  173. package/skills/omc-capability-scouting.md +55 -55
  174. package/skills/omc-config-draft-review.md +171 -171
  175. package/skills/omc-five-piece-flow.md +35 -35
  176. package/skills/omc-glossary.md +86 -86
  177. package/skills/omc-refusals.md +110 -110
  178. package/skills/omc-requirement-analysis.md +161 -161
  179. package/skills/omc-requirement-vocabulary.md +51 -51
  180. package/skills/omc-start-here.md +82 -82
  181. package/skills/omc-tool-selection.md +119 -119
  182. package/skills/omc-write-hazards.md +88 -87
  183. package/dist/approval-token.js +0 -102
  184. package/dist/server.js +0 -92
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/dist/lanes/app.js CHANGED
File without changes
@@ -162,7 +162,20 @@ export function registerApplyChangesLane(tools) {
162
162
  }
163
163
  try {
164
164
  const outcome = await target.writeExecute(session, change.args);
165
- steps.push({ step: change.tool, index, status: "ok", performed: outcome.performed, readback: outcome.readback });
165
+ // A step-chain write (field.update cascadeRepublish) may RETURN a
166
+ // mid-chain stop instead of throwing: surface it as this batch's
167
+ // stop with the inner chain preserved for audit.
168
+ if ("status" in outcome) {
169
+ const progressFile = recordProgress("STOPPED", steps.length);
170
+ const cascadeStop = {
171
+ status: outcome.status,
172
+ steps,
173
+ failedStep: { index, tool: change.tool },
174
+ envelope: { ...outcome.envelope, message: `${String(outcome.envelope.message ?? "步骤失败")}(第 ${index + 1} 步 ${change.tool} 的内部级联中途停止)`, progressFile },
175
+ };
176
+ return cascadeStop;
177
+ }
178
+ steps.push({ step: change.tool, index, status: "ok", performed: outcome.performed, readback: outcome.readback, ...(outcome.steps !== undefined ? { chainSteps: outcome.steps } : {}) });
166
179
  recordProgress("RUNNING", index + 1);
167
180
  }
168
181
  catch (error) {
@@ -111,6 +111,10 @@ export const B2_LANE_CONTRACT = Object.freeze({
111
111
  scope: "readback",
112
112
  description: "B2 业务集成服务读回:get_by_code + 方法列表(filterFlag=false 原样;方法行解码 configJson/inputParametersJson/outputParametersJson,方法 configJson 读回为明文、枚举双表示解码为名字形——8.6.26 写侧 'Post'/8.6.39 读侧 0 都解码;方法列表失败不阻断服务读回)。读面覆盖二开扩展包发布的方法(同表存储;adapterCode=INTERNAL 是本地扩展适配器)。",
113
113
  }),
114
+ "service.list": Object.freeze({
115
+ scope: "readback",
116
+ description: "列出业务集成服务(/bizservice/service/list_by_category_id;categoryId 省略或空串=全部;keyword 走 list_by_category_id_find 仅匹配 name/code 大小写无关包含)。响应是字段白名单投影(id/code/name/adapterCode/serviceCategoryId/description/shared)——平台行携带完整 configJson(授权配置,可能带凭据),工具层永不回显 configJson。",
117
+ }),
114
118
  "service.register": Object.freeze({
115
119
  scope: B2_SCOPE,
116
120
  description: "注册业务集成服务(`/bizservice/service/create`,body `{code,name,adapterCode,description,configJson?}`;adapterCode 空 → 500005)+ 可选方法(`/bizservice/method/create`,可带 `configJson`/`inputParametersJson`/`outputParametersJson`),并回读。serviceConfig 明文序列化(RESTFUL/INTERNAL/SOAP 需 auth+commonlist.url;DATA_SQL/DATA_TABLE 需 commonlist.databaseConnectionPoolCode;SAP 需 commonlist.sapConnectionPoolCode;KAFKA 需 commonlist.kafkaConnectionPoolCode),methodConfig 由工具 AES-128-ECB+Base64 加密(SOAP 方法由 WSDL 动态发现,无固定方法 configJson)。参数面结构化序列化;RESTFUL 每参数需 configJson.position,URL_VARIABLE 拒绝;required 三态透传。设计器同款必填:name(≤50)、adapterCode、serviceCategoryId;除 SOAP 动态方法外,各原生适配器带 methodName 时必带 methodConfig;INTERNAL 方法创建走二开扩展包导入,不在本写面;仅名字/参数面的注册壳不可绑定。",
@@ -225,6 +229,7 @@ export function assertB2Contract() {
225
229
  throw new Error("b2 contract must pin the update metadata passthrough");
226
230
  if (!B2_WIRE.requiredFields.includes("serviceCategoryId"))
227
231
  throw new Error("b2 contract must pin the designer-parity requireds");
232
+ assertDescriptionCarries(B2_LANE_CONTRACT, "service.list", ["list_by_category_id", "categoryId", "keyword", "configJson", "白名单"]);
228
233
  assertDescriptionCarries(B2_LANE_CONTRACT, "service.category.list", ["category/list", "所属目录"]);
229
234
  assertDescriptionCarries(B2_LANE_CONTRACT, "service.category.create", ["category/create", "name"]);
230
235
  assertDescriptionCarries(B2_LANE_CONTRACT, "service.category.update", ["category/update", "501052", "501004"]);
@@ -7,6 +7,7 @@ export const B2_COVERAGE = declareLaneCoverage(B2_LANE_CONTRACT.lane, [
7
7
  evidence: [".scratch/omc-route-wayfinder/leg2-closeout.md", "packages/omc-cli/src/lanes/b2-registration.ts", "packages/omc-cli/src/bizservice-config.ts", ".scratch/omc-app-management/research/bizservice-integration-backend-face.md", ".scratch/omc-app-management/evidence/bizservice-rule-binding-fwb-20260914.md"],
8
8
  tools: [
9
9
  claim("service.get", T["service.get"].scope),
10
+ claim("service.list", T["service.list"].scope),
10
11
  claim("service.register", T["service.register"].scope),
11
12
  claim("service.update", T["service.update"].scope),
12
13
  claim("method.get", T["method.get"].scope),
@@ -12,7 +12,7 @@ import { wireCall, wireRows, wireDownloadBuffer, wireUploadFile, WireError } fro
12
12
  import { ZipFormatError, readZipEntries, zipEntryByBasename } from "../zip.js";
13
13
  import { B2_LANE_CONTRACT } from "./b2-registration-contract.js";
14
14
  import { BIZSERVICE_WRITE_ADAPTERS, assertBizServiceAdapterWhitelisted, assertStructuredConfig, compileMethodConfigPlain, compileParameterFace, compileServiceConfig, maskServiceConfigSecrets, normalizeMethodConfigReadback, normalizeParameterFaceReadback, parseIeManifest, iePackageSummary, } from "../bizservice-config.js";
15
- import { encryptBizServiceMethodConfigJson, encryptBizDbPoolPassword } from "omc-core";
15
+ import { omcCore } from "../omc-core-lazy.js";
16
16
  const B2_LANE = B2_LANE_CONTRACT.lane;
17
17
  const B2_TOOLS = B2_LANE_CONTRACT.tools;
18
18
  /** Decode a JSON-string column (configJson/inputParametersJson/…); policy: non-strings null-coalesce, unparseable passes through. */
@@ -76,6 +76,25 @@ function parseDeclaredParameterCodes(inputParametersJson) {
76
76
  }
77
77
  return { all, required };
78
78
  }
79
+ /**
80
+ * Ticket T08 — readback face for a service LIST row: an explicit field
81
+ * whitelist WITHOUT `configJson`. The platform's list_by_category_id rows
82
+ * carry the full BizServiceModel including the authorization config (which
83
+ * may contain credentials), so the projection lives at the tool boundary —
84
+ * the list face is for inventory/disambiguation; the structured (masked)
85
+ * config is service.get's job.
86
+ */
87
+ function serviceReadbackFace(row) {
88
+ return {
89
+ id: row.id,
90
+ code: row.code,
91
+ name: row.name,
92
+ ...(row.adapterCode !== undefined && row.adapterCode !== null ? { adapterCode: row.adapterCode } : {}),
93
+ ...(row.serviceCategoryId !== undefined && row.serviceCategoryId !== null ? { serviceCategoryId: row.serviceCategoryId } : {}),
94
+ ...(row.description !== undefined && row.description !== null ? { description: row.description } : {}),
95
+ ...(typeof row.shared === "boolean" ? { shared: row.shared } : {}),
96
+ };
97
+ }
79
98
  /**
80
99
  * Ticket 104 — readback face for a connection-pool row: an explicit field
81
100
  * whitelist WITHOUT `password`. The platform's raw list does not mask the
@@ -180,6 +199,36 @@ export function registerB2Lane(tools) {
180
199
  };
181
200
  }),
182
201
  }));
202
+ tools.push(defineTool({
203
+ name: "service.list",
204
+ layer: "low",
205
+ kind: "read",
206
+ lane: B2_LANE,
207
+ scope: B2_TOOLS["service.list"].scope,
208
+ description: B2_TOOLS["service.list"].description,
209
+ inputSchema: {
210
+ environment: environmentArg,
211
+ categoryId: z.string().optional().describe("所属分类 id(service.category.list 取;省略或空串=全部,与设计器一致)"),
212
+ keyword: z.string().optional().describe("按名称/编码过滤(大小写无关包含;传了走 list_by_category_id_find)"),
213
+ capability: capabilityArg,
214
+ },
215
+ handler: async (ctx, args, self) => governedRead(ctx, self, args, async (session) => {
216
+ // 8.6.26 evidence (BizServiceController.java): list_by_category_id with
217
+ // categoryId="" lists ALL services (the designer's「全部」row); the
218
+ // _find variant filters name/code case-insensitively. Both return the
219
+ // FULL BizServiceModel — configJson included (authorization config,
220
+ // possibly carrying credentials). The tool projects a field whitelist
221
+ // at the boundary: configJson (and any non-whitelist column) NEVER
222
+ // echoes. Read a single service's structured (masked) config via
223
+ // service.get.
224
+ const rows = wireRows((await wireCall(session, {
225
+ path: args.keyword !== undefined ? ENDPOINTS.bizServiceListByCategoryIdFind : ENDPOINTS.bizServiceListByCategoryId,
226
+ query: { categoryId: args.categoryId ?? "", ...(args.keyword !== undefined ? { keyword: args.keyword } : {}) },
227
+ })).data);
228
+ const services = rows.map((row) => serviceReadbackFace(row));
229
+ return { services, codes: services.map((row) => row.code) };
230
+ }),
231
+ }));
183
232
  tools.push(defineTool({
184
233
  name: "method.get",
185
234
  layer: "low",
@@ -253,7 +302,7 @@ export function registerB2Lane(tools) {
253
302
  serviceCode: args.serviceCode,
254
303
  code: args.methodCode,
255
304
  name: args.name ?? (typeof currentRecord.name === "string" ? currentRecord.name : args.methodCode),
256
- ...(methodConfigPlain !== undefined ? { configJson: encryptBizServiceMethodConfigJson(methodConfigPlain) } : currentConfigPlain !== undefined ? { configJson: encryptBizServiceMethodConfigJson(currentConfigPlain) } : {}),
305
+ ...(methodConfigPlain !== undefined ? { configJson: (await omcCore()).encryptBizServiceMethodConfigJson(methodConfigPlain) } : currentConfigPlain !== undefined ? { configJson: (await omcCore()).encryptBizServiceMethodConfigJson(currentConfigPlain) } : {}),
257
306
  ...(inputJson !== undefined && inputJson !== null ? { inputParametersJson: inputJson } : {}),
258
307
  ...(outputJson !== undefined && outputJson !== null ? { outputParametersJson: outputJson } : {}),
259
308
  },
@@ -300,7 +349,7 @@ export function registerB2Lane(tools) {
300
349
  serviceCode: args.serviceCode,
301
350
  code: args.methodCode,
302
351
  name: args.name,
303
- ...(methodConfigPlain !== undefined ? { configJson: encryptBizServiceMethodConfigJson(methodConfigPlain) } : {}),
352
+ ...(methodConfigPlain !== undefined ? { configJson: (await omcCore()).encryptBizServiceMethodConfigJson(methodConfigPlain) } : {}),
304
353
  ...(inputJson !== undefined && inputJson !== null ? { inputParametersJson: inputJson } : {}),
305
354
  ...(outputJson !== undefined && outputJson !== null ? { outputParametersJson: outputJson } : {}),
306
355
  },
@@ -458,7 +507,7 @@ export function registerB2Lane(tools) {
458
507
  databaseType: args.databaseType,
459
508
  jdbcUrl: args.jdbcUrl,
460
509
  ...(args.username !== undefined ? { username: args.username } : {}),
461
- ...(args.password !== undefined ? { password: encryptBizDbPoolPassword(args.password) } : {}),
510
+ ...(args.password !== undefined ? { password: (await omcCore()).encryptBizDbPoolPassword(args.password) } : {}),
462
511
  ...(args.description !== undefined ? { description: args.description } : {}),
463
512
  ...(args.datasourceType !== undefined ? { datasourceType: args.datasourceType } : {}),
464
513
  ...(args.externInfo !== undefined ? { externInfo: args.externInfo } : {}),
@@ -511,7 +560,7 @@ export function registerB2Lane(tools) {
511
560
  // New passwords ride the RSA boundary; the echoed stored value is
512
561
  // already ciphertext (designer parity — the overwrite keeps the
513
562
  // credential column intact when the caller does not rotate it).
514
- password: args.password !== undefined ? encryptBizDbPoolPassword(args.password) : currentRecord.password ?? "",
563
+ password: args.password !== undefined ? (await omcCore()).encryptBizDbPoolPassword(args.password) : currentRecord.password ?? "",
515
564
  description: args.description !== undefined ? args.description : currentRecord.description ?? "",
516
565
  datasourceType: args.datasourceType ?? currentRecord.datasourceType ?? "DATABASE",
517
566
  externInfo: args.externInfo !== undefined ? args.externInfo : currentRecord.externInfo ?? "",
@@ -683,7 +732,7 @@ export function registerB2Lane(tools) {
683
732
  method: "POST",
684
733
  body: {
685
734
  serviceCode: args.serviceCode, code: args.methodName, name: args.methodName,
686
- ...(methodConfigPlain !== undefined ? { configJson: encryptBizServiceMethodConfigJson(methodConfigPlain) } : {}),
735
+ ...(methodConfigPlain !== undefined ? { configJson: (await omcCore()).encryptBizServiceMethodConfigJson(methodConfigPlain) } : {}),
687
736
  ...(inputJson !== undefined ? { inputParametersJson: inputJson } : {}),
688
737
  ...(outputJson !== undefined ? { outputParametersJson: outputJson } : {}),
689
738
  },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -183,7 +183,8 @@ export const FIELD_LANE_CONTRACT = Object.freeze({
183
183
  "Use it to add relation `mappings` (source/target carry-through) or change field options.",
184
184
  "A relation's display field is the field-row top-level `relativePropertyCode` (options.displayField is mirrored but not what the runtime reads); the tool rewrites it when you pass a new displayField.",
185
185
  "表单条件(`options.requiredFormula`(必填)/`readonlyCondition`(只读)/`displayFormula`(显示))会**自动配对**一条 `bizdatarule` type3/4/5 行:门户**运行时**读字段 options,表单设计器**设计态** Set/Unset 读 `bizdatarule/list`——只写 options 会让设计器显示 Unset。readback.formConditionSync 报 created/updated/deleted/skipped;清空条件传空串(如 requiredFormula:\"\")会同时删规则行;字段未发布时只写 options 并在 skipped 说明原因。",
186
- "Re-publish with field.publish for the change to take effect.",
186
+ "Re-publish with field.publish for the change to take effect — or pass `cascadeRepublish:true` for the one-call chain: field.update → field.publish → form.draft(rebuild) → form.publish(force). The cascade re-scaffolds the default form from the published fields (non-structural control options are preserved); there is no cross-endpoint transaction, so a mid-chain failure is reported honestly as partial (status:partial) with steps/failedStep/envelope (the completed prefix stays).",
187
+ "form.publish(rebuild:true) is the same form-side refresh without re-touching the field: it discards the existing draft, re-scaffolds from the published face, and force-promotes.",
187
188
  ]),
188
189
  }),
189
190
  "field.publish": Object.freeze({
@@ -216,7 +217,7 @@ export function assertFieldContract() {
216
217
  if (FIELD_LANE_CONTRACT.wire.publishScope !== "model")
217
218
  throw new Error("field contract pins the publish scope to model");
218
219
  assertDescriptionCarries(FIELD_LANE_CONTRACT, "field.create", ["bizproperty/create", "OCR/ELECTRONIC_SIGN", "`choices`", "options.optionsSet", "`relativeCode`", "searchFormula", "`options.formulaConfig`", "`options.relevanceField`", "code_reserved", "WORK_SHEET"]);
219
- assertDescriptionCarries(FIELD_LANE_CONTRACT, "field.update", ["bizproperty/update", "`options`", "field.publish"]);
220
+ assertDescriptionCarries(FIELD_LANE_CONTRACT, "field.update", ["bizproperty/update", "`options`", "field.publish", "cascadeRepublish", "form.draft(rebuild)", "form.publish(force)", "published fields", "non-structural", "partial"]);
220
221
  assertDescriptionCarries(FIELD_LANE_CONTRACT, "field.publish", ["bizproperty/publish", "published", "Model-scoped", "publishedCodes"]);
221
222
  assertDescriptionCarries(FIELD_LANE_CONTRACT, "field.delete", ["bizproperty/force_delete", "absence"]);
222
223
  }
File without changes