@stalfh233/omc-cli 0.5.16 → 0.6.0

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/README.md CHANGED
@@ -101,11 +101,12 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
101
101
  | 业务规则 `B1-businessrule` | `rule.list`、`rule.get` |
102
102
  | 流程 / 待办 / 组织 `W7-S29-greenfield` | `workflow.get_draft`(每节点 `formButtons.enabled/disabled`)、`workflow.get_published`、`workflow.validate`、`workflow.instanceActivity`、`workitem.list`、`org.role.list`、`org.role.users`、`org.role.userScope`、`org.department.list`、`org.department.users`、`org.user.search`、`org.user.info`、`org.user.departments` |
103
103
  | 在线脚本 `online-js` | `onlinejs.get`(草稿/发布面 `customScript` 原文 + SHA-256) |
104
+ | 后端在线开发 `programming-online-dev` | `programming.schemas`、`programming.sources`、`programming.get`、`programming.compile`、`programming.save`、`programming.publish`(另配 `omc programming pull/status/apply/adopt/test` 文件级工作区) |
104
105
  | 运行数据 `M2-runtime-data` | `data.list`、`data.load` |
105
106
  | 报表 `R1-report` | `report.tree`、`report.get`、`report.chartdata`、`report.datasource.list` |
106
107
  | 业务集成 `B2-registration` | `service.list`、`service.get`、`method.get`、`service.category.list`、`service.dbconnpool.list`、`service.adapter_config`、`service.table_list`、`service.table_sql`、`service.export`、`service.check` |
107
108
 
108
- ## 能力总览(16 项)
109
+ ## 能力总览(17 项)
109
110
 
110
111
  `omc capabilities` 的静态快照;跑 `omc capabilities` / `omc capability <id>` 取实时投影。
111
112
  「依赖」= `requires → precedes`,即能力之间的配置顺序。
@@ -125,6 +126,7 @@ omc call discover_environment --env <env> --args '{}' # 环境就绪(真实
125
126
  | `rule-lifecycle` | 规则生命周期 | `S25-rule-lifecycle` | executable | none | businessrule → - |
126
127
  | `workflow` | 流程模板 | `W7-S29-greenfield` | executable | partial | model → - |
127
128
  | `onlinejs` | 表单在线 JS | `online-js` | executable | partial | form → - |
129
+ | `programming` | 后端在线开发(业务规则 Java 源码) | `programming-online-dev` | executable | none | businessrule → - |
128
130
  | `bizservice` | 业务集成 | `B2-registration` | executable | partial | - → - |
129
131
  | `report` | 报表 | `R1-report` | partial | partial | - → - |
130
132
  | `data` | 运行数据 | `M2-runtime-data` | executable | none | field → - |
@@ -971,6 +971,44 @@
971
971
  "draft published script readback"
972
972
  ]
973
973
  },
974
+ {
975
+ "capability": "P1 programming online development",
976
+ "status": "executable",
977
+ "scope": "Design-time 在线开发 (/api/api/programming): list codable schemas/sources, read single source via get_source (template generation when no draft), remote compile check, save draft (version 0, unconditional overwrite), publish (server-side save+compile+published row, and BUSINESS_RULE rule flipped to CODING+enabled+published). IDE-only endpoints (startDebugging/debug/complete/go_definition) are out of scope; SPI/SERVICE source families are not opened in 8.6.26.",
978
+ "excluded_or_unsupported_reason": "startDebugging/debug/complete/go_definition are WEB-IDE-only (attach-style debugging, no synthetic payload). SPI/SERVICE get_source is rejected by the platform in 8.6.26. Published content has no wire read endpoint, so rollback relies on local git.",
979
+ "version_family": "8.6",
980
+ "anchor_version": "8.6.26",
981
+ "evidence": [
982
+ {
983
+ "path": ".scratch/backend-online-coding/recon.md",
984
+ "anchor": "§2 controller; §8 facade storage/runtime; §7.8 authority"
985
+ },
986
+ {
987
+ "path": "packages/omc-cli/src/lanes/programming.ts",
988
+ "anchor": "registerProgrammingLane"
989
+ },
990
+ {
991
+ "path": "packages/omc-cli/src/lanes/programming-contract.ts",
992
+ "anchor": "PROGRAMMING_LANE_CONTRACT"
993
+ },
994
+ {
995
+ "path": "packages/omc-cli/test/programming.test.ts",
996
+ "anchor": "programming lane tests"
997
+ }
998
+ ],
999
+ "lane": "programming-online-dev",
1000
+ "tools": [
1001
+ "programming.schemas",
1002
+ "programming.sources",
1003
+ "programming.get",
1004
+ "programming.compile",
1005
+ "programming.save",
1006
+ "programming.publish"
1007
+ ],
1008
+ "scopes": [
1009
+ "programming backend java source business rule handler read template compile save draft publish"
1010
+ ]
1011
+ },
974
1012
  {
975
1013
  "capability": "M2 runtime data write and authoritative readback (form/save + form/load)",
976
1014
  "status": "executable",
@@ -116,6 +116,12 @@ export const CALL_FACE_TASKS = [
116
116
  await agent.discovery("contract", "B1-businessrule");
117
117
  await agent.write({ __tool: "model.create", name: "回填源", code: "eval_rs" });
118
118
  await agent.write({ __tool: "model.create", name: "回填目标", code: "eval_rt" });
119
+ // Live-fidelity: the platform's rule self-check (550018) only accepts
120
+ // PUBLISHED target fields — on a real model these columns exist. Seed them
121
+ // (the fake's self-check is the 1.1.25-faithful gate, not a stub).
122
+ server.fake.seedField("eval_rt", "latestPeriod", 0);
123
+ server.fake.seedField("eval_rs", "declarePeriod", 0);
124
+ server.fake.seedField("eval_rs", "project", 9);
119
125
  server.fake.seedDataRow("eval_rt", { latestPeriod: "" }, "eval_rt_row");
120
126
  await agent.write({
121
127
  __tool: "rule.save",
@@ -177,6 +177,12 @@ models:
177
177
  // Uncovered capability: the businessrule graph goes through the escape
178
178
  // hatch (omc call) under the same governance.
179
179
  await agent.discovery("contract", "B1-businessrule");
180
+ // Live-fidelity: the platform's rule self-check (550018) only accepts
181
+ // PUBLISHED target fields — on a real model these columns exist. Seed them
182
+ // (the fake's self-check is the 1.1.25-faithful gate, not a stub).
183
+ server.fake.seedField("eval_rt", "latestPeriod", 0);
184
+ server.fake.seedField("eval_rs", "declarePeriod", 0);
185
+ server.fake.seedField("eval_rs", "project", 9);
180
186
  server.fake.seedDataRow("eval_rt", { latestPeriod: "" }, "eval_rt_row");
181
187
  await agent.write({
182
188
  __tool: "rule.save",
@@ -137,7 +137,17 @@ const SURFACES = [
137
137
  operations: ["compose"],
138
138
  requires: ["form"],
139
139
  intent: { level: "partial", note: "声明式支持 onlinejs.compose 的 source/replace/publish。" },
140
- notes: "仅表单在线 JS(BizSheet 子能力);后端的在线开发(/api/api/programming Java 源码)用二开扩展包,不在本 CLI 范围。",
140
+ notes: "仅表单在线 JS(BizSheet 子能力)。后端的在线开发(/api/api/programming Java 源码)是独立 programming 能力面,两者不互换。",
141
+ },
142
+ {
143
+ id: "programming",
144
+ title: "后端在线开发",
145
+ lane: LANES.programming,
146
+ description: "设计时在线开发(/api/api/programming):业务规则 Java Handler 源码的读取/模板生成、编译检查、草稿保存与发布(发布即把规则切编码模式并生效)。",
147
+ operations: ["schemas", "sources", "get", "compile", "save", "publish"],
148
+ requires: ["businessrule"],
149
+ intent: { level: "none", note: "未覆盖;用 omc call programming.*(源码内容与包路径/类名校验由工具 preflight 承担)。" },
150
+ notes: "与表单 Online JS 严格分离(历史事故:旧 onlinejs.draft/publish 错包本面被移除)。publish 内部强制 compile 并把规则切 DevelopmentMode.CODING+enable;运行时只执行 published 最新版,草稿保存不生效。published 内容无 wire 读取端点,回滚靠本地 git。IDE 专用端点(startDebugging/debug/complete/go_definition)不包裹。",
141
151
  },
142
152
  {
143
153
  id: "bizservice",
@@ -182,6 +192,7 @@ const CAPABILITY_ALIASES = Object.freeze({
182
192
  "rule-lifecycle": ["rule-lifecycles", "S25-rule-lifecycle"],
183
193
  workflow: ["workflows", "W7-S29-greenfield"],
184
194
  onlinejs: ["online-js"],
195
+ programming: ["backend-online-coding", "programming-online-dev", "在线开发", "在线编码"],
185
196
  bizservice: ["bizservices", "B2-registration"],
186
197
  report: ["reports", "R1-report"],
187
198
  data: ["runtime-data", "M2-runtime-data"],
@@ -229,6 +240,7 @@ export const CAPABILITY_CUES = Object.freeze({
229
240
  "rule-lifecycle": ["启用规则", "停用规则", "删除规则", "规则启停", "触发规则", "执行规则", "跑规则", "按钮规则", "规则验收"],
230
241
  workflow: ["流程", "工作流", "审批", "节点", "待办", "流转", "参与人", "审批人", "角色", "部门负责人", "审批角色"],
231
242
  onlinejs: ["在线js", "脚本", "联动", "onload", "onvalidate"],
243
+ programming: ["在线开发", "在线编码", "后端源码", "java源码", "编码模式", "handler", "programming", "源码编译"],
232
244
  bizservice: ["集成", "接口", "服务", "回调", "对接", "外部系统", "datasql", "restful"],
233
245
  report: ["报表", "图表", "看板", "bi", "dashboard"],
234
246
  // r18 (2026-10-26): "造数据 / 造测试单据 / 录数据" is the RUN-DATA capability, but
@@ -266,8 +278,12 @@ export const BOUNDARY_CUES = Object.freeze({
266
278
  },
267
279
  "inline-coding": {
268
280
  status: "unsupported",
269
- cues: ["在线开发", "在线编码", "内联代码", "内联编码", "execute_code", "online_develop", "java源码", "后端源码", "二开编码"],
270
- note: "在线开发(/api/api/programming Java 源码)/ EXECUTE_CODE / ONLINE_DEVELOP 不在 CLI 内(boundary-unsupported、unsupported);用二开扩展包",
281
+ // programming lane now owns 在线开发/在线编码/Java 源码原文 (business rules).
282
+ // The remaining boundary is the graphical rule-graph's EXECUTE_CODE /
283
+ // ONLINE_DEVELOP NODE kinds and any non-business-rule source family
284
+ // (SPI/SERVICE, not opened in 8.6.26) — those are not configuration.
285
+ cues: ["内联代码", "内联编码", "execute_code", "online_develop", "二开编码"],
286
+ note: "规则图里的 EXECUTE_CODE / ONLINE_DEVELOP 节点与 SPI/SERVICE 源码面不在 CLI 内;业务规则的 Java 源码(在线开发)用 programming.*(programming.get 模板 → save → compile → publish)",
271
287
  },
272
288
  "nested-child-table": {
273
289
  status: "blocked",
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { BATCH_MAX_ENTRIES, runBatchCall } from "./batch-call.js";
15
15
  import { cmdModelScaffold } from "./model-scaffold.js";
16
16
  import { cmdFieldAdd } from "./field-add.js";
17
17
  import { cmdModelPurge } from "./model-purge.js";
18
+ import { cmdProgrammingAdopt, cmdProgrammingApply, cmdProgrammingPull, cmdProgrammingStatus, cmdProgrammingTest } from "./programming-workflow.js";
18
19
  import { environmentWritable, requiresProductionConfirmation } from "./environment-policy.js";
19
20
  import { CLI_EXIT_CODES, classifyResult, envelope, errorEnvelope, installCodePageSafeStderr, refusalText, requestId } from "./cli-contract.js";
20
21
  import { configureOutput, humanSummary, stripOutputFlags, writeEnvelope } from "./cli-output.js";
@@ -1925,6 +1926,25 @@ async function main() {
1925
1926
  intentCommand.command("readback <file>").option("--env <environment>").option("--max-output-bytes <bytes>").action(async (file, options) => {
1926
1927
  commandExitCode = await withTransport(options, request, (transport) => cmdIntentReadback(file, options.env, transport));
1927
1928
  });
1929
+ // Design-time 在线开发 working copy (file-based, git-friendly): a CLI
1930
+ // management face, not call tools — the governed writes go through the
1931
+ // registry tools (programming.save/publish) so the write gate still applies.
1932
+ const programmingCommand = command.command("programming").description("后端在线开发(/api/api/programming)本地工作区:pull 拉取源码树+manifest / status 本地改动与远端漂移 / apply 按文件保存草稿(--publish 发布)/ adopt 图形化规则→编码模板 / test fixture 驱动的自动验证(compile→publish→触发→断言→清理)");
1933
+ programmingCommand.command("pull").option("--env <environment>").option("--environment <environment>").option("--app <appCode>", "应用编码;省略时用环境配置的 applicationCode").option("--max-output-bytes <bytes>").addHelpText("after", "\n只读远端 + 本地建树(.omc-programming/sources/<schemaCode>/<ruleCode>.java + manifest.json,带环境戳)。不写环境。\n").action(async (options) => {
1934
+ commandExitCode = await withTransport(options, request, async (transport) => emitResult(await cmdProgrammingPull({ environment: options.environment ?? options.env, appCode: options.app }), transport));
1935
+ });
1936
+ programmingCommand.command("status").option("--env <environment>").option("--environment <environment>").option("--deep", "再比远端草稿内容哈希(漂移检测)").option("--app <appCode>").option("--max-output-bytes <bytes>").action(async (options) => {
1937
+ commandExitCode = await withTransport(options, request, async (transport) => emitResult(await cmdProgrammingStatus({ environment: options.environment ?? options.env, deep: options.deep === true, appCode: options.app }), transport));
1938
+ });
1939
+ programmingCommand.command("apply").option("--env <environment>").option("--environment <environment>").option("--file <file...>", "只应用指定文件(省略时=manifest 里 modified+untracked)").option("--publish", "保存后发布(publish 内部编译并把规则切编码模式)").option("--dry-run", "零写入预览(模板校验 + 将写入的内容哈希)").option("--max-output-bytes <bytes>").addHelpText("after", "\n逐文件走编程面治理:内容哈希漂移门(平台无乐观锁)→ programming.save(必要时 programming.publish)。写权限/生产确认/写锁/审计 artifact 与 omc call 相同。\n").action(async (options) => {
1940
+ commandExitCode = await withTransport(options, request, async (transport) => emitResult(await cmdProgrammingApply({ environment: options.environment ?? options.env, files: options.file, publish: options.publish === true, dryRun: options.dryRun === true }), transport));
1941
+ });
1942
+ programmingCommand.command("adopt").requiredOption("--schema <code>").requiredOption("--rule <code>").option("--env <environment>").option("--environment <environment>").option("--max-output-bytes <bytes>").addHelpText("after", "\n图形化规则 → 编码模式:读平台模板入工作区(规则必须已存在)。真正切换模式发生在 apply --publish(publish 内嵌 CODING+enable+发布规则)。\n").action(async (options) => {
1943
+ commandExitCode = await withTransport(options, request, async (transport) => emitResult(await cmdProgrammingAdopt({ environment: options.environment ?? options.env, schemaCode: options.schema, ruleCode: options.rule }), transport));
1944
+ });
1945
+ programmingCommand.command("test").option("--env <environment>").option("--environment <environment>").option("--schema <code>").option("--rule <code>").option("--file <path>").option("--fixture <path>", "fixture 路径(默认 .omc-programming/tests/<schema>/<rule>.yaml)").option("--force-publish", "即使内容哈希未变也重发").option("--max-output-bytes <bytes>").addHelpText("after", "\n全自动验证:compile 门禁 → publish(内容未变可跳过)→ 按 fixture.trigger 触发(create/update/delete/load 用 data.*,custom 用 rule.run)→ fixture.asserts 用 data.list 断言 → fixture.cleanup 只删自己种的行。编码规则内联执行:触发响应即成败(失败带 Java errmsg)。\n").action(async (options) => {
1946
+ commandExitCode = await withTransport(options, request, async (transport) => emitResult(await cmdProgrammingTest({ environment: options.environment ?? options.env, schemaCode: options.schema, ruleCode: options.rule, file: options.file, fixture: options.fixture, forcePublish: options.forcePublish === true }), transport));
1947
+ });
1928
1948
  // ADR-0008: local derived rule-graph index — CLI management face, not a call
1929
1949
  // tool (so it stays out of `commands --json`/coverage by construction).
1930
1950
  const graphCommand = command.command("graph").description("本地派生规则图索引(ADR-0008):pull 全量重建(有界并发)/ references 离线引用 / impact 影响面 / search 规则 code+name 检索(非规则体)/ status 完整性与新鲜度。候选定位器——不进 baseline/plan/写门禁");
@@ -1984,7 +2004,7 @@ main().then((code) => {
1984
2004
  // envelope (or, for `env --json`, their own JSON) and were printing a second
1985
2005
  // empty envelope after it (live 2026-10-23: `omc rollback apply` answered two
1986
2006
  // lines, violating the one-envelope stdout contract).
1987
- const ownsStdout = ["call", "script", "artifact", "commands", "describe", "contract", "capabilities", "capability", "next", "usage", "intent", "help", "form-default", "compute", "rule-when", "approval-flow", "model-scaffold", "field-add", "model-purge", "rollback", "env", "graph", "--version", "-V"];
2007
+ const ownsStdout = ["call", "script", "artifact", "commands", "describe", "contract", "capabilities", "capability", "next", "usage", "intent", "help", "form-default", "compute", "rule-when", "approval-flow", "model-scaffold", "field-add", "model-purge", "rollback", "env", "graph", "programming", "--version", "-V"];
1988
2008
  if (ownsStdout.includes(argv[2] ?? "")) {
1989
2009
  completeUsage(code);
1990
2010
  finish(code);
@@ -11,6 +11,7 @@ import { LIST_VIEW_LANE_CONTRACT } from "./lanes/list-view-contract.js";
11
11
  import { META_LANE_CONTRACT } from "./lanes/meta-contract.js";
12
12
  import { MODEL_LANE_CONTRACT } from "./lanes/model-contract.js";
13
13
  import { ONLINE_JS_LANE_CONTRACT } from "./lanes/online-js-contract.js";
14
+ import { PROGRAMMING_LANE_CONTRACT } from "./lanes/programming-contract.js";
14
15
  import { RULE_LIFECYCLE_LANE_CONTRACT } from "./lanes/rule-lifecycle-contract.js";
15
16
  import { REPORT_LANE_CONTRACT } from "./lanes/report-contract.js";
16
17
  import { RUNTIME_DATA_LANE_CONTRACT } from "./lanes/runtime-data-contract.js";
@@ -29,6 +30,7 @@ export const LANE_CONTRACTS = Object.freeze([
29
30
  META_LANE_CONTRACT,
30
31
  MODEL_LANE_CONTRACT,
31
32
  ONLINE_JS_LANE_CONTRACT,
33
+ PROGRAMMING_LANE_CONTRACT,
32
34
  RULE_LIFECYCLE_LANE_CONTRACT,
33
35
  REPORT_LANE_CONTRACT,
34
36
  RUNTIME_DATA_LANE_CONTRACT,
@@ -10,6 +10,7 @@ import { LIST_VIEW_COVERAGE } from "./lanes/list-view-coverage.js";
10
10
  import { FOLDER_COVERAGE } from "./lanes/folder-coverage.js";
11
11
  import { MODEL_COVERAGE } from "./lanes/model-coverage.js";
12
12
  import { ONLINE_JS_COVERAGE } from "./lanes/online-js-coverage.js";
13
+ import { PROGRAMMING_COVERAGE } from "./lanes/programming-coverage.js";
13
14
  import { RULE_LIFECYCLE_COVERAGE } from "./lanes/rule-lifecycle-coverage.js";
14
15
  import { RUNTIME_DATA_COVERAGE } from "./lanes/runtime-data-coverage.js";
15
16
  import { WORKFLOW_COVERAGE } from "./lanes/workflow-coverage.js";
@@ -33,6 +34,7 @@ const DECLARATIONS = Object.freeze([
33
34
  ...RUNTIME_DATA_COVERAGE,
34
35
  ...WORKFLOW_COVERAGE,
35
36
  ...ONLINE_JS_COVERAGE,
37
+ ...PROGRAMMING_COVERAGE,
36
38
  ...B2_COVERAGE,
37
39
  ...LIST_VIEW_COVERAGE,
38
40
  ...REPORT_COVERAGE,
package/dist/endpoints.js CHANGED
@@ -153,13 +153,19 @@ export const ENDPOINTS = {
153
153
  bizServiceCheck: "/api/api/bizservice/service/check",
154
154
  bizServiceImport: "/api/api/bizservice/service/import",
155
155
  runtimeQueryUploadFile: "/api/api/runtime/query/upload_file",
156
- // `/api/api/programming/*` (design-time 在线开发: business-rule / SPI /
157
- // service-component JAVA source) is deliberately NOT wrapped. It is backend
158
- // Java authored through 二开扩展包, not form Online JS, and no operator asked
159
- // for it here — so the CLI exposes no tool for it. Live 2026-09-21 evidence
160
- // (ProgrammingController.java:99, base is the doubled `/api/api/programming`
161
- // under the `/api` context-path; the earlier single-`/api` form 404s) is
162
- // recorded in the online-js contract's history note.
156
+ // Design-time 在线开发 (backend JAVA source: business-rule handler / SPI /
157
+ // service component), authored through the `programming` lane. Base is the
158
+ // DOUBLED `/api/api/programming` under the `/api` context-path
159
+ // (ProgrammingController.java, 8.6.26 source; the earlier single-`/api` form
160
+ // 404s). `getSourceCode` is @Deprecated — the modern read/template face is
161
+ // `get_source` (SourceQueryModel, codeType is the NUMERIC metadata.app.CodeType
162
+ // index). See `.scratch/backend-online-coding/recon.md`.
163
+ programmingListSchemas: "/api/api/programming/list_schemas_by_appcode",
164
+ programmingListSources: "/api/api/programming/list_source_by_schemacode",
165
+ programmingGetSource: "/api/api/programming/get_source",
166
+ programmingSaveSource: "/api/api/programming/saveSourceCode",
167
+ programmingCompile: "/api/api/programming/compile",
168
+ programmingPublish: "/api/api/programming/publish",
163
169
  workflowCreate: "/api/api/workflow/create",
164
170
  workflowGetHeader: "/api/api/workflow/get_header",
165
171
  workflowGetDraft: "/api/api/workflow/get_draft",
@@ -24,6 +24,7 @@ export const LANES = Object.freeze({
24
24
  runtimeData: "M2-runtime-data",
25
25
  workflow: "W7-S29-greenfield",
26
26
  onlineJs: "online-js",
27
+ programming: "programming-online-dev",
27
28
  b2: "B2-registration",
28
29
  listView: "W1-list-view",
29
30
  report: "R1-report",
@@ -12,7 +12,7 @@ export const ONLINE_JS_WIRE = Object.freeze({
12
12
  composePreserve: `reads the existing draftHtmlJson, JSON-parses the COMPLETE element array, preserves the platform template including the ${PLATFORM_LIFECYCLE_WRAPPER_MARKER} lifecycle body — the style, toolbar, template, beforeCustomScript, and afterCustomScript elements verbatim (attrs included), plus every other layout/control element — fills in missing skeleton elements with platform defaults, replaces ONLY the customScript element, then stringifies the COMPLETE array`,
13
13
  composePublish: "bizsheet/update then bizsheet/publish (force:true) promoting the full layout; publishedHtmlJson is read back verbatim and SHA-256-fingerprinted for byte-equality verification",
14
14
  regression: "issue 11: never overwrite the layout with a standalone script object or a partial [customScript]-only array",
15
- programmingRemoved: "the former onlinejs.draft/publish tools wrapped the design-time 在线开发 face (POST /api/api/programming/get_source|saveSourceCode|publish; base is the DOUBLED /api/api/programming under the /api context-path, live 2026-09-21; the earlier single-/api guess 404s). That face authors backend JAVA (business-rule/SPI/service-component source), which is written through 二开扩展包, NOT form Online JS — so the CLI exposes no tool for it and the two misleadingly-named tools were removed. onlinejs.compose is the only Online JS tool",
15
+ programmingRemoved: "the former onlinejs.draft/publish tools wrapped the design-time 在线开发 face (POST /api/api/programming/get_source|saveSourceCode|publish; base is the DOUBLED /api/api/programming under the /api context-path, live 2026-09-21; the earlier single-/api guess 404s). That face authors backend JAVA (business-rule handler source), NOT form Online JS — the two misleadingly-named tools were removed. The face is now owned by the INDEPENDENT programming lane (programming.*: get/save/compile/publish), deliberately separate from onlinejs; onlinejs.compose stays the only Online JS tool",
16
16
  });
17
17
  const ONLINE_JS_SCOPE = "draft promotion publish verbatim readback";
18
18
  export const ONLINE_JS_LANE_CONTRACT = Object.freeze({
@@ -63,7 +63,7 @@ export const ONLINE_JS_LANE_CONTRACT = Object.freeze({
63
63
  behaviorBinding("test/issue-11-online-js.test.ts#onlinejs.compose refuses to overwrite the layout with a standalone script object", assertOnlineJsContract),
64
64
  ]),
65
65
  programmingRemoved: Object.freeze([
66
- behaviorBinding("test/onlinejs-no-programming-tool.test.ts#the removed online-development tools are absent from the registry", assertOnlineJsContract),
66
+ behaviorBinding("test/onlinejs-no-programming-tool.test.ts#the old onlinejs draft/publish names stay gone; programming tools are their own lane", assertOnlineJsContract),
67
67
  ]),
68
68
  }),
69
69
  evidence: Object.freeze([
@@ -90,8 +90,8 @@ export function assertOnlineJsContract() {
90
90
  throw new Error("online-js contract must pin the publish chain");
91
91
  if (!ONLINE_JS_WIRE.regression.includes("partial [customScript]-only"))
92
92
  throw new Error("online-js contract must pin the issue-11 regression shape");
93
- if (!ONLINE_JS_WIRE.programmingRemoved.includes("二开扩展包"))
94
- throw new Error("online-js contract must record why the programming tools are absent");
93
+ if (!ONLINE_JS_WIRE.programmingRemoved.includes("二开扩展包") && !ONLINE_JS_WIRE.programmingRemoved.includes("INDEPENDENT programming lane"))
94
+ throw new Error("online-js contract must record where the programming face went");
95
95
  if (!PLATFORM_DEFAULT_LIFECYCLE_SCRIPT.includes(PLATFORM_LIFECYCLE_WRAPPER_MARKER))
96
96
  throw new Error("online-js emitter must contain the platform lifecycle wrapper");
97
97
  if (!ONLINE_JS_WIRE.composeElement.includes(PLATFORM_LIFECYCLE_WRAPPER_MARKER))
@@ -0,0 +1,113 @@
1
+ import { assertDescriptionCarries, assertLaneContract, behaviorBinding, CONTRACT_FAMILY, CONTRACT_REFERENCE_VERSION } from "./contract-support.js";
2
+ import { LANES } from "./lane-ids.js";
3
+ /**
4
+ * Programming lane wire facts — the design-time 在线开发 face
5
+ * (`/api/api/programming/*`, doubled base under the `/api` context-path),
6
+ * authoritative against the CloudPivot **8.6.26 decompiled source**
7
+ * (`launcher-8.6.26/cloudpivot-engine-8.6.26/.../controller/programming/ProgrammingController.java`)
8
+ * and cross-checked against the live AI-Platform-v1.1.25 deployment (identical
9
+ * 11-method surface, identical paths). Full evidence:
10
+ * `.scratch/backend-online-coding/recon.md`.
11
+ */
12
+ export const PROGRAMMING_WIRE = Object.freeze({
13
+ surface: "设计时::在线开发 面(类级 /api/programming → 实际 doubled /api/api/programming/*):编写后端 JAVA 源码(业务规则 Handler),不是表单 Online JS。读取/模板面用 POST get_source(SourceQueryModel);getSourceCode 已弃用。编译 POST compile,保存草稿 POST saveSourceCode,发布 POST publish。",
14
+ codeType: "codeType 是 metadata.app.CodeType 的**数字索引**(@JsonValue getIndex):BUSINESS_RULE=0、SPI=1、SERVICE=2;客户端传字符串名会被平台以「accepted [0,1,2]」拒绝(live 2026-11-03 实证)。8.6.26 的 get_source 仅支持 BUSINESS_RULE,其它类型回 SYS_PARAMETER_ERROR。",
15
+ sourceIdentity: "源码主键 code = schemaCode + ruleCode + \"Handler\"(SourceUtil.getBusinessRuleSourceCode)。未保存草稿时 get_source 仍返回内存骨架:按规则类型选模板(Create/Update/Delete/Load/Cancel/Available/GetList → <ruleCode>HandlerTemplate,其余 → CustomizedHandlerTemplate),version=null 即未落库草稿。",
16
+ storage: "单表 h_source_code:草稿行固定 version=0(published=false),保存是**无条件覆盖 content**,平台无乐观锁/CAS(originalVersion 无比较逻辑);publish 另插 published=true、version=maxPublished+1 的新行,草稿行不动。运行时正式执行读 published 最新版(BusinessLauncher.buildReleaseRequest → getLatestPublishedSourceByCode),调试读草稿(version=0)。publish 内容没有 wire 读取端点。",
17
+ publishFlow: "publish 服务端内部:saveDraftSource → 强制 compile(失败 → PROGRAMMING_COMPILE_FAILED)→ facade.publish 插新版本行 → 若 codeType=BUSINESS_RULE 则把规则置 DevelopmentMode.CODING(=1)、enabled=true 并发布规则(ProgrammingController.publishBusinessRule)。因此 publish 载荷**必须携带 businessRule 整模型**(工具内用 businessrule/list_by_schemacode 按 code 补齐)。",
18
+ compile: "POST /compile body {code, content};服务端 convertSourceCode → 编辑器租户缓存 updateSourceCode(内存副作用)→ compileFile → CompileFileResult{success, errors[]}。编译是规则可运行的最低门槛:运行时每次执行都重新编译,坏源码会让该规则的每次业务执行失败。",
19
+ templateGuard: "平台预置代码模板,包路径/类名/继承关系不得修改(改了规则无法运行)。工具 preflight:content 若声明 class 名必须等于 code 主键;若声明 package 必须是平台模板包——8.6.26 为 com.authine.cloudpivot.engine.service.bizrule.handler,live 8.7(AI-Platform 1.1.25) 为 com.authine.cloudpivot.engine.sourcecode.handler(真机冒烟实证),两者都接受,其它拒绝。",
20
+ boundary: "startDebugging/debug/complete/go_definition 是 WEB-IDE 专用(调试为 attach 式:DebugVo 无合成载荷,debugId 只是随机 UUID,断点靠真实业务事件触发),不包裹。SPI/SERVICE 两类源码的写入面在 8.6.26 未开放(get_source 直接拒绝非 BUSINESS_RULE),v1 只做业务规则。",
21
+ authority: "权限:ProgrammingController 无 @RequireAdmin / isAdmin 判断(AdminPermissionInterceptor 只看类级注解),普通已登录用户即可调用;CLI 标准会话无需额外管理员前置。",
22
+ });
23
+ const PROGRAMMING_SCOPE = "programming backend java source business rule handler read template compile save draft publish";
24
+ export const PROGRAMMING_LANE_CONTRACT = Object.freeze({
25
+ lane: LANES.programming,
26
+ family: CONTRACT_FAMILY,
27
+ referenceVersion: CONTRACT_REFERENCE_VERSION,
28
+ endpoints: Object.freeze({
29
+ listSchemas: "programmingListSchemas",
30
+ listSources: "programmingListSources",
31
+ getSource: "programmingGetSource",
32
+ compile: "programmingCompile",
33
+ save: "programmingSaveSource",
34
+ publish: "programmingPublish",
35
+ }),
36
+ wire: Object.freeze({
37
+ surface: PROGRAMMING_WIRE.surface,
38
+ codeType: PROGRAMMING_WIRE.codeType,
39
+ sourceIdentity: PROGRAMMING_WIRE.sourceIdentity,
40
+ storage: PROGRAMMING_WIRE.storage,
41
+ publishFlow: PROGRAMMING_WIRE.publishFlow,
42
+ compile: PROGRAMMING_WIRE.compile,
43
+ templateGuard: PROGRAMMING_WIRE.templateGuard,
44
+ boundary: PROGRAMMING_WIRE.boundary,
45
+ authority: PROGRAMMING_WIRE.authority,
46
+ }),
47
+ wireBindings: Object.freeze({
48
+ surface: Object.freeze([behaviorBinding("test/programming.test.ts#programming lane pins the 8.6.26 online-development face", assertProgrammingContract)]),
49
+ codeType: Object.freeze([behaviorBinding("test/programming.test.ts#programming lane pins the 8.6.26 online-development face", assertProgrammingContract)]),
50
+ sourceIdentity: Object.freeze([behaviorBinding("test/programming.test.ts#programming.get derives the source code from schema+rule and returns the template skeleton", assertProgrammingContract)]),
51
+ storage: Object.freeze([behaviorBinding("test/programming.test.ts#programming.save writes the draft and publish inserts a new published version", assertProgrammingContract)]),
52
+ publishFlow: Object.freeze([behaviorBinding("test/programming.test.ts#programming.publish compiles, publishes and flips the business rule to coding mode", assertProgrammingContract)]),
53
+ compile: Object.freeze([behaviorBinding("test/programming.test.ts#programming.publish refuses when compilation fails", assertProgrammingContract)]),
54
+ templateGuard: Object.freeze([behaviorBinding("test/programming.test.ts#programming.save refuses a source whose package or class name deviates from the template", assertProgrammingContract)]),
55
+ boundary: Object.freeze([behaviorBinding("test/programming.test.ts#programming lane pins the 8.6.26 online-development face", assertProgrammingContract)]),
56
+ authority: Object.freeze([behaviorBinding("test/programming.test.ts#programming lane pins the 8.6.26 online-development face", assertProgrammingContract)]),
57
+ }),
58
+ evidence: Object.freeze([
59
+ ".scratch/backend-online-coding/recon.md",
60
+ ".scratch/backend-online-coding/spec.md",
61
+ "packages/omc-cli/src/lanes/programming.ts",
62
+ "packages/omc-cli/test/programming.test.ts",
63
+ ]),
64
+ tools: Object.freeze({
65
+ "programming.schemas": Object.freeze({
66
+ scope: PROGRAMMING_SCOPE,
67
+ description: "读取应用下**已有在线源码**的模型清单(GET list_schemas_by_appcode)。注意平台语义:只列 `listSourceCodes` 非空的模型,未编码规则的发现走 rule.list。返回 [{code,name}]。只读。",
68
+ }),
69
+ "programming.sources": Object.freeze({
70
+ scope: PROGRAMMING_SCOPE,
71
+ description: "读取模型下的在线源码清单(GET list_source_by_schemacode,含 businessRule 整模型与草稿 content/version)。平台语义:任一规则缺草稿会整列报错(PROGRAMMING_SOURCE_NONEXISTENT),工具捕获后回 present:false + 提示。只读。",
72
+ }),
73
+ "programming.get": Object.freeze({
74
+ scope: PROGRAMMING_SCOPE,
75
+ description: "读取单条源码(POST get_source):给 schemaCode+businessRuleCode(code 省略时按 `schemaCode+ruleCode+\"Handler\"` 推导),未保存草稿时平台按规则类型模板生成骨架(provisioned:false)。返回 content/className/codeType/version + sha256。只读。",
76
+ }),
77
+ "programming.compile": Object.freeze({
78
+ scope: PROGRAMMING_SCOPE,
79
+ description: "远程编译检查(POST compile,body {code,content}):返回 CompileFileResult{success, errors}。编译是规则可运行的最低门槛(运行时每次执行都重新编译)。只读(唯一副作用是平台编辑器租户内存缓存)。",
80
+ }),
81
+ "programming.save": Object.freeze({
82
+ scope: PROGRAMMING_SCOPE,
83
+ description: "保存源码草稿(POST saveSourceCode):content 写入 version=0 草稿行(**无条件覆盖,平台无乐观锁**)。preflight:class 名必须等于 code(`schemaCode+ruleCode+\"Handler\"`);声明的 package 必须是平台模板包(8.6.26 `com.authine.cloudpivot.engine.service.bizrule.handler` / live 8.7 `com.authine.cloudpivot.engine.sourcecode.handler`)——改模板即规则无法运行。保存后读回判等。草稿不影响运行时,生效需 programming.publish。",
84
+ }),
85
+ "programming.publish": Object.freeze({
86
+ scope: PROGRAMMING_SCOPE,
87
+ description: "发布源码(POST publish):内部 saveDraftSource→**强制 compile**(失败回 PROGRAMMING_COMPILE_FAILED)→插 published 新版本行→BUSINESS_RULE 规则自动置 DevelopmentMode.CODING+enabled+发布。载荷必须带 businessRule 整模型(工具内按 ruleCode 从 businessrule/list_by_schemacode 补齐;规则不存在则拒绝)。发布后运行时才执行该源码。content 省略时用远端草稿。",
88
+ }),
89
+ }),
90
+ });
91
+ export function assertProgrammingContract() {
92
+ assertLaneContract(PROGRAMMING_LANE_CONTRACT);
93
+ if (!PROGRAMMING_WIRE.surface.includes("/api/api/programming"))
94
+ throw new Error("programming contract must pin the doubled base");
95
+ if (!PROGRAMMING_WIRE.codeType.includes("BUSINESS_RULE=0"))
96
+ throw new Error("programming contract must pin the numeric CodeType index");
97
+ if (!PROGRAMMING_WIRE.sourceIdentity.includes("Handler"))
98
+ throw new Error("programming contract must pin the source code identity");
99
+ if (!PROGRAMMING_WIRE.storage.includes("h_source_code"))
100
+ throw new Error("programming contract must pin the storage table");
101
+ if (!PROGRAMMING_WIRE.publishFlow.includes("businessRule"))
102
+ throw new Error("programming contract must pin the publish payload rule model");
103
+ if (!PROGRAMMING_WIRE.templateGuard.includes("com.authine.cloudpivot.engine.service.bizrule.handler"))
104
+ throw new Error("programming contract must pin the 8.6 template package");
105
+ if (!PROGRAMMING_WIRE.templateGuard.includes("com.authine.cloudpivot.engine.sourcecode.handler"))
106
+ throw new Error("programming contract must pin the live 8.7 template package");
107
+ assertDescriptionCarries(PROGRAMMING_LANE_CONTRACT, "programming.get", ["get_source", "Handler", "模板"]);
108
+ assertDescriptionCarries(PROGRAMMING_LANE_CONTRACT, "programming.save", ["saveSourceCode", "com.authine.cloudpivot.engine.service.bizrule.handler", "sourcecode.handler", "Handler"]);
109
+ assertDescriptionCarries(PROGRAMMING_LANE_CONTRACT, "programming.publish", ["compile", "businessRule", "CODING"]);
110
+ assertDescriptionCarries(PROGRAMMING_LANE_CONTRACT, "programming.schemas", ["list_schemas_by_appcode"]);
111
+ assertDescriptionCarries(PROGRAMMING_LANE_CONTRACT, "programming.sources", ["list_source_by_schemacode"]);
112
+ }
113
+ //# sourceMappingURL=programming-contract.js.map
@@ -0,0 +1,18 @@
1
+ import { claim, declareLaneCoverage } from "./coverage-declaration.js";
2
+ import { PROGRAMMING_LANE_CONTRACT } from "./programming-contract.js";
3
+ const T = PROGRAMMING_LANE_CONTRACT.tools;
4
+ export const PROGRAMMING_COVERAGE = declareLaneCoverage(PROGRAMMING_LANE_CONTRACT.lane, [
5
+ {
6
+ capability: "P1 programming online development",
7
+ evidence: ["packages/omc-cli/src/lanes/programming.ts", "packages/omc-cli/test/programming.test.ts", ".scratch/backend-online-coding/recon.md"],
8
+ tools: [
9
+ claim("programming.schemas", T["programming.schemas"].scope),
10
+ claim("programming.sources", T["programming.sources"].scope),
11
+ claim("programming.get", T["programming.get"].scope),
12
+ claim("programming.compile", T["programming.compile"].scope),
13
+ claim("programming.save", T["programming.save"].scope),
14
+ claim("programming.publish", T["programming.publish"].scope),
15
+ ],
16
+ },
17
+ ]);
18
+ //# sourceMappingURL=programming-coverage.js.map