oasis_test 0.1.61 → 0.1.63

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 (2) hide show
  1. package/dist/index.js +815 -304
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,6 +34,197 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  mod
35
35
  ));
36
36
 
37
+ // ../core/src/content-type-handler.ts
38
+ function deliverableSubmit(ctx) {
39
+ const id = ctx.artifactId;
40
+ if (ctx.action === "integrate") {
41
+ return `- \`oasis integrate ${id} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u4F60\u5728 deliverable/ \u5408\u6210\u7684\u6574\u5408\u7248\u843D\u4E3A\u65B0 head\uFF08\u7CFB\u7EDF\u5185\u90E8\u4F1A\u628A\u961F\u5217\u91CC\u5176\u4F59\u5F85\u843D\u7248\u672C\u4E00\u5E76\u5E76\u8FDB\u6765\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose / advance \u4EA4\u96C6\u6210**\uFF08\u90A3\u4F1A\u628A\u96C6\u6210\u5F53\u6210\u53C8\u4E00\u4E2A\u5E76\u5217\u7248\u672C\u3001\u843D\u9519\u7248\u672C\uFF09\u3002`;
42
+ }
43
+ if (ctx.isPart) {
44
+ return `- \`oasis propose ${id} --part <\u4F60\u7684\u90E8\u4EF6> --file <\u6587\u4EF6> --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u4EA7\u51FA\u3002\`--part\` \u5FC5\u987B\u662F\u5206\u914D\u7ED9\u4F60\u7684\u90A3\u4E2A\u90E8\u4EF6\u540D\uFF1B\`--file\` \u6307\u5411\u4F60\u5199\u597D\u7684\u5185\u5BB9\u6587\u4EF6\uFF08\u90E8\u4EF6\u662F\u72EC\u7ACB\u788E\u7247\uFF0C\u5199\u4F60\u81EA\u5DF1\u7684\uFF09\uFF1B\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u505A\u4E86\u5565\u3002\u4EA7\u5B8C\u5373\u53EF\u6536\u5DE5\uFF0C\u96C6\u6210\u7531 owner \u505A\u3002`;
45
+ }
46
+ return `- \`oasis propose ${id} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4EA7\u7269\u7684\u65B0\u4E00\u7248\u3002\`--from-dir\` \u6307\u5411\u4F60\u7F16\u8F91\u540E\u7684 deliverable/\uFF08\u7CFB\u7EDF\u636E\u76EE\u5F55\u7B97\u8FD9\u7248\u3001\u5E76\u548C\u4E0A\u4E00\u7248 diff\uFF09\uFF1B\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u6539\u4E86\u5565\uFF08\u7ED9\u8BC4\u5BA1/\u540E\u4EBA\u770B\uFF0C\u4E0D\u7528\u50CF git commit \u90A3\u4E48\u7EC6\uFF09\u3002`;
47
+ }
48
+ function handlerFor(repos, contentType) {
49
+ if (repos?.some((r) => r.remote)) return codeHandler;
50
+ if (contentType === "media") return mediaHandler;
51
+ return textHandler;
52
+ }
53
+ function handlerForType(contentType) {
54
+ if (contentType === void 0) return void 0;
55
+ if (contentType === "code") return codeHandler;
56
+ if (contentType === "media") return mediaHandler;
57
+ return textHandler;
58
+ }
59
+ var slug, textHandler, codeHandler, mediaHandler;
60
+ var init_content_type_handler = __esm({
61
+ "../core/src/content-type-handler.ts"() {
62
+ "use strict";
63
+ slug = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
64
+ textHandler = {
65
+ produceInstructions(ctx) {
66
+ const { artifactId } = ctx;
67
+ if (ctx.part !== void 0) {
68
+ return [
69
+ `\u628A\u4F60\u8FD9\u4E2A\u90E8\u4EF6 \`${ctx.part}\` \u7684\u5B8C\u6574\u5185\u5BB9\u5199\u8FDB\u672C\u5730\u6587\u4EF6\uFF0C\u7136\u540E**\u53EA\u6267\u884C\u8FD9\u4E00\u6B65**\uFF1A`,
70
+ `\`oasis propose ${artifactId} --file <\u6587\u4EF6> --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\`\u3002`,
71
+ `**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D\u8981 advance\u3001\u4E0D\u8981 conclude**\u3002\u7B49\u6240\u6709\u5144\u5F1F\u90E8\u4EF6\u90FD\u4EA7\u51FA\u540E\uFF0C\u7CFB\u7EDF\u4F1A\u628A\u5404\u90E8\u4EF6\u96C6\u6210\u6210\u4E00\u7248\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`,
72
+ ``,
73
+ `> \u26A0 \u5927\u6587\u4EF6\u52A1\u5FC5**\u589E\u91CF\u5199**\uFF08Write \u9AA8\u67B6 \u2192 Edit \u5206\u6B21\u8FFD\u52A0\uFF09\uFF0C\u522B\u4E00\u6B21\u6027\u5410\u6574\u4E2A\u5927\u6587\u4EF6\uFF08\u6709\u8F93\u51FA token \u4E0A\u9650\uFF09\u3002`
74
+ ];
75
+ }
76
+ if (ctx.unionInto !== void 0) {
77
+ return [
78
+ `\u4F60\u4EA7\u51FA\u7684\u662F**\u6574\u4F53\u4EA4\u4ED8\u7269\u7684\u4E00\u4E2A\u6A21\u5757**\uFF0C\u4F1A\u88AB\u673A\u68B0\u5E76\u5165 \`${ctx.unionInto}\`\uFF08\u6309\u6587\u4EF6\u540D\u5408\u5E76\uFF09\u3002`,
79
+ `\u628A\u4F60\u8D1F\u8D23\u7684\u6587\u4EF6\u5199\u8FDB\u4E00\u4E2A**\u76EE\u5F55**\uFF08\u6587\u4EF6\u540D\u5373\u5B83\u5728\u6700\u7EC8\u4EA4\u4ED8\u7269\u91CC\u7684\u8DEF\u5F84\uFF0C\u5982 \`combat.js\`\uFF09\uFF0C\u7136\u540E\uFF1A`,
80
+ `\`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\` \u2192 \`oasis conclude ${artifactId}\`\u3002`,
81
+ `**\u53EA\u5199\u4F60\u8FD9\u4E2A\u6A21\u5757\u7684\u6587\u4EF6\uFF0C\u4E0D\u8981\u78B0\u522B\u7684\u6A21\u5757\u7684\u6587\u4EF6**\uFF08\u540C\u540D\u4F1A\u8986\u76D6\u522B\u4EBA\u7684\uFF09\u3002\u4E25\u683C\u9075\u5B88\u4E0A\u6E38 tech-design \u7EA6\u5B9A\u7684\u63A5\u53E3/\u5168\u5C40\u547D\u540D\uFF0C\u6A21\u5757\u95F4\u624D\u80FD\u62FC\u5F97\u8D77\u6765\u3002`,
82
+ `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
83
+ ];
84
+ }
85
+ return [
86
+ ctx.hasHead ? `\u4F60\u7684\u4EA4\u4ED8\u7269\u5728 \`./deliverable/\` \u2014\u2014 \u91CC\u9762\u5DF2\u662F**\u5F53\u524D\u7248\u672C\u7684\u5B8C\u6574\u5185\u5BB9**\uFF0C\u76F4\u63A5\u5728\u5B83\u4E0A\u9762\u589E\u5220\u6539\u3002` : `\u4F60\u7684\u4EA4\u4ED8\u7269\u76EE\u5F55\u662F \`./deliverable/\`\uFF08\u5F53\u524D\u4E3A\u7A7A\u2014\u2014\u8FD9\u662F\u5168\u65B0\u4EA7\u7269\uFF0C\u5728\u5176\u4E2D\u4EA7\u51FA\u9996\u7248\u5185\u5BB9\uFF09\u3002`,
87
+ `- \u6700\u7EC8\u4EA7\u7269\u53EA\u653E \`deliverable/\`\uFF1B\u4E34\u65F6\u6587\u4EF6\uFF08\u8349\u7A3F/\u811A\u672C/\u5B9E\u9A8C\uFF09\u653E\u5230**\u522B\u5904**\uFF0C\u522B\u7559\u5728 \`deliverable/\` \u91CC\u3002`,
88
+ `- \u6539\u597D\u540E\u6574\u76EE\u5F55\u63D0\u4EA4\uFF1A\`oasis propose ${artifactId} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\``,
89
+ ` \u2192 \u7CFB\u7EDF\u81EA\u52A8\u843D\u5730\u4E3A\u65B0\u7248\u672C\uFF08**\u65E0\u9700 advance**\uFF09\uFF1B\u6574\u4E2A\u4EA4\u4ED8\u7269**\u771F\u6B63\u5B8C\u6574**\u4E86\u518D \`oasis conclude ${artifactId}\`\u3002`,
90
+ ...ctx.hasHead ? [`\uFF08\`deliverable/\` \u672C\u5C31\u662F head \u7684\u5B8C\u6574\u526F\u672C\u2014\u2014\u4F60\u4EA4\u7684\u4E00\u5B9A\u662F\u5B8C\u6574\u7248\uFF0C\u4E0D\u4F1A\u4E22\u5DF2\u6709\u5185\u5BB9\u3002\uFF09`] : [],
91
+ `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
92
+ ];
93
+ },
94
+ integrateInstructions(ctx) {
95
+ const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
96
+ return [
97
+ `\u5404\u90E8\u4EF6\u4ECE**\u5171\u540C\u7956\u5148 H** \u5206\u5934\u4EA7\u51FA\u3001\u5404\u6539\u5404\u5757\uFF1A\u6BCF\u6761\u5185\u5BB9\u5728 \`parts/<\u540D>/CONTENT.md\`\uFF08\u6216\u5176\u5C55\u5F00\u6587\u4EF6\uFF09\u91CC\uFF08${parts}\uFF09\uFF0CH \u7684\u5185\u5BB9\u5728 \`deliverable/\`\uFF08\u4ECE\u96F6\u5206\u89E3\u5219\u4E3A\u7A7A\uFF09\u3002`,
98
+ `\u628A\u5B83\u4EEC\u5408\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u6574\u5408\u7248**\u5C31\u5730\u5199\u8FDB \`deliverable/\`**\u2014\u2014**\u4EE5 H \u4E3A\u5E95**\u3001\u628A\u5404\u90E8\u4EF6\u6539\u52A8**\u5168\u5E76\u8FDB\u6765**\uFF08\u5355\u6587\u4EF6 \`deliverable/CONTENT.md\`\uFF0C\u591A\u6587\u4EF6\u94FA\u6210\u591A\u4E2A\u6587\u4EF6\uFF1B\u5927\u6587\u4EF6\u589E\u91CF\u5199\uFF1AWrite \u9AA8\u67B6 \u2192 Edit \u8FFD\u52A0\uFF09\uFF0C\u7136\u540E\uFF1A`,
99
+ `\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002`,
100
+ ``,
101
+ `**\u5FE0\u5B9E\u5408\u5E76\u3001\u522B\u4E22\u4EFB\u4F55\u90E8\u4EF6**\uFF1B\u63A5\u53E3/\u547D\u540D\u5BF9\u4E0D\u9F50\u5C31\u6309\u4E0A\u6E38\u89C4\u683C\u6821\u6B63\uFF0C\u8BA9 N \u4E2A\u90E8\u4EF6\u62FC\u6210\u4E00\u4E2A\u81EA\u6D3D\u6574\u4F53\u3002`
102
+ ];
103
+ },
104
+ submitInstruction: deliverableSubmit,
105
+ collectionStrategy: { kind: "inline" },
106
+ mergeMode: "oasis-queue"
107
+ };
108
+ codeHandler = {
109
+ produceInstructions(ctx) {
110
+ const { artifactId } = ctx;
111
+ const repos = ctx.repos ?? [];
112
+ const r0 = repos[0];
113
+ if (ctx.part !== void 0) {
114
+ const work = `feat/${slug(artifactId)}__${slug(ctx.part)}`;
115
+ const feat2 = `feat/${slug(artifactId)}`;
116
+ const dev = r0?.develop ?? "develop";
117
+ return [
118
+ `\u4F60\u4EA7\u51FA\u7684\u662F\u9879\u76EE **${ctx.projectName ?? "?"}** \u7684\u4EE3\u7801\u3001\u4E14\u53EA\u662F\u90E8\u4EF6 \`${ctx.part}\`\u2014\u2014\u5185\u5BB9\u6D3B\u5728 git \u91CC\u3002\u4F60**\u53EA\u7BA1\u4EA7\u51FA\u4F60\u8FD9\u5757\u3001push \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u5206\u652F**\uFF1B\`oasis propose\` \u65F6**\u7CFB\u7EDF\u4F1A\u628A\u5B83\u5E72\u51C0\u5408\u8FDB\u5DE5\u5355\u5206\u652F \`${feat2}\`**\uFF08\u4E0D\u7528\u4F60\u624B\u52A8\u5408\u3001\u4E5F\u4E0D\u7528\u7B49 owner \u5408\uFF09\u3002`,
119
+ `1. \`git clone ${r0?.remote ?? "<remote>"} repos/${r0?.id ?? "<id>"}\` \u2192 \`cd repos/${r0?.id ?? "<id>"}\` \u2192 \u4ECE\u4E3B\u5E72\u8D77\u4F60\u7684\u5DE5\u4F5C\u5206\u652F\uFF1A\`git checkout ${dev} && git checkout -b ${work}\``,
120
+ `2. \u5B9E\u73B0\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u6539\u52A8\uFF08\u4E25\u5B88\u4E0A\u6E38\u63A5\u53E3\u7EA6\u5B9A\uFF09\u3001\u81EA\u67E5\u80FD\u6784\u5EFA/\u80FD\u8DD1\u3001\`git commit\``,
121
+ `3. **\u53EA\u63A8\u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u5206\u652F**\uFF1A\`git push -u origin ${work}\`\uFF08**\u522B\u78B0 \`${feat2}\`**\u2014\u2014\u5408\u5E76\u4EA4\u7ED9\u7CFB\u7EDF\uFF09`,
122
+ `4. \u56DE\u62A5\uFF1A\`oasis propose ${artifactId} --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD>"\`\uFF08\u7CFB\u7EDF\u628A \`${work}\` \u5E72\u51C0\u5408\u8FDB \`${feat2}\` \u518D\u6536\u96C6\uFF1B**\u82E5\u4E0E \`${feat2}\` \u51B2\u7A81\u4F1A\u5F53\u573A\u6253\u56DE**\u2014\u2014\u6309\u63D0\u793A \`git fetch && git rebase origin/${feat2}\` \u89E3\u5B8C\u51B2\u7A81\u3001\`git push -f origin ${work}\` \u540E\u91CD\u65B0 propose\uFF09`,
123
+ `**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D conclude**\u3002\u5404\u90E8\u4EF6\u90FD\u5E76\u5B8C\u540E\u7531 owner \u9A8C\u6574\u4F53\u6536\u53E3\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
124
+ ];
125
+ }
126
+ const feat = `feat/${slug(artifactId)}`;
127
+ if (ctx.isFinalize) {
128
+ const r02 = repos[0];
129
+ return [
130
+ `\u4F60\u662F **${ctx.projectName ?? "?"}** \u4EE3\u7801\u4EA4\u4ED8\u7269\u7684 owner\uFF0C\u6765**\u6536\u53E3**\uFF1A\u5404\u90E8\u4EF6\u5DF2\u5404\u81EA\u5E76\u5165**\u5DE5\u5355\u5206\u652F \`${feat}\`**\uFF08\u6BCF\u4E2A part \u5E72\u5B8C\u81EA\u5DF1 merge \u8FDB\u6765\u3001\u89E3\u4E86\u5404\u81EA\u51B2\u7A81\uFF09\u3002\u4F60**\u4E0D\u7528\u518D\u9010\u6761\u5408\u5E76**\uFF0C\u53EA\u9A8C\u6574\u4F53 + \u6536\u53E3\uFF1A`,
131
+ `1. \`git clone ${r02?.remote ?? "<remote>"} repos/${r02?.id ?? "<id>"}\` \u2192 \`cd repos/${r02?.id ?? "<id>"} && git fetch && git checkout ${feat}\`\uFF08**\u662F\u5DF2\u5B58\u5728\u7684\u5DE5\u5355\u5206\u652F\u3001\u542B\u5404\u90E8\u4EF6\u5168\u90E8\u6539\u52A8**\uFF0C\u4E0D\u662F\u4ECE\u4E3B\u5E72\u65B0\u5EFA\uFF09\u3002\u591A\u4ED3\u5219\u5BF9\u6BCF\u4E2A\u78B0\u8FC7\u7684\u4ED3\u540C\u6837 checkout \`${feat}\`\u3002`,
132
+ `2. **\u9A8C\u6574\u4F53\u80FD\u6784\u5EFA / \u80FD\u8DD1**\uFF08\u8FD9\u624D\u662F\u4F60 owner \u7684\u5224\u65AD\u6D3B\uFF09\uFF1B\u6709\u6B8B\u7559\u95EE\u9898\uFF08\u63A5\u53E3\u6CA1\u5BF9\u9F50 / \u6784\u5EFA\u5931\u8D25 / \u90E8\u4EF6\u8854\u63A5\u5904\u8981\u6536\u5C3E\uFF09\u2192 \u5C31\u5730\u4FEE\u3001\`git commit\`\u3001\`git push origin ${feat}\`\u3002`,
133
+ `3. \`oasis propose ${artifactId} --reason "<\u6536\u53E3\u8BF4\u660E>"\`\uFF08\u7CFB\u7EDF\u4ECE\u5404\u4ED3 \`${feat}\` \u6536\u5168 {\u4ED3\u5E93\u2192\u63D0\u4EA4} = \u5168\u90E8\u4EF6\u6574\u5408\u6001\uFF09\u2192 \`oasis conclude ${artifactId}\`\uFF08conclude \u540E\u9010\u4ED3 gated \u5408\u8FDB\u4E3B\u5E72\uFF09\u3002`,
134
+ ``,
135
+ `**\u4F60\u8FD9\u7EA7\u662F\u628A\u5173\u6574\u4F53\u81EA\u6D3D + \u6536\u53E3\u6210\u4EA4\u4ED8\u7248\uFF0C\u4E0D\u662F\u91CD\u65B0\u5B9E\u73B0**\u2014\u2014\u5404\u90E8\u4EF6\u7684\u5B9E\u73B0\u5DF2\u5728\u5B83\u4EEC\u5404\u81EA\u6536\u5C3E\u65F6\u5E76\u8FDB \`${feat}\` \u4E86\u3002**\u522B\u7528 \`oasis integrate\`**\uFF1A\u4EE3\u7801\u6536\u53E3\u5C31\u8D70 \`propose\`\uFF08git-native\uFF09\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
136
+ ];
137
+ }
138
+ const repoLines = repos.map((r) => ` - \`${r.id}\`\uFF1A${r.remote ?? "<remote>"}\uFF08\u4E3B\u5E72 \`${r.develop}\`\uFF09`);
139
+ const short = (s2) => s2.slice(0, 12);
140
+ const versionLine = (() => {
141
+ if (!(ctx.hasHead && ctx.headExternalRef)) return [];
142
+ const ref2 = ctx.headExternalRef;
143
+ if (ref2.trim().startsWith("{")) {
144
+ try {
145
+ const m2 = JSON.parse(ref2);
146
+ const parts = Object.entries(m2).map(([id, sha2]) => `\`${id}\` @ \`${short(sha2)}\``);
147
+ return [`> **\u5F53\u524D\u7248\u672C**\uFF08\u4F60\u4E0A\u4E00\u7248\uFF0C\u5DF2\u5E76\u5165\u5404\u4ED3\u4E3B\u5E72\uFF09\uFF1A${parts.join("\uFF0C")}\u2014\u2014clone \u4E3B\u5E72\u5373\u5728\u5B83\u4E4B\u4E0A\u6539\uFF0C\u522B\u4ECE\u5934\u5199\u3002`];
148
+ } catch {
149
+ }
150
+ }
151
+ return [`> **\u5F53\u524D\u7248\u672C**\uFF08\u4F60\u4E0A\u4E00\u7248\uFF0C\u5DF2\u5E76\u5165\u4E3B\u5E72\uFF09\uFF1A\`${repos[0]?.id ?? "<\u4E3B\u4ED3>"}\` @ \`${short(ref2)}\`\u2014\u2014clone \u4E3B\u5E72\u5373\u5728\u5B83\u4E4B\u4E0A\u6539\uFF0C\u522B\u4ECE\u5934\u5199\u3002`];
152
+ })();
153
+ return [
154
+ `\u4F60\u4EA7\u51FA\u7684\u662F\u9879\u76EE **${ctx.projectName ?? "?"}** \u7684\u4EE3\u7801\u2014\u2014**\u5185\u5BB9\u76F4\u63A5\u6D3B\u5728 git \u4ED3\u5E93\u91CC**\uFF0C\u4F60\u50CF\u5F00\u53D1\u8005\u4E00\u6837\u5728 git \u91CC\u5E72\uFF0C\u4EA7\u7269\u4E0D\u8FDB Oasis \u6863\u6848\u67DC\u3002`,
155
+ ``,
156
+ `\u672C\u9879\u76EE\u7684\u4EE3\u7801\u4ED3\u5E93\uFF08\u4F1E\u4ED3\uFF0C\u672C\u673A\u51ED\u8BC1\u5DF2\u914D\u597D\uFF1B**\u53EA\u52A8\u4F60\u9700\u8981\u6539\u7684\uFF0C\u4E0D\u78B0\u7684\u4E0D\u7528 clone**\uFF09\uFF1A`,
157
+ ...repoLines,
158
+ `> \u636E\u4F60\u7684**\u4E0A\u6E38\u8F93\u5165**\u5224\u65AD\u672C\u5DE5\u5355\u8BE5\u843D\u5728**\u54EA\u4E2A**\u4ED3\uFF0C\u518D\u5BF9\u7167\u4E0A\u9762\u9879\u76EE\u73B0\u6709\u7684\u4ED3\uFF1A\u9700\u8981\u7684\u4ED3**\u5728\u6E05\u5355\u91CC** \u2192 \u53EA\u5728\u90A3\u4E2A\u4ED3\u91CC\u5E72\uFF1B**\u4E0D\u5728**\uFF08\u9879\u76EE\u6709\u522B\u7684\u4ED3\u3001\u4F46\u6CA1\u6709\u4F60\u9700\u8981\u7684\u90A3\u4E2A\uFF09\u2192 \u522B\u51D1\u5408\u7528\u522B\u7684\u4ED3\uFF0C\u7528 \`oasis gap ${artifactId} --desc "<\u7F3A\u54EA\u4E2A\u4ED3\u53CA\u7528\u9014>"\` \u62A5\u7F3A\u53E3\u3002`,
159
+ ...versionLine,
160
+ ``,
161
+ `### \u5DE5\u4F5C\u6D41\u7A0B\uFF08git-native\uFF0C\u4F1E\u4ED3\uFF09`,
162
+ `1. \u5BF9**\u6BCF\u4E2A\u4F60\u8981\u6539\u7684\u4ED3\u5E93**\uFF1A\`git clone <remote> repos/<id>\` \u2192 \`cd repos/<id> && git checkout <\u4E3B\u5E72> && git checkout -b ${feat}\``,
163
+ `2. \u57FA\u4E8E \`inputs/\`\uFF08\u4E0A\u6E38\u89C4\u683C\uFF09\u5B9E\u73B0\u6539\u52A8\u2014\u2014clone \u4E0B\u6765\u7684\u4E3B\u5E72\u5DF2\u542B\u6B64\u524D\u5DF2\u5E76\u5165\u7684\u4EE3\u7801\uFF0C**\u5728\u5B83\u4E4B\u4E0A\u6539**`,
164
+ `3. \u6BCF\u4E2A\u6539\u8FC7\u7684\u4ED3\u5E93\uFF1A\u81EA\u67E5\uFF08\u8BED\u6CD5/\u6784\u5EFA/\u80FD\u8DD1\uFF09\uFF0C\`git commit\`\uFF0C\`git push -u origin ${feat}\``,
165
+ `4. \u56DE\u62A5 Oasis\uFF1A\`oasis propose ${artifactId} --reason "<\u4E00\u53E5\u8BDD>"\` \u2192 \`oasis conclude ${artifactId}\``,
166
+ ` **\u4E0D\u7528\u62A5 commit sha**\u2014\u2014\u7CFB\u7EDF\u4F1A\u53BB\u5404\u4ED3\u5E93\u7684 \`${feat}\` \u5206\u652F\u81EA\u52A8\u6536\u96C6\u4F60\u7684\u63D0\u4EA4\uFF0C\u62FC\u6210 {\u4ED3\u5E93\u2192\u63D0\u4EA4} \u6E05\u5355\uFF1B\u6CA1\u78B0\u7684\u4ED3\u5E93\uFF08\`${feat}\` \u65E0\u65B0\u63D0\u4EA4\uFF09\u4E0D\u8BA1\u5165\u3002`,
167
+ ``,
168
+ `**\u4E0D\u8981\u63A8\u4E3B\u5E72\u3001\u4E0D\u8981\u81EA\u5DF1\u5408\u5E76**\u2014\u2014\u96C6\u6210\u7531\u7CFB\u7EDF\u5728 conclude \u540E\u9010\u4ED3\u628A\u5173\u5408\u5E76\u3002\u7248\u672C\u5386\u53F2 diff \u4F60\u81EA\u5DF1 \`git log -p\` \u5373\u53EF\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
169
+ ];
170
+ },
171
+ integrateInstructions(ctx) {
172
+ const { artifactId } = ctx;
173
+ const r0 = ctx.repos?.[0];
174
+ const feat = `feat/${slug(artifactId)}`;
175
+ return [
176
+ `\u8FD9\u662F\u4EE3\u7801\u2014\u2014\u5404\u90E8\u4EF6**\u5DF2\u81EA\u884C\u5E76\u5165\u5DE5\u5355\u5206\u652F \`${feat}\`**\uFF08\u6BCF\u4E2A part \u5E72\u5B8C\u81EA\u5DF1 merge \u8FDB\u6765\u3001\u89E3\u4E86\u5404\u81EA\u7684\u51B2\u7A81\uFF09\u3002\u4F60 owner **\u4E0D\u7528\u518D\u9010\u6761\u5408\u5E76**\uFF0C\u53EA\u9A8C\u6574\u4F53 + \u6536\u53E3\uFF1A`,
177
+ `1. \`git clone ${r0?.remote ?? "<remote>"} repos/${r0?.id ?? "<id>"}\` \u2192 \`git checkout ${feat}\`\uFF0C**\u9A8C\u6574\u4F53\u80FD\u6784\u5EFA/\u80FD\u8DD1**\uFF08\u8FD9\u624D\u662F\u4F60 owner \u7684\u5224\u65AD\u6D3B\uFF09\u3002`,
178
+ `2. \u82E5\u6709\u6B8B\u7559\u95EE\u9898\uFF08\u63A5\u53E3\u6CA1\u5BF9\u9F50\u3001\u6784\u5EFA\u5931\u8D25\u3001\u90E8\u4EF6\u8854\u63A5\u5904\u8981\u6536\u5C3E\uFF09\u2192 \u5C31\u5730\u4FEE\u3001\`git commit\`\u3001\`git push origin ${feat}\`\u3002`,
179
+ `3. \`oasis integrate ${artifactId} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${artifactId}\`\uFF08\u7CFB\u7EDF\u4ECE \`${feat}\` \u6536\u96C6\u6574\u5408\u7248\uFF1Bconclude \u540E\u9010\u4ED3\u540C\u6B65\u8FDB develop\uFF09\u3002`,
180
+ ``,
181
+ `**\u4F60\u8FD9\u7EA7\u662F\u628A\u5173\u6574\u4F53\u81EA\u6D3D\u3001\u4E0D\u662F\u91CD\u65B0\u5408\u5E76**\u2014\u2014\u5408\u5E76\u5DF2\u7ECF\u5728\u5404\u90E8\u4EF6\u6536\u5C3E\u65F6\u5404\u81EA\u505A\u5B8C\u4E86\u3002`
182
+ ];
183
+ },
184
+ submitInstruction(ctx) {
185
+ const id = ctx.artifactId;
186
+ if (ctx.action === "integrate") {
187
+ return `- \`oasis integrate ${id} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u5DE5\u5355\u5206\u652F\u7684\u6574\u5408\u7248\u843D\u4E3A R\\*\uFF08\u7CFB\u7EDF\u4ECE git \u6536\u96C6\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose/advance \u4EA4\u96C6\u6210**\u3002`;
188
+ }
189
+ if (ctx.isPart) {
190
+ return `- \`oasis propose ${id} --part <\u4F60\u7684\u90E8\u4EF6> --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u4EA7\u51FA\uFF08\u4EE3\u7801\uFF1A\u4F60 push \u4F60\u7684\u5DE5\u4F5C\u5206\u652F\uFF0C\u7CFB\u7EDF\u628A\u5B83\u5E72\u51C0\u5408\u8FDB\u5DE5\u5355\u5206\u652F\u518D\u6536\u96C6\uFF1B\u51B2\u7A81\u4F1A\u6253\u56DE\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002\`--part\` \u5FC5\u987B\u662F\u5206\u914D\u7ED9\u4F60\u7684\u90E8\u4EF6\u540D\u3002\u4EA7\u5B8C\u5373\u53EF\u6536\u5DE5\u3002`;
191
+ }
192
+ return `- \`oasis propose ${id} --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4EA7\u7269\u7684\u65B0\u4E00\u7248\uFF08\u4EE3\u7801\uFF1A\u4F60\u5DF2 push \u5230\u5DE5\u5355\u5206\u652F\uFF0C\u7CFB\u7EDF\u4ECE\u5404\u4ED3\u6536\u96C6 {\u4ED3\u5E93\u2192\u63D0\u4EA4}\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u6539\u4E86\u5565\u3002`;
193
+ },
194
+ collectionStrategy: { kind: "external-git" },
195
+ mergeMode: "external-ff"
196
+ // Phase B:code git-native——part 直接 FF、owner produce 收口(见 §9.1/§9.5)
197
+ };
198
+ mediaHandler = {
199
+ produceInstructions(ctx) {
200
+ const { artifactId } = ctx;
201
+ if (ctx.part !== void 0) {
202
+ return [
203
+ `\u4F60\u4EA7\u51FA\u7684\u662F**\u5A92\u4F53**\u4EA4\u4ED8\u7269\u7684\u4E00\u4E2A\u90E8\u4EF6 \`${ctx.part}\`\uFF08\u89C6\u9891/\u97F3\u9891/\u56FE\u7B49\uFF09\u3002\u628A\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u6210\u54C1\u6587\u4EF6\u5199\u8FDB\u672C\u5730\uFF0C\u7136\u540E**\u53EA\u8FD9\u4E00\u6B65**\uFF1A`,
204
+ `\`oasis propose ${artifactId} --file <\u6210\u54C1\u6587\u4EF6> --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD>"\`\u3002**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D conclude**\uFF0C\u7B49\u5404\u90E8\u4EF6\u9F50\u4E86 owner \u6536\u53E3\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
205
+ ];
206
+ }
207
+ return [
208
+ ctx.hasHead ? `\u4F60\u7684\u4EA4\u4ED8\u7269\u5728 \`./deliverable/\`\uFF08\u5A92\u4F53\u6210\u54C1\uFF1A\u89C6\u9891/\u97F3\u9891/\u56FE\u7B49\uFF09\u2014\u2014\u5728\u5B83\u57FA\u7840\u4E0A\u66F4\u65B0/\u66FF\u6362\u3002` : `\u4F60\u7684\u4EA4\u4ED8\u7269\u76EE\u5F55\u662F \`./deliverable/\`\uFF08\u5F53\u524D\u4E3A\u7A7A\u2014\u2014\u4EA7\u51FA\u9996\u7248\u6210\u54C1\uFF09\u3002`,
209
+ `\u628A**\u6E32\u67D3/\u5BFC\u51FA\u597D\u7684\u6210\u54C1\u6587\u4EF6**\u653E\u8FDB \`deliverable/\`\uFF08\u6309\u4EA4\u4ED8\u8981\u6C42\u7684\u683C\u5F0F\uFF0C\u5982 mp4/wav/png\uFF1B\u5927\u4E8C\u8FDB\u5236\u76F4\u63A5\u653E\uFF0C\u522B\u585E\u8FDB\u6587\u672C\uFF09\uFF0C\u7136\u540E\uFF1A`,
210
+ `\`oasis propose ${artifactId} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\` \u2192 \u6574\u4F53\u5B8C\u6574\u4E86\u518D \`oasis conclude ${artifactId}\`\u3002`,
211
+ `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\uFF08\u811A\u672C/\u5206\u955C/\u7D20\u6750\u6CA1\u5C31\u7EEA\uFF09\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
212
+ ];
213
+ },
214
+ integrateInstructions(ctx) {
215
+ const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
216
+ return [
217
+ `\u5404\u90E8\u4EF6\u7684\u6210\u54C1\u5728 \`parts/<\u540D>/\`\uFF08${parts}\uFF09\u3002\u628A\u5B83\u4EEC\u6C47\u6210**\u6700\u7EC8\u6210\u54C1**\u653E\u8FDB \`deliverable/\`\uFF08\u6309\u4EA4\u4ED8\u683C\u5F0F\u3001\u987A\u5E8F/\u62FC\u63A5\u6B63\u786E\uFF09\uFF0C\u7136\u540E\uFF1A`,
218
+ `\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002**\u522B\u4E22\u4EFB\u4F55\u90E8\u4EF6**\u3002`
219
+ ];
220
+ },
221
+ submitInstruction: deliverableSubmit,
222
+ collectionStrategy: { kind: "inline" },
223
+ mergeMode: "oasis-queue"
224
+ };
225
+ }
226
+ });
227
+
37
228
  // ../core/src/read-model.ts
38
229
  function emptyReadModel() {
39
230
  return {
@@ -78,6 +269,7 @@ function fold(model, op) {
78
269
  workspace: p2.workspace,
79
270
  persistence: p2.persistence ?? "temp",
80
271
  createdVia: p2.createdVia,
272
+ ...p2.isFinalOutput !== void 0 ? { isFinalOutput: p2.isFinalOutput } : {},
81
273
  ...p2.title !== void 0 ? { title: p2.title } : {},
82
274
  ...p2.description !== void 0 ? { description: p2.description } : {},
83
275
  ...p2.docType !== void 0 ? { docType: p2.docType } : {},
@@ -944,10 +1136,38 @@ function computeCascadeActions(model, schema) {
944
1136
  }
945
1137
  return out;
946
1138
  }
1139
+ function advanceableHead(model, schema, id) {
1140
+ if (lifecycleOf(model, id) !== "active") return null;
1141
+ const head = queueOf(model, id)[0];
1142
+ if (!head) return null;
1143
+ if (isStale(model, head)) return null;
1144
+ if (pendingConcludeAttempt(model, id)) return null;
1145
+ const artifact = model.artifacts.get(id);
1146
+ const typeDef = schema.get(artifact.type);
1147
+ const isExternalFF = handlerForType(typeDef?.contentType)?.mergeMode === "external-ff";
1148
+ const eagerFF = head.part === void 0 || (artifact.parts ?? []).length <= 1 || isExternalFF;
1149
+ if (!eagerFF) return null;
1150
+ const proposerIsAgent = head.author.startsWith("actor:agent:");
1151
+ const requiresHumanReview = proposerIsAgent && (head.resolves !== void 0 ? head.resolves.some((annId) => {
1152
+ const a = model.annotations.get(annId);
1153
+ return a !== void 0 && isHumanChangeRequest(a);
1154
+ }) : hasOpenHumanChangeRequest(model, id));
1155
+ if (requiresHumanReview) return null;
1156
+ return head;
1157
+ }
1158
+ function computeAdvanceableRevisions(model, schema) {
1159
+ const out = [];
1160
+ for (const a of model.artifacts.values()) {
1161
+ const rev = advanceableHead(model, schema, a.id);
1162
+ if (rev) out.push({ artifactId: a.id, revisionId: rev.id });
1163
+ }
1164
+ return out;
1165
+ }
947
1166
  var CorruptLogError, STRUCTURAL_OP_KINDS, ROUND_LIMIT_MARKER, PENDING_ROLE_PREFIX, PENDING_UNASSIGNED;
948
1167
  var init_read_model = __esm({
949
1168
  "../core/src/read-model.ts"() {
950
1169
  "use strict";
1170
+ init_content_type_handler();
951
1171
  CorruptLogError = class extends Error {
952
1172
  name = "CorruptLogError";
953
1173
  constructor(op, detail) {
@@ -1036,197 +1256,6 @@ var init_content_handler = __esm({
1036
1256
  }
1037
1257
  });
1038
1258
 
1039
- // ../core/src/content-type-handler.ts
1040
- function deliverableSubmit(ctx) {
1041
- const id = ctx.artifactId;
1042
- if (ctx.action === "integrate") {
1043
- return `- \`oasis integrate ${id} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u4F60\u5728 deliverable/ \u5408\u6210\u7684\u6574\u5408\u7248\u843D\u4E3A\u65B0 head\uFF08\u7CFB\u7EDF\u5185\u90E8\u4F1A\u628A\u961F\u5217\u91CC\u5176\u4F59\u5F85\u843D\u7248\u672C\u4E00\u5E76\u5E76\u8FDB\u6765\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose / advance \u4EA4\u96C6\u6210**\uFF08\u90A3\u4F1A\u628A\u96C6\u6210\u5F53\u6210\u53C8\u4E00\u4E2A\u5E76\u5217\u7248\u672C\u3001\u843D\u9519\u7248\u672C\uFF09\u3002`;
1044
- }
1045
- if (ctx.isPart) {
1046
- return `- \`oasis propose ${id} --part <\u4F60\u7684\u90E8\u4EF6> --file <\u6587\u4EF6> --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u4EA7\u51FA\u3002\`--part\` \u5FC5\u987B\u662F\u5206\u914D\u7ED9\u4F60\u7684\u90A3\u4E2A\u90E8\u4EF6\u540D\uFF1B\`--file\` \u6307\u5411\u4F60\u5199\u597D\u7684\u5185\u5BB9\u6587\u4EF6\uFF08\u90E8\u4EF6\u662F\u72EC\u7ACB\u788E\u7247\uFF0C\u5199\u4F60\u81EA\u5DF1\u7684\uFF09\uFF1B\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u505A\u4E86\u5565\u3002\u4EA7\u5B8C\u5373\u53EF\u6536\u5DE5\uFF0C\u96C6\u6210\u7531 owner \u505A\u3002`;
1047
- }
1048
- return `- \`oasis propose ${id} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4EA7\u7269\u7684\u65B0\u4E00\u7248\u3002\`--from-dir\` \u6307\u5411\u4F60\u7F16\u8F91\u540E\u7684 deliverable/\uFF08\u7CFB\u7EDF\u636E\u76EE\u5F55\u7B97\u8FD9\u7248\u3001\u5E76\u548C\u4E0A\u4E00\u7248 diff\uFF09\uFF1B\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u6539\u4E86\u5565\uFF08\u7ED9\u8BC4\u5BA1/\u540E\u4EBA\u770B\uFF0C\u4E0D\u7528\u50CF git commit \u90A3\u4E48\u7EC6\uFF09\u3002`;
1049
- }
1050
- function handlerFor(repos, contentType) {
1051
- if (repos?.some((r) => r.remote)) return codeHandler;
1052
- if (contentType === "media") return mediaHandler;
1053
- return textHandler;
1054
- }
1055
- function handlerForType(contentType) {
1056
- if (contentType === void 0) return void 0;
1057
- if (contentType === "code") return codeHandler;
1058
- if (contentType === "media") return mediaHandler;
1059
- return textHandler;
1060
- }
1061
- var slug, textHandler, codeHandler, mediaHandler;
1062
- var init_content_type_handler = __esm({
1063
- "../core/src/content-type-handler.ts"() {
1064
- "use strict";
1065
- slug = (id) => id.replace(/[^a-zA-Z0-9_-]+/g, "_");
1066
- textHandler = {
1067
- produceInstructions(ctx) {
1068
- const { artifactId } = ctx;
1069
- if (ctx.part !== void 0) {
1070
- return [
1071
- `\u628A\u4F60\u8FD9\u4E2A\u90E8\u4EF6 \`${ctx.part}\` \u7684\u5B8C\u6574\u5185\u5BB9\u5199\u8FDB\u672C\u5730\u6587\u4EF6\uFF0C\u7136\u540E**\u53EA\u6267\u884C\u8FD9\u4E00\u6B65**\uFF1A`,
1072
- `\`oasis propose ${artifactId} --file <\u6587\u4EF6> --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\`\u3002`,
1073
- `**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D\u8981 advance\u3001\u4E0D\u8981 conclude**\u3002\u7B49\u6240\u6709\u5144\u5F1F\u90E8\u4EF6\u90FD\u4EA7\u51FA\u540E\uFF0C\u7CFB\u7EDF\u4F1A\u628A\u5404\u90E8\u4EF6\u96C6\u6210\u6210\u4E00\u7248\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`,
1074
- ``,
1075
- `> \u26A0 \u5927\u6587\u4EF6\u52A1\u5FC5**\u589E\u91CF\u5199**\uFF08Write \u9AA8\u67B6 \u2192 Edit \u5206\u6B21\u8FFD\u52A0\uFF09\uFF0C\u522B\u4E00\u6B21\u6027\u5410\u6574\u4E2A\u5927\u6587\u4EF6\uFF08\u6709\u8F93\u51FA token \u4E0A\u9650\uFF09\u3002`
1076
- ];
1077
- }
1078
- if (ctx.unionInto !== void 0) {
1079
- return [
1080
- `\u4F60\u4EA7\u51FA\u7684\u662F**\u6574\u4F53\u4EA4\u4ED8\u7269\u7684\u4E00\u4E2A\u6A21\u5757**\uFF0C\u4F1A\u88AB\u673A\u68B0\u5E76\u5165 \`${ctx.unionInto}\`\uFF08\u6309\u6587\u4EF6\u540D\u5408\u5E76\uFF09\u3002`,
1081
- `\u628A\u4F60\u8D1F\u8D23\u7684\u6587\u4EF6\u5199\u8FDB\u4E00\u4E2A**\u76EE\u5F55**\uFF08\u6587\u4EF6\u540D\u5373\u5B83\u5728\u6700\u7EC8\u4EA4\u4ED8\u7269\u91CC\u7684\u8DEF\u5F84\uFF0C\u5982 \`combat.js\`\uFF09\uFF0C\u7136\u540E\uFF1A`,
1082
- `\`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\` \u2192 \`oasis conclude ${artifactId}\`\u3002`,
1083
- `**\u53EA\u5199\u4F60\u8FD9\u4E2A\u6A21\u5757\u7684\u6587\u4EF6\uFF0C\u4E0D\u8981\u78B0\u522B\u7684\u6A21\u5757\u7684\u6587\u4EF6**\uFF08\u540C\u540D\u4F1A\u8986\u76D6\u522B\u4EBA\u7684\uFF09\u3002\u4E25\u683C\u9075\u5B88\u4E0A\u6E38 tech-design \u7EA6\u5B9A\u7684\u63A5\u53E3/\u5168\u5C40\u547D\u540D\uFF0C\u6A21\u5757\u95F4\u624D\u80FD\u62FC\u5F97\u8D77\u6765\u3002`,
1084
- `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1085
- ];
1086
- }
1087
- return [
1088
- ctx.hasHead ? `\u4F60\u7684\u4EA4\u4ED8\u7269\u5728 \`./deliverable/\` \u2014\u2014 \u91CC\u9762\u5DF2\u662F**\u5F53\u524D\u7248\u672C\u7684\u5B8C\u6574\u5185\u5BB9**\uFF0C\u76F4\u63A5\u5728\u5B83\u4E0A\u9762\u589E\u5220\u6539\u3002` : `\u4F60\u7684\u4EA4\u4ED8\u7269\u76EE\u5F55\u662F \`./deliverable/\`\uFF08\u5F53\u524D\u4E3A\u7A7A\u2014\u2014\u8FD9\u662F\u5168\u65B0\u4EA7\u7269\uFF0C\u5728\u5176\u4E2D\u4EA7\u51FA\u9996\u7248\u5185\u5BB9\uFF09\u3002`,
1089
- `- \u6700\u7EC8\u4EA7\u7269\u53EA\u653E \`deliverable/\`\uFF1B\u4E34\u65F6\u6587\u4EF6\uFF08\u8349\u7A3F/\u811A\u672C/\u5B9E\u9A8C\uFF09\u653E\u5230**\u522B\u5904**\uFF0C\u522B\u7559\u5728 \`deliverable/\` \u91CC\u3002`,
1090
- `- \u6539\u597D\u540E\u6574\u76EE\u5F55\u63D0\u4EA4\uFF1A\`oasis propose ${artifactId} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\``,
1091
- ` \u2192 \u7CFB\u7EDF\u81EA\u52A8\u843D\u5730\u4E3A\u65B0\u7248\u672C\uFF08**\u65E0\u9700 advance**\uFF09\uFF1B\u6574\u4E2A\u4EA4\u4ED8\u7269**\u771F\u6B63\u5B8C\u6574**\u4E86\u518D \`oasis conclude ${artifactId}\`\u3002`,
1092
- ...ctx.hasHead ? [`\uFF08\`deliverable/\` \u672C\u5C31\u662F head \u7684\u5B8C\u6574\u526F\u672C\u2014\u2014\u4F60\u4EA4\u7684\u4E00\u5B9A\u662F\u5B8C\u6574\u7248\uFF0C\u4E0D\u4F1A\u4E22\u5DF2\u6709\u5185\u5BB9\u3002\uFF09`] : [],
1093
- `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1094
- ];
1095
- },
1096
- integrateInstructions(ctx) {
1097
- const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
1098
- return [
1099
- `\u5404\u90E8\u4EF6\u4ECE**\u5171\u540C\u7956\u5148 H** \u5206\u5934\u4EA7\u51FA\u3001\u5404\u6539\u5404\u5757\uFF1A\u6BCF\u6761\u5185\u5BB9\u5728 \`parts/<\u540D>/CONTENT.md\`\uFF08\u6216\u5176\u5C55\u5F00\u6587\u4EF6\uFF09\u91CC\uFF08${parts}\uFF09\uFF0CH \u7684\u5185\u5BB9\u5728 \`deliverable/\`\uFF08\u4ECE\u96F6\u5206\u89E3\u5219\u4E3A\u7A7A\uFF09\u3002`,
1100
- `\u628A\u5B83\u4EEC\u5408\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u6574\u5408\u7248**\u5C31\u5730\u5199\u8FDB \`deliverable/\`**\u2014\u2014**\u4EE5 H \u4E3A\u5E95**\u3001\u628A\u5404\u90E8\u4EF6\u6539\u52A8**\u5168\u5E76\u8FDB\u6765**\uFF08\u5355\u6587\u4EF6 \`deliverable/CONTENT.md\`\uFF0C\u591A\u6587\u4EF6\u94FA\u6210\u591A\u4E2A\u6587\u4EF6\uFF1B\u5927\u6587\u4EF6\u589E\u91CF\u5199\uFF1AWrite \u9AA8\u67B6 \u2192 Edit \u8FFD\u52A0\uFF09\uFF0C\u7136\u540E\uFF1A`,
1101
- `\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002`,
1102
- ``,
1103
- `**\u5FE0\u5B9E\u5408\u5E76\u3001\u522B\u4E22\u4EFB\u4F55\u90E8\u4EF6**\uFF1B\u63A5\u53E3/\u547D\u540D\u5BF9\u4E0D\u9F50\u5C31\u6309\u4E0A\u6E38\u89C4\u683C\u6821\u6B63\uFF0C\u8BA9 N \u4E2A\u90E8\u4EF6\u62FC\u6210\u4E00\u4E2A\u81EA\u6D3D\u6574\u4F53\u3002`
1104
- ];
1105
- },
1106
- submitInstruction: deliverableSubmit,
1107
- collectionStrategy: { kind: "inline" },
1108
- mergeMode: "oasis-queue"
1109
- };
1110
- codeHandler = {
1111
- produceInstructions(ctx) {
1112
- const { artifactId } = ctx;
1113
- const repos = ctx.repos ?? [];
1114
- const r0 = repos[0];
1115
- if (ctx.part !== void 0) {
1116
- const work = `feat/${slug(artifactId)}__${slug(ctx.part)}`;
1117
- const feat2 = `feat/${slug(artifactId)}`;
1118
- const dev = r0?.develop ?? "develop";
1119
- return [
1120
- `\u4F60\u4EA7\u51FA\u7684\u662F\u9879\u76EE **${ctx.projectName ?? "?"}** \u7684\u4EE3\u7801\u3001\u4E14\u53EA\u662F\u90E8\u4EF6 \`${ctx.part}\`\u2014\u2014\u5185\u5BB9\u6D3B\u5728 git \u91CC\u3002\u4F60**\u53EA\u7BA1\u4EA7\u51FA\u4F60\u8FD9\u5757\u3001push \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u5206\u652F**\uFF1B\`oasis propose\` \u65F6**\u7CFB\u7EDF\u4F1A\u628A\u5B83\u5E72\u51C0\u5408\u8FDB\u5DE5\u5355\u5206\u652F \`${feat2}\`**\uFF08\u4E0D\u7528\u4F60\u624B\u52A8\u5408\u3001\u4E5F\u4E0D\u7528\u7B49 owner \u5408\uFF09\u3002`,
1121
- `1. \`git clone ${r0?.remote ?? "<remote>"} repos/${r0?.id ?? "<id>"}\` \u2192 \`cd repos/${r0?.id ?? "<id>"}\` \u2192 \u4ECE\u4E3B\u5E72\u8D77\u4F60\u7684\u5DE5\u4F5C\u5206\u652F\uFF1A\`git checkout ${dev} && git checkout -b ${work}\``,
1122
- `2. \u5B9E\u73B0\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u6539\u52A8\uFF08\u4E25\u5B88\u4E0A\u6E38\u63A5\u53E3\u7EA6\u5B9A\uFF09\u3001\u81EA\u67E5\u80FD\u6784\u5EFA/\u80FD\u8DD1\u3001\`git commit\``,
1123
- `3. **\u53EA\u63A8\u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u5206\u652F**\uFF1A\`git push -u origin ${work}\`\uFF08**\u522B\u78B0 \`${feat2}\`**\u2014\u2014\u5408\u5E76\u4EA4\u7ED9\u7CFB\u7EDF\uFF09`,
1124
- `4. \u56DE\u62A5\uFF1A\`oasis propose ${artifactId} --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD>"\`\uFF08\u7CFB\u7EDF\u628A \`${work}\` \u5E72\u51C0\u5408\u8FDB \`${feat2}\` \u518D\u6536\u96C6\uFF1B**\u82E5\u4E0E \`${feat2}\` \u51B2\u7A81\u4F1A\u5F53\u573A\u6253\u56DE**\u2014\u2014\u6309\u63D0\u793A \`git fetch && git rebase origin/${feat2}\` \u89E3\u5B8C\u51B2\u7A81\u3001\`git push -f origin ${work}\` \u540E\u91CD\u65B0 propose\uFF09`,
1125
- `**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D conclude**\u3002\u5404\u90E8\u4EF6\u90FD\u5E76\u5B8C\u540E\u7531 owner \u9A8C\u6574\u4F53\u6536\u53E3\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1126
- ];
1127
- }
1128
- const feat = `feat/${slug(artifactId)}`;
1129
- if (ctx.isFinalize) {
1130
- const r02 = repos[0];
1131
- return [
1132
- `\u4F60\u662F **${ctx.projectName ?? "?"}** \u4EE3\u7801\u4EA4\u4ED8\u7269\u7684 owner\uFF0C\u6765**\u6536\u53E3**\uFF1A\u5404\u90E8\u4EF6\u5DF2\u5404\u81EA\u5E76\u5165**\u5DE5\u5355\u5206\u652F \`${feat}\`**\uFF08\u6BCF\u4E2A part \u5E72\u5B8C\u81EA\u5DF1 merge \u8FDB\u6765\u3001\u89E3\u4E86\u5404\u81EA\u51B2\u7A81\uFF09\u3002\u4F60**\u4E0D\u7528\u518D\u9010\u6761\u5408\u5E76**\uFF0C\u53EA\u9A8C\u6574\u4F53 + \u6536\u53E3\uFF1A`,
1133
- `1. \`git clone ${r02?.remote ?? "<remote>"} repos/${r02?.id ?? "<id>"}\` \u2192 \`cd repos/${r02?.id ?? "<id>"} && git fetch && git checkout ${feat}\`\uFF08**\u662F\u5DF2\u5B58\u5728\u7684\u5DE5\u5355\u5206\u652F\u3001\u542B\u5404\u90E8\u4EF6\u5168\u90E8\u6539\u52A8**\uFF0C\u4E0D\u662F\u4ECE\u4E3B\u5E72\u65B0\u5EFA\uFF09\u3002\u591A\u4ED3\u5219\u5BF9\u6BCF\u4E2A\u78B0\u8FC7\u7684\u4ED3\u540C\u6837 checkout \`${feat}\`\u3002`,
1134
- `2. **\u9A8C\u6574\u4F53\u80FD\u6784\u5EFA / \u80FD\u8DD1**\uFF08\u8FD9\u624D\u662F\u4F60 owner \u7684\u5224\u65AD\u6D3B\uFF09\uFF1B\u6709\u6B8B\u7559\u95EE\u9898\uFF08\u63A5\u53E3\u6CA1\u5BF9\u9F50 / \u6784\u5EFA\u5931\u8D25 / \u90E8\u4EF6\u8854\u63A5\u5904\u8981\u6536\u5C3E\uFF09\u2192 \u5C31\u5730\u4FEE\u3001\`git commit\`\u3001\`git push origin ${feat}\`\u3002`,
1135
- `3. \`oasis propose ${artifactId} --reason "<\u6536\u53E3\u8BF4\u660E>"\`\uFF08\u7CFB\u7EDF\u4ECE\u5404\u4ED3 \`${feat}\` \u6536\u5168 {\u4ED3\u5E93\u2192\u63D0\u4EA4} = \u5168\u90E8\u4EF6\u6574\u5408\u6001\uFF09\u2192 \`oasis conclude ${artifactId}\`\uFF08conclude \u540E\u9010\u4ED3 gated \u5408\u8FDB\u4E3B\u5E72\uFF09\u3002`,
1136
- ``,
1137
- `**\u4F60\u8FD9\u7EA7\u662F\u628A\u5173\u6574\u4F53\u81EA\u6D3D + \u6536\u53E3\u6210\u4EA4\u4ED8\u7248\uFF0C\u4E0D\u662F\u91CD\u65B0\u5B9E\u73B0**\u2014\u2014\u5404\u90E8\u4EF6\u7684\u5B9E\u73B0\u5DF2\u5728\u5B83\u4EEC\u5404\u81EA\u6536\u5C3E\u65F6\u5E76\u8FDB \`${feat}\` \u4E86\u3002**\u522B\u7528 \`oasis integrate\`**\uFF1A\u4EE3\u7801\u6536\u53E3\u5C31\u8D70 \`propose\`\uFF08git-native\uFF09\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1138
- ];
1139
- }
1140
- const repoLines = repos.map((r) => ` - \`${r.id}\`\uFF1A${r.remote ?? "<remote>"}\uFF08\u4E3B\u5E72 \`${r.develop}\`\uFF09`);
1141
- const short = (s2) => s2.slice(0, 12);
1142
- const versionLine = (() => {
1143
- if (!(ctx.hasHead && ctx.headExternalRef)) return [];
1144
- const ref2 = ctx.headExternalRef;
1145
- if (ref2.trim().startsWith("{")) {
1146
- try {
1147
- const m2 = JSON.parse(ref2);
1148
- const parts = Object.entries(m2).map(([id, sha2]) => `\`${id}\` @ \`${short(sha2)}\``);
1149
- return [`> **\u5F53\u524D\u7248\u672C**\uFF08\u4F60\u4E0A\u4E00\u7248\uFF0C\u5DF2\u5E76\u5165\u5404\u4ED3\u4E3B\u5E72\uFF09\uFF1A${parts.join("\uFF0C")}\u2014\u2014clone \u4E3B\u5E72\u5373\u5728\u5B83\u4E4B\u4E0A\u6539\uFF0C\u522B\u4ECE\u5934\u5199\u3002`];
1150
- } catch {
1151
- }
1152
- }
1153
- return [`> **\u5F53\u524D\u7248\u672C**\uFF08\u4F60\u4E0A\u4E00\u7248\uFF0C\u5DF2\u5E76\u5165\u4E3B\u5E72\uFF09\uFF1A\`${repos[0]?.id ?? "<\u4E3B\u4ED3>"}\` @ \`${short(ref2)}\`\u2014\u2014clone \u4E3B\u5E72\u5373\u5728\u5B83\u4E4B\u4E0A\u6539\uFF0C\u522B\u4ECE\u5934\u5199\u3002`];
1154
- })();
1155
- return [
1156
- `\u4F60\u4EA7\u51FA\u7684\u662F\u9879\u76EE **${ctx.projectName ?? "?"}** \u7684\u4EE3\u7801\u2014\u2014**\u5185\u5BB9\u76F4\u63A5\u6D3B\u5728 git \u4ED3\u5E93\u91CC**\uFF0C\u4F60\u50CF\u5F00\u53D1\u8005\u4E00\u6837\u5728 git \u91CC\u5E72\uFF0C\u4EA7\u7269\u4E0D\u8FDB Oasis \u6863\u6848\u67DC\u3002`,
1157
- ``,
1158
- `\u672C\u9879\u76EE\u7684\u4EE3\u7801\u4ED3\u5E93\uFF08\u4F1E\u4ED3\uFF0C\u672C\u673A\u51ED\u8BC1\u5DF2\u914D\u597D\uFF1B**\u53EA\u52A8\u4F60\u9700\u8981\u6539\u7684\uFF0C\u4E0D\u78B0\u7684\u4E0D\u7528 clone**\uFF09\uFF1A`,
1159
- ...repoLines,
1160
- `> \u636E\u4F60\u7684**\u4E0A\u6E38\u8F93\u5165**\u5224\u65AD\u672C\u5DE5\u5355\u8BE5\u843D\u5728**\u54EA\u4E2A**\u4ED3\uFF0C\u518D\u5BF9\u7167\u4E0A\u9762\u9879\u76EE\u73B0\u6709\u7684\u4ED3\uFF1A\u9700\u8981\u7684\u4ED3**\u5728\u6E05\u5355\u91CC** \u2192 \u53EA\u5728\u90A3\u4E2A\u4ED3\u91CC\u5E72\uFF1B**\u4E0D\u5728**\uFF08\u9879\u76EE\u6709\u522B\u7684\u4ED3\u3001\u4F46\u6CA1\u6709\u4F60\u9700\u8981\u7684\u90A3\u4E2A\uFF09\u2192 \u522B\u51D1\u5408\u7528\u522B\u7684\u4ED3\uFF0C\u7528 \`oasis gap ${artifactId} --desc "<\u7F3A\u54EA\u4E2A\u4ED3\u53CA\u7528\u9014>"\` \u62A5\u7F3A\u53E3\u3002`,
1161
- ...versionLine,
1162
- ``,
1163
- `### \u5DE5\u4F5C\u6D41\u7A0B\uFF08git-native\uFF0C\u4F1E\u4ED3\uFF09`,
1164
- `1. \u5BF9**\u6BCF\u4E2A\u4F60\u8981\u6539\u7684\u4ED3\u5E93**\uFF1A\`git clone <remote> repos/<id>\` \u2192 \`cd repos/<id> && git checkout <\u4E3B\u5E72> && git checkout -b ${feat}\``,
1165
- `2. \u57FA\u4E8E \`inputs/\`\uFF08\u4E0A\u6E38\u89C4\u683C\uFF09\u5B9E\u73B0\u6539\u52A8\u2014\u2014clone \u4E0B\u6765\u7684\u4E3B\u5E72\u5DF2\u542B\u6B64\u524D\u5DF2\u5E76\u5165\u7684\u4EE3\u7801\uFF0C**\u5728\u5B83\u4E4B\u4E0A\u6539**`,
1166
- `3. \u6BCF\u4E2A\u6539\u8FC7\u7684\u4ED3\u5E93\uFF1A\u81EA\u67E5\uFF08\u8BED\u6CD5/\u6784\u5EFA/\u80FD\u8DD1\uFF09\uFF0C\`git commit\`\uFF0C\`git push -u origin ${feat}\``,
1167
- `4. \u56DE\u62A5 Oasis\uFF1A\`oasis propose ${artifactId} --reason "<\u4E00\u53E5\u8BDD>"\` \u2192 \`oasis conclude ${artifactId}\``,
1168
- ` **\u4E0D\u7528\u62A5 commit sha**\u2014\u2014\u7CFB\u7EDF\u4F1A\u53BB\u5404\u4ED3\u5E93\u7684 \`${feat}\` \u5206\u652F\u81EA\u52A8\u6536\u96C6\u4F60\u7684\u63D0\u4EA4\uFF0C\u62FC\u6210 {\u4ED3\u5E93\u2192\u63D0\u4EA4} \u6E05\u5355\uFF1B\u6CA1\u78B0\u7684\u4ED3\u5E93\uFF08\`${feat}\` \u65E0\u65B0\u63D0\u4EA4\uFF09\u4E0D\u8BA1\u5165\u3002`,
1169
- ``,
1170
- `**\u4E0D\u8981\u63A8\u4E3B\u5E72\u3001\u4E0D\u8981\u81EA\u5DF1\u5408\u5E76**\u2014\u2014\u96C6\u6210\u7531\u7CFB\u7EDF\u5728 conclude \u540E\u9010\u4ED3\u628A\u5173\u5408\u5E76\u3002\u7248\u672C\u5386\u53F2 diff \u4F60\u81EA\u5DF1 \`git log -p\` \u5373\u53EF\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1171
- ];
1172
- },
1173
- integrateInstructions(ctx) {
1174
- const { artifactId } = ctx;
1175
- const r0 = ctx.repos?.[0];
1176
- const feat = `feat/${slug(artifactId)}`;
1177
- return [
1178
- `\u8FD9\u662F\u4EE3\u7801\u2014\u2014\u5404\u90E8\u4EF6**\u5DF2\u81EA\u884C\u5E76\u5165\u5DE5\u5355\u5206\u652F \`${feat}\`**\uFF08\u6BCF\u4E2A part \u5E72\u5B8C\u81EA\u5DF1 merge \u8FDB\u6765\u3001\u89E3\u4E86\u5404\u81EA\u7684\u51B2\u7A81\uFF09\u3002\u4F60 owner **\u4E0D\u7528\u518D\u9010\u6761\u5408\u5E76**\uFF0C\u53EA\u9A8C\u6574\u4F53 + \u6536\u53E3\uFF1A`,
1179
- `1. \`git clone ${r0?.remote ?? "<remote>"} repos/${r0?.id ?? "<id>"}\` \u2192 \`git checkout ${feat}\`\uFF0C**\u9A8C\u6574\u4F53\u80FD\u6784\u5EFA/\u80FD\u8DD1**\uFF08\u8FD9\u624D\u662F\u4F60 owner \u7684\u5224\u65AD\u6D3B\uFF09\u3002`,
1180
- `2. \u82E5\u6709\u6B8B\u7559\u95EE\u9898\uFF08\u63A5\u53E3\u6CA1\u5BF9\u9F50\u3001\u6784\u5EFA\u5931\u8D25\u3001\u90E8\u4EF6\u8854\u63A5\u5904\u8981\u6536\u5C3E\uFF09\u2192 \u5C31\u5730\u4FEE\u3001\`git commit\`\u3001\`git push origin ${feat}\`\u3002`,
1181
- `3. \`oasis integrate ${artifactId} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${artifactId}\`\uFF08\u7CFB\u7EDF\u4ECE \`${feat}\` \u6536\u96C6\u6574\u5408\u7248\uFF1Bconclude \u540E\u9010\u4ED3\u540C\u6B65\u8FDB develop\uFF09\u3002`,
1182
- ``,
1183
- `**\u4F60\u8FD9\u7EA7\u662F\u628A\u5173\u6574\u4F53\u81EA\u6D3D\u3001\u4E0D\u662F\u91CD\u65B0\u5408\u5E76**\u2014\u2014\u5408\u5E76\u5DF2\u7ECF\u5728\u5404\u90E8\u4EF6\u6536\u5C3E\u65F6\u5404\u81EA\u505A\u5B8C\u4E86\u3002`
1184
- ];
1185
- },
1186
- submitInstruction(ctx) {
1187
- const id = ctx.artifactId;
1188
- if (ctx.action === "integrate") {
1189
- return `- \`oasis integrate ${id} --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2014\u2014 **\u6536\u53E3\u4E13\u7528**\uFF1A\u628A\u5DE5\u5355\u5206\u652F\u7684\u6574\u5408\u7248\u843D\u4E3A R\\*\uFF08\u7CFB\u7EDF\u4ECE git \u6536\u96C6\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002**\u7EDD\u4E0D\u8981\u7528 propose/advance \u4EA4\u96C6\u6210**\u3002`;
1190
- }
1191
- if (ctx.isPart) {
1192
- return `- \`oasis propose ${id} --part <\u4F60\u7684\u90E8\u4EF6> --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u4EA7\u51FA\uFF08\u4EE3\u7801\uFF1A\u4F60 push \u4F60\u7684\u5DE5\u4F5C\u5206\u652F\uFF0C\u7CFB\u7EDF\u628A\u5B83\u5E72\u51C0\u5408\u8FDB\u5DE5\u5355\u5206\u652F\u518D\u6536\u96C6\uFF1B\u51B2\u7A81\u4F1A\u6253\u56DE\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002\`--part\` \u5FC5\u987B\u662F\u5206\u914D\u7ED9\u4F60\u7684\u90E8\u4EF6\u540D\u3002\u4EA7\u5B8C\u5373\u53EF\u6536\u5DE5\u3002`;
1193
- }
1194
- return `- \`oasis propose ${id} --reason "<\u4E00\u53E5\u8BDD>"\` \u2014\u2014 \u63D0\u4EA4\u4EA7\u7269\u7684\u65B0\u4E00\u7248\uFF08\u4EE3\u7801\uFF1A\u4F60\u5DF2 push \u5230\u5DE5\u5355\u5206\u652F\uFF0C\u7CFB\u7EDF\u4ECE\u5404\u4ED3\u6536\u96C6 {\u4ED3\u5E93\u2192\u63D0\u4EA4}\uFF1B\u89C1\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002\`--reason\` \u4E00\u53E5\u8BDD\u8BF4\u6E05\u6539\u4E86\u5565\u3002`;
1195
- },
1196
- collectionStrategy: { kind: "external-git" },
1197
- mergeMode: "external-ff"
1198
- // Phase B:code git-native——part 直接 FF、owner produce 收口(见 §9.1/§9.5)
1199
- };
1200
- mediaHandler = {
1201
- produceInstructions(ctx) {
1202
- const { artifactId } = ctx;
1203
- if (ctx.part !== void 0) {
1204
- return [
1205
- `\u4F60\u4EA7\u51FA\u7684\u662F**\u5A92\u4F53**\u4EA4\u4ED8\u7269\u7684\u4E00\u4E2A\u90E8\u4EF6 \`${ctx.part}\`\uFF08\u89C6\u9891/\u97F3\u9891/\u56FE\u7B49\uFF09\u3002\u628A\u4F60\u8FD9\u4E2A\u90E8\u4EF6\u7684\u6210\u54C1\u6587\u4EF6\u5199\u8FDB\u672C\u5730\uFF0C\u7136\u540E**\u53EA\u8FD9\u4E00\u6B65**\uFF1A`,
1206
- `\`oasis propose ${artifactId} --file <\u6210\u54C1\u6587\u4EF6> --part ${ctx.part} --reason "<\u4E00\u53E5\u8BDD>"\`\u3002**\u5230\u6B64\u4E3A\u6B62\u2014\u2014\u4E0D conclude**\uFF0C\u7B49\u5404\u90E8\u4EF6\u9F50\u4E86 owner \u6536\u53E3\u3002**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1207
- ];
1208
- }
1209
- return [
1210
- ctx.hasHead ? `\u4F60\u7684\u4EA4\u4ED8\u7269\u5728 \`./deliverable/\`\uFF08\u5A92\u4F53\u6210\u54C1\uFF1A\u89C6\u9891/\u97F3\u9891/\u56FE\u7B49\uFF09\u2014\u2014\u5728\u5B83\u57FA\u7840\u4E0A\u66F4\u65B0/\u66FF\u6362\u3002` : `\u4F60\u7684\u4EA4\u4ED8\u7269\u76EE\u5F55\u662F \`./deliverable/\`\uFF08\u5F53\u524D\u4E3A\u7A7A\u2014\u2014\u4EA7\u51FA\u9996\u7248\u6210\u54C1\uFF09\u3002`,
1211
- `\u628A**\u6E32\u67D3/\u5BFC\u51FA\u597D\u7684\u6210\u54C1\u6587\u4EF6**\u653E\u8FDB \`deliverable/\`\uFF08\u6309\u4EA4\u4ED8\u8981\u6C42\u7684\u683C\u5F0F\uFF0C\u5982 mp4/wav/png\uFF1B\u5927\u4E8C\u8FDB\u5236\u76F4\u63A5\u653E\uFF0C\u522B\u585E\u8FDB\u6587\u672C\uFF09\uFF0C\u7136\u540E\uFF1A`,
1212
- `\`oasis propose ${artifactId} --from-dir deliverable/ --reason "<\u4E00\u53E5\u8BDD\u7406\u7531>"\` \u2192 \u6574\u4F53\u5B8C\u6574\u4E86\u518D \`oasis conclude ${artifactId}\`\u3002`,
1213
- `**\u522B\u7F16\u9020\uFF1B\u7F3A\u524D\u7F6E\uFF08\u811A\u672C/\u5206\u955C/\u7D20\u6750\u6CA1\u5C31\u7EEA\uFF09\u6309\u751F\u4EA7\u8005\u5B88\u5219\u8D70 oasis gap**\u3002`
1214
- ];
1215
- },
1216
- integrateInstructions(ctx) {
1217
- const parts = ctx.queuedPartLabels.map((p2) => `\`${p2}\``).join("\u3001") || "\uFF08\u961F\u5217\u4E3A\u7A7A\uFF09";
1218
- return [
1219
- `\u5404\u90E8\u4EF6\u7684\u6210\u54C1\u5728 \`parts/<\u540D>/\`\uFF08${parts}\uFF09\u3002\u628A\u5B83\u4EEC\u6C47\u6210**\u6700\u7EC8\u6210\u54C1**\u653E\u8FDB \`deliverable/\`\uFF08\u6309\u4EA4\u4ED8\u683C\u5F0F\u3001\u987A\u5E8F/\u62FC\u63A5\u6B63\u786E\uFF09\uFF0C\u7136\u540E\uFF1A`,
1220
- `\`oasis integrate ${ctx.artifactId} --from-dir deliverable/ --reason "<\u96C6\u6210\u8BF4\u660E>"\` \u2192 \`oasis conclude ${ctx.artifactId}\`\u3002**\u522B\u4E22\u4EFB\u4F55\u90E8\u4EF6**\u3002`
1221
- ];
1222
- },
1223
- submitInstruction: deliverableSubmit,
1224
- collectionStrategy: { kind: "inline" },
1225
- mergeMode: "oasis-queue"
1226
- };
1227
- }
1228
- });
1229
-
1230
1259
  // ../core/src/diff.ts
1231
1260
  function lineDiff(oldText, newText, capLines = 60) {
1232
1261
  const a = oldText.split("\n");
@@ -1344,9 +1373,11 @@ var init_op_payloads = __esm({
1344
1373
  });
1345
1374
 
1346
1375
  // ../contract/src/artifact.ts
1376
+ var DELIVERY_SUMMARY_ARTIFACT_TYPE;
1347
1377
  var init_artifact = __esm({
1348
1378
  "../contract/src/artifact.ts"() {
1349
1379
  "use strict";
1380
+ DELIVERY_SUMMARY_ARTIFACT_TYPE = "delivery-summary";
1350
1381
  }
1351
1382
  });
1352
1383
 
@@ -2044,6 +2075,7 @@ var init_kernel = __esm({
2044
2075
  // §6.1/§6.3:工作流节点(auto/prereq/seed)默认 persistent;随手 scratch 默认 temp
2045
2076
  persistence: args.persistence ?? (createdVia === "manual-spawn" ? "temp" : "persistent"),
2046
2077
  createdVia,
2078
+ ...args.isFinalOutput !== void 0 ? { isFinalOutput: args.isFinalOutput } : {},
2047
2079
  inputs,
2048
2080
  ...args.title !== void 0 ? { title: args.title } : {},
2049
2081
  ...args.description !== void 0 ? { description: args.description } : {},
@@ -2069,7 +2101,8 @@ var init_kernel = __esm({
2069
2101
  }
2070
2102
  /** routing 兜底(pending → 实派)与改派 */
2071
2103
  async assignOwner(args) {
2072
- this.artifactOrThrow(args.artifactId);
2104
+ const artifact = this.artifactOrThrow(args.artifactId);
2105
+ this.assertDeliverySummaryStructureMutable([artifact.id], args.actor, "\u6539\u6D3E\u8D1F\u8D23\u4EBA");
2073
2106
  const payload = { owner: args.owner };
2074
2107
  await this.commit(args.artifactId, args.actor, "assign_owner", args.artifactId, payload);
2075
2108
  }
@@ -2232,6 +2265,28 @@ var init_kernel = __esm({
2232
2265
  landed.push(head);
2233
2266
  }
2234
2267
  }
2268
+ /**
2269
+ * 冻结孤儿自愈(ADR 0069):把「本该自动落地却撞冻结没落」的 fresh 队首补落地。
2270
+ * 每落一条都对 **live model** 重算 `advanceableHead`(staleness 用落完上一条后的**新** head)——
2271
+ * 链式(Z.base=刚落的 X)顺序落完;竞争(Z.base=旧 head)落第一条后转 stale 自动停,不盲连落。
2272
+ * 尽力而为:撞显式冻结(`this.frozen`,纯投影看不见)或落地异常即停,留下次解冻 / serve 60s 循环兜底。
2273
+ * 钩子(`submitReview` 评审解冻后)与 serve 政策循环共用此方法。actor 记为 `actor:system`(系统协调动作)。
2274
+ */
2275
+ async advanceOrphans(artifactId) {
2276
+ const landed = [];
2277
+ for (; ; ) {
2278
+ if (this.frozen.has(artifactId)) break;
2279
+ const rev = advanceableHead(this.model, this.schema, artifactId);
2280
+ if (!rev) break;
2281
+ try {
2282
+ await this.landHead(artifactId, SYSTEM_ACTOR, rev);
2283
+ landed.push(rev);
2284
+ } catch {
2285
+ break;
2286
+ }
2287
+ }
2288
+ return landed;
2289
+ }
2235
2290
  /**
2236
2291
  * group-commit(spec §5.5):队首 stale + 检查贵 + 多条竞争 → owner 把**整队 drain** 进一次集成。
2237
2292
  * batch = 全部排队项(含排在 stale 队首之后的 fresh 项——严格 FIFO 下它们反正动不了,
@@ -2441,6 +2496,7 @@ var init_kernel = __esm({
2441
2496
  await this.commit(args.artifactId, args.actor, "submit_review", attempt.head, payload, void 0, args.hand);
2442
2497
  const status = attemptStatus(this.model, attempt);
2443
2498
  if (status === "satisfied") await this.fireTriggers(args.artifactId);
2499
+ if (status !== "pending") await this.advanceOrphans(args.artifactId);
2444
2500
  return { status };
2445
2501
  }
2446
2502
  async fireTriggers(artifactId) {
@@ -2757,6 +2813,7 @@ var init_kernel = __esm({
2757
2813
  this.ensureActive(args.artifactId);
2758
2814
  const artifact = this.artifactOrThrow(args.artifactId);
2759
2815
  this.artifactOrThrow(args.to);
2816
+ this.assertDeliverySummaryStructureMutable([args.artifactId, args.to], args.actor, "\u4FEE\u6539\u8FDE\u7EBF");
2760
2817
  if (args.to === args.artifactId) throw new KernelError("\u4E0D\u80FD\u4F9D\u8D56\u81EA\u5DF1");
2761
2818
  if (artifact.inputs.some((e) => e.to === args.to)) {
2762
2819
  throw new KernelError(`\u4F9D\u8D56\u8FB9\u5DF2\u5B58\u5728: ${args.artifactId} \u2192 ${args.to}`);
@@ -2829,7 +2886,7 @@ var init_kernel = __esm({
2829
2886
  const keep = args.preserveEdgesAmong;
2830
2887
  for (const edge of this.edgesTouching(args.artifactId)) {
2831
2888
  if (keep?.has(edge.artifactId) && keep.has(edge.to)) continue;
2832
- await this.unlinkInput({ artifactId: edge.artifactId, to: edge.to, actor: args.actor });
2889
+ await this.unlinkInputUnchecked({ artifactId: edge.artifactId, to: edge.to, actor: args.actor });
2833
2890
  }
2834
2891
  }
2835
2892
  const payload = {
@@ -2924,6 +2981,11 @@ var init_kernel = __esm({
2924
2981
  }
2925
2982
  /** 治理断边(§11.3/§11.6:边即可见性拓扑——产线用法应经 Intervention 获得 preview) */
2926
2983
  async unlinkInput(args) {
2984
+ this.assertDeliverySummaryStructureMutable([args.artifactId, args.to], args.actor, "\u4FEE\u6539\u8FDE\u7EBF");
2985
+ await this.unlinkInputUnchecked(args);
2986
+ }
2987
+ /** cancel 级联仍须能机械断边;这条内部路径保留原调用者审计身份,但不开放给人工命令。 */
2988
+ async unlinkInputUnchecked(args) {
2927
2989
  this.ensureActive(args.artifactId);
2928
2990
  const artifact = this.artifactOrThrow(args.artifactId);
2929
2991
  if (!artifact.inputs.some((e) => e.to === args.to)) {
@@ -2932,6 +2994,25 @@ var init_kernel = __esm({
2932
2994
  const payload = { to: args.to };
2933
2995
  await this.commit(args.artifactId, args.actor, "unlink_input", args.artifactId, payload);
2934
2996
  }
2997
+ assertDeliverySummaryStructureMutable(ids, actor, action) {
2998
+ if (actor === SYSTEM_ACTOR) return;
2999
+ const summary = ids.map((id) => this.model.artifacts.get(id)).find((a) => a?.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
3000
+ if (!summary) return;
3001
+ throw new KernelError(
3002
+ `\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u4EBA\u5DE5${action}\uFF08${summary.id}\uFF09\u3002\u8BF7\u4FEE\u6539\u5176\u5B83\u4E1A\u52A1\u8282\u70B9\u5E76\u91CD\u65B0\u89C4\u5212\u672B\u7AEF\u95ED\u73AF\u3002`
3003
+ );
3004
+ }
3005
+ /** 已落图的交付汇总只允许系统改结构;建单 plan 中首次原子 spawn 不受影响。 */
3006
+ assertDeliverySummaryIntervention(plan, actor) {
3007
+ if (actor === SYSTEM_ACTOR) return;
3008
+ for (const op of plan.ops) {
3009
+ if (op.action === "edit" || op.action === "assign") {
3010
+ this.assertDeliverySummaryStructureMutable([op.artifactId], actor, op.action === "edit" ? "\u7F16\u8F91\u8282\u70B9\u5B9A\u4E49" : "\u6539\u6D3E\u8D1F\u8D23\u4EBA");
3011
+ } else if (op.action === "link" || op.action === "unlink") {
3012
+ this.assertDeliverySummaryStructureMutable([op.artifactId, op.to], actor, "\u4FEE\u6539\u8FDE\u7EBF");
3013
+ }
3014
+ }
3015
+ }
2935
3016
  // ── Intervention(§11.7):结构变更的原子单位 ────────────────────────────
2936
3017
  /**
2937
3018
  * 预检(②终态校验 + ③advisory + ④爆炸半径)。preview 白送:
@@ -3127,6 +3208,7 @@ ${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192
3127
3208
  */
3128
3209
  async applyIntervention(plan, baseSeqs, actor) {
3129
3210
  plan = this.resolveInterventionRefs(plan);
3211
+ this.assertDeliverySummaryIntervention(plan, actor);
3130
3212
  const { changeClass, nonMonotonicOps } = classifyIntervention(plan);
3131
3213
  if (changeClass === "C" && !isHumanActor(actor)) {
3132
3214
  throw new KernelError(
@@ -3226,6 +3308,7 @@ ${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192
3226
3308
  workspace: op.workspace ?? "ws:default",
3227
3309
  persistence: "persistent",
3228
3310
  createdVia: isPrereqFill(spawnId) ? "prereq" : "manual-spawn",
3311
+ ...op.isFinalOutput !== void 0 ? { isFinalOutput: op.isFinalOutput } : {},
3229
3312
  ...op.title !== void 0 ? { title: op.title } : {},
3230
3313
  ...op.description !== void 0 ? { description: op.description } : {},
3231
3314
  ...op.docType !== void 0 ? { docType: op.docType } : {},
@@ -3371,9 +3454,9 @@ ${res.candidates.map((c) => ` - ${c.type}:${c.title ?? "(\u65E0title)"} \u2192
3371
3454
  /** sealed = 不再接受变更(§7.1);annotation / reject 不在此列(结案后补注与清理仍允许) */
3372
3455
  ensureActive(artifactId) {
3373
3456
  const lifecycle = lifecycleOf(this.model, artifactId);
3374
- if (lifecycle !== "active") {
3375
- throw new KernelError(`artifact is ${lifecycle}, no further changes: ${artifactId}`);
3376
- }
3457
+ if (lifecycle === "active") return;
3458
+ const why = lifecycle === "sealed:frozen" ? "\u6B64\u8282\u70B9\u5DF2\u88AB\u51BB\u7ED3\uFF08sealed:frozen\uFF09\uFF1A\u4FDD\u7559\u73B0\u6709\u4EA7\u51FA\u3001\u6682\u505C\u4E00\u5207\u6539\u52A8\uFF0C\u4E0D\u63A5\u53D7\u4EFB\u4F55\u63D0\u4EA4\u6216\u4FEE\u6539\u3002\u8981\u7EE7\u7EED\u987B\u7531\u4EBA\u5728\u6539\u56FE\u91CC reopen \u89E3\u51BB\uFF1B\u4F60\u82E5\u662F agent\u3001\u786E\u6709\u5FC5\u8981\u6539\uFF0C\u8BF7\u63D0 gap \u8BF4\u660E\u8BC9\u6C42" : lifecycle === "sealed:cancelled" ? "\u6B64\u8282\u70B9\u5DF2\u88AB\u53D6\u6D88\u5E9F\u5F03\uFF08sealed:cancelled\uFF09\uFF1A\u4F9D\u8D56\u8FB9\u5DF2\u65AD\uFF0C\u4E0D\u518D\u63A5\u53D7\u4EFB\u4F55\u4FEE\u6539" : lifecycle === "sealed:accepted" ? "\u6B64\u8282\u70B9\u5DF2\u9A8C\u6536\u7ED3\u6848\uFF08sealed:accepted\uFF09\uFF1A\u4E0D\u518D\u63A5\u53D7\u4FEE\u6539\uFF1B\u786E\u9700\u6539\u52A8\u8BF7\u5148 reopen" : `artifact is ${lifecycle}, no further changes`;
3459
+ throw new KernelError(`${why}: ${artifactId}`);
3377
3460
  }
3378
3461
  artifactOrThrow(id) {
3379
3462
  const artifact = this.model.artifacts.get(id);
@@ -3842,6 +3925,37 @@ async function assembleContext(args) {
3842
3925
  ...partSpec?.deps && partSpec.deps.length > 0 ? [`- \u4F60\u4F9D\u8D56\u7684\u5144\u5F1F\u90E8\u4EF6\uFF08\u5DF2\u5148\u4EA7\u51FA\uFF0C\u89C1\u90BB\u57DF/\u5F53\u524D\u5185\u5BB9\uFF09\uFF1A${partSpec.deps.join("\u3001")}`] : [],
3843
3926
  `- \u4E25\u5B88\u4E0A\u6E38\u7EA6\u5B9A\u7684\u63A5\u53E3/\u547D\u540D\uFF0C\u5404\u90E8\u4EF6\u624D\u62FC\u5F97\u8D77\u6765\u3002\u96C6\u6210\u7531\u7CFB\u7EDF\u5728\u5404\u90E8\u4EF6\u9F50\u4E86\u540E\u6536\u53E3\uFF0C**\u4E0D\u5F52\u4F60\u505A**\u3002`
3844
3927
  ] : [],
3928
+ // integrate:把「当初为什么这么切」铺给收口人。此前它只拿到部件**名字**(handler 的 queuedPartLabels)
3929
+ // 与各部件的产出内容,看不到 spec——而边界("只改你名下目录 X")、契约("面向契约类型编程、测试用 fake")、
3930
+ // 逐条交付清单全在 `add_part.payload.spec` 里(实测每条上千字)。少了它,integrate 只能看着几坨产出猜:
3931
+ // 某块是不是越界改了别人的文件、合起来 typecheck 红是预期内还是真 bug、某块是不是压根没做完。
3932
+ // 这些是 owner 当初**外化**过的决策依据,本该每轮递给它——不该靠续会话去继承(best-effort、换节点即失效、
3933
+ // 冷启动 runtime 不支持,且 whole produce 那轮结束时各部件**尚未动工**,续过来反而是陈旧心智)。
3934
+ // 守卫 queuedParts.length:队列为空不是真正的 integrate 场景(computeIntegrateJobs 只在有排队产出时派发),
3935
+ // 此时把所有已声明部件列成「缺块」既无意义又污染 TASK.md。
3936
+ ...integrating && queuedParts.length > 0 ? (() => {
3937
+ const defined = artifact.parts ?? [];
3938
+ const queuedNames = new Set(queuedParts.map((p2) => p2.part));
3939
+ const missing = defined.filter((d) => !queuedNames.has(d.name));
3940
+ const lines = [``, `**\u672C\u4EA7\u7269\u5F53\u521D\u88AB\u5E76\u884C\u5206\u89E3\u6210 ${defined.length || queuedParts.length} \u4E2A\u90E8\u4EF6\uFF0C\u4F60\u73B0\u5728\u8981\u628A\u5B83\u4EEC\u5408\u6210\u5B8C\u6574\u7684\u4E00\u7248\u3002\u5F53\u521D\u6BCF\u5757\u662F\u8FD9\u4E48\u5207\u7684\uFF1A**`];
3941
+ for (const q of queuedParts) {
3942
+ const def = defined.find((d) => d.name === q.part);
3943
+ lines.push(``, `### \u90E8\u4EF6 \`${q.part}\`${def?.deps?.length ? `\uFF08\u4F9D\u8D56\uFF1A${def.deps.join("\u3001")}\uFF09` : ""}`);
3944
+ lines.push(`- \u5185\u5BB9\u5728 \`parts/${slug2(q.part)}/\``);
3945
+ lines.push(
3946
+ def?.spec ? `- \u5F53\u521D\u8981\u6C42\u5B83\u4EA7\u51FA\uFF1A
3947
+ ${def.spec.split("\n").map((l) => ` > ${l}`).join("\n")}` : `- \uFF08\u65E0 spec \u8BB0\u5F55${def ? "" : "\uFF1B\u8BE5 revision \u672A\u6807 part\uFF0C\u6309\u5185\u5BB9\u81EA\u884C\u5224\u65AD"}\uFF09`
3948
+ );
3949
+ }
3950
+ for (const m2 of missing) {
3951
+ lines.push(``, `### \u26A0 \u90E8\u4EF6 \`${m2.name}\` \u2014\u2014 **\u5B9A\u4E49\u4E86\u4F46\u6CA1\u6709\u4EA7\u51FA\u6392\u961F**`);
3952
+ if (m2.spec) lines.push(`- \u5F53\u521D\u8981\u6C42\u5B83\u4EA7\u51FA\uFF1A
3953
+ ${m2.spec.split("\n").map((l) => ` > ${l}`).join("\n")}`);
3954
+ lines.push(`- **\u7F3A\u8FD9\u4E00\u5757**\uFF1A\u5148\u5224\u65AD\u80FD\u4E0D\u80FD\u5408\uFF1B\u4E0D\u80FD\u5C31\u522B\u786C\u5408\uFF0C\`oasis add-revision\` \u628A\u5B83\u6D3E\u51FA\u53BB\u8865\u3002`);
3955
+ }
3956
+ lines.push(``, `\u5408\u5E76\u65F6\u6309\u5404\u5757\u5F53\u521D\u7EA6\u5B9A\u7684\u8FB9\u754C\u4E0E\u63A5\u53E3\u5BF9\u9F50\uFF1B\u67D0\u5757\u6CA1\u505A\u5230\u5B83 spec \u8981\u6C42\u7684\uFF0C\u4F60\u53EF\u4EE5 \`oasis add-revision\` \u6253\u56DE\u53BB\u8865\uFF0C\u4E0D\u5FC5\u81EA\u5DF1\u66FF\u5B83\u5199\u3002`);
3957
+ return lines;
3958
+ })() : [],
3845
3959
  ``,
3846
3960
  `## \u600E\u4E48\u63D0\u4EA4`,
3847
3961
  ``,
@@ -3867,17 +3981,15 @@ async function assembleContext(args) {
3867
3981
  })
3868
3982
  ];
3869
3983
  })() : canvasMode ? [
3870
- `\u4F60\u8D1F\u8D23 **design \u4EA7\u7269 \`${artifactId}\`**\uFF0C\u8FD0\u884C\u5728**\u901A\u7528\u8FD0\u884C\u65F6**\u4E0A\u2014\u2014\u4F60\u662F**\u7532\u65B9\u8BBE\u8BA1\u8D1F\u8D23\u4EBA**\uFF0C\u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8**\u534F\u4F5C\u753B\u677F\u5F15\u64CE**\u6253\u78E8\u754C\u9762\u7A3F\uFF0C\u6EE1\u610F\u4E86\u624D\u4EA4\u4ED8\uFF1A`,
3984
+ // 内核只出「你产什么 + 内核权威锚点」——**设计工作流散文全在你的人设(identity/AGENTS.md「产出流程」)**,
3985
+ // 那是单一真源、改它=改配置不发版(提案 design-conversational-driving 治本,2026-07-17)。这里不重复正文,
3986
+ // 只钉住内核唯一不能漂的两件事:真实 artifactId、缺前置的逃生口 gap。
3987
+ `\u4F60\u8D1F\u8D23 **design \u4EA7\u7269 \`${artifactId}\`**\uFF0C\u8FD0\u884C\u5728**\u901A\u7528\u8FD0\u884C\u65F6**\u4E0A\u2014\u2014\u4F60\u662F\u7532\u65B9\u8BBE\u8BA1\u8D1F\u8D23\u4EBA\uFF0C\u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8**\u534F\u4F5C\u753B\u677F\u5F15\u64CE**\u6253\u78E8\u754C\u9762\u7A3F\u3002`,
3871
3988
  ``,
3872
- ` oasis design chat ${artifactId} --message "<\u8FD9\u4E00\u8F6E\u8981\u5F15\u64CE\u505A\u4EC0\u4E48\uFF1A\u8BBE\u8BA1\u9700\u6C42 + \u771F\u5B9E\u6570\u636E>"`,
3873
- ` oasis design chat ${artifactId} --turn continue --message "<\u8BFB\u5B8C\u4E0A\u4E00\u7248\u4EA7\u7269\u540E\uFF0C\u8981\u6539\u54EA\u91CC>"`,
3989
+ `**\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF08\u600E\u4E48\u53D6\u8D27/\u8D77\u8F6E/\u8BFB\u4EA7\u7269/\u4EA4\u7A3F/\u522B\u624B\u6539\u677F\u6587\u4EF6\u2026\uFF09\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\` \u7684\u300C\u4EA7\u51FA\u6D41\u7A0B\uFF08design \u4EA7\u7269\uFF09\u300D**\u2014\u2014\u628A\u91CC\u9762 \`<artifactId>\` \u90FD\u6362\u6210\u672C\u4EA7\u7269 \`${artifactId}\`\u3002`,
3874
3990
  ``,
3875
- `**\u6BCF\u4E00\u8F6E\u7684\u95ED\u73AF\uFF08\u522B\u8DF3\u6B65\uFF09**\uFF1A`,
3876
- `1. **\u5582\u9971\u5B83**\uFF1A\u9996\u8F6E \`--message\` \u628A\u8BBE\u8BA1\u9700\u6C42\u8FDE\u540C**\u771F\u5B9E\u793A\u4F8B\u6570\u636E**\u4E00\u8D77\u7ED9\u8DB3\u2014\u2014\u5F15\u64CE\u53EA\u62FF\u5230 \`DESIGN_BRIEF.md\`/\`inputs/\`\uFF0CPRD \u91CC\u6CA1\u6709\u7684 mock \u6570\u636E\u5B83\u4F1A**\u81EA\u5DF1\u7F16**\uFF08\u66FE\u628A\u5A74\u513F\u770B\u62A4\u5668\u7F16\u6210\u8033\u673A\uFF09\u3002\u771F\u5B9E\u4E1A\u52A1\u6570\u636E\uFF08\u4EA7\u54C1/\u4E70\u5BB6/\u8BA2\u5355\u7B49\uFF09\u5728\u54EA\u3001\u957F\u4EC0\u4E48\u6837\uFF0C\u4F60**\u5FC5\u987B\u5728 message \u91CC\u5199\u6E05\u6216\u544A\u8BC9\u5B83\u53BB\u54EA\u53D6**\uFF1B`,
3877
- `2. **\u8BFB\u4EA7\u7269**\uFF1A\u547D\u4EE4\u8DD1\u5B8C\u4F1A\u6253\u5370\u672C\u8F6E\u4EA4\u4ED8\u7684 artifact \u6E05\u5355\uFF08\`index.html\` / \`acceptance.md\` \u53CA status\uFF09\u2014\u2014**\u6253\u5F00 \`index.html\` \u771F\u8BFB\u4E00\u904D**\uFF0C\u5BF9\u7167 \`DESIGN_BRIEF.md\` \u4E0E \`inputs/\` \u6838\uFF1A\u6570\u636E\u5BF9\u4E0D\u5BF9\uFF1F\u72B6\u6001/\u8FB9\u754C\u6001\u5168\u4E0D\u5168\uFF1F\u6709\u6CA1\u6709\u7F16\u9020\uFF1F`,
3878
- `3. **\u51B3\u5B9A**\uFF1A\u591F\u597D \u2192 \`oasis propose ${artifactId} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\` \u4EA4\u7A3F\uFF1B\u8FD8\u5DEE \u2192 \`oasis design chat ${artifactId} --turn continue --message "<\u5177\u4F53\u6539\u4EC0\u4E48>"\` **\u518D\u4E00\u8F6E**\u6253\u78E8\uFF1B\u9047\u5230**\u4F60\u4E5F\u5B9A\u4E0D\u4E86\u7684\u53D6\u820D**\uFF08\u5982\u67D0\u6001\u7528\u54EA\u4E2A\u4E70\u5BB6\u627F\u8F7D\uFF09\u2192 \u5199\u8FDB acceptance.md \u7684\u300C\u5F00\u653E\u95EE\u9898\u300D\uFF0C\u6216 \`oasis gap ${artifactId} --desc "<\u8981\u4EBA\u5B9A\u4EC0\u4E48>"\` \u5192\u6CE1\u7ED9\u4EBA\uFF1B`,
3879
- `- **\u522B\u628A\u7B2C\u4E00\u7248\u76F2\u76EE propose**\u2014\u2014\u5148\u8BFB\u3001\u5148\u81EA\u68C0\u3001\u4E0D\u591F\u5C31\u518D\u4E00\u8F6E\uFF0C\u6BD4 propose \u51FA\u53BB\u88AB\u4E0B\u6E38\u6253\u56DE\u3001\u7ED5\u6574\u5F20\u56FE\u4E00\u5708\u4FBF\u5B9C\u5F97\u591A\uFF1B`,
3880
- `- \u5F15\u64CE\u4E00\u8F6E\u8981 13\u201344 \u5206\u949F\uFF08\u771F\u5728\u753B\u5E03\u4E0A\u753B\uFF09\uFF0C\`oasis design chat\` \u5185\u90E8\u5DF2\u8F6E\u8BE2 30 \u5206\u949F\u3002**\u522B\u7528 \`timeout\` \u5305\u5B83\u3001\u522B 15 \u5206\u949F\u5C31\u5F53\u5B83\u5931\u8D25**\u2014\u2014\u8010\u5FC3\u7B49\u5B83\u8DD1\u5B8C\uFF1B`,
3991
+ `\u5185\u6838\u53EA\u5728\u6B64\u9489\u4E24\u6761\u4E0D\u53EF\u6F02\u7684\uFF1A`,
3992
+ `- **\u4EA4\u7A3F\u8D70 \`oasis propose ${artifactId} --from-dir <\u76EE\u5F55> --reason "\u2026"\`**\uFF08proposed \u624D\u8FDB\u7248\u672C\u53F2\u3001\u4E0B\u6E38\u624D\u62FF\u5F97\u5230\uFF1B\u4E0D propose = head \u4ECD \u2205\uFF09\uFF1B`,
3881
3993
  `- \u7F3A**\u8FD8\u4E0D\u5B58\u5728\u7684\u4E0A\u6E38\u524D\u7F6E**\u505A\u4E0D\u4E0B\u53BB \u2192 \`oasis gap ${artifactId} --desc "<\u7F3A\u4EC0\u4E48>"\` \u62A5\u7F3A\u53E3\u540E\u6536\u5DE5\u3002`
3882
3994
  ] : (() => {
3883
3995
  const codeRepo = args.codeRepo ?? null;
@@ -3933,7 +4045,7 @@ async function assembleContext(args) {
3933
4045
  ``,
3934
4046
  ...canvasMode ? [
3935
4047
  `## \u5173\u4E8E\u5DE5\u5177`,
3936
- `oasis CLI \u5DF2\u6CE8\u5165\uFF08\`OASIS_SERVER\` / \`OASIS_TOKEN\` \u5C31\u7EEA\uFF0C\u65E0\u9700\u914D\u7F6E\uFF09\u2014\u2014\u4F60\u7528 \`oasis design chat\` \u591A\u8F6E\u9A71\u52A8\u753B\u677F\u5F15\u64CE\u6253\u78E8\u754C\u9762\u7A3F\uFF0C\u8BFB\u4EA7\u7269\u540E\u7528 \`oasis propose\` \u4EA4\u7A3F\u3001\`oasis gap\` \u62A5\u7F3A\u53E3\uFF08\u90FD\u89C1\u4E0A\u9762\u300C\u600E\u4E48\u63D0\u4EA4\u300D\uFF09\u3002\u8BBE\u8BA1\u9700\u6C42\u89C1\u672C\u76EE\u5F55 \`DESIGN_BRIEF.md\`\uFF0C\u4E0A\u6E38\u8F93\u5165\u5728 \`inputs/\`\u3002`
4048
+ `oasis CLI \u5DF2\u6CE8\u5165\uFF08\`OASIS_SERVER\` / \`OASIS_TOKEN\` \u5C31\u7EEA\uFF0C\u65E0\u9700\u914D\u7F6E\uFF09\u3002\u8BBE\u8BA1\u5DE5\u4F5C\u6D41\u7528\u54EA\u4E9B\u547D\u4EE4\u3001\u600E\u4E48\u7528\uFF0C\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\` \u7684\u300C\u4EA7\u51FA\u6D41\u7A0B\u300D\u3002\u8BBE\u8BA1\u9700\u6C42\u89C1\u672C\u76EE\u5F55 \`DESIGN_BRIEF.md\`\uFF0C\u4E0A\u6E38\u8F93\u5165\u5728 \`inputs/\`\u3002`
3937
4049
  ] : commandReference({
3938
4050
  artifactId,
3939
4051
  action: args.action ?? "produce",
@@ -3961,14 +4073,14 @@ async function assembleContext(args) {
3961
4073
  (s2) => `- ${s2.upstream}: pin=${s2.pinned ?? "\u2205"} \u2192 \u6700\u65B0 ${s2.latest}${s2.upstreamCancelled ? "\uFF08\u26A0 \u4E0A\u6E38\u5DF2 cancelled\uFF09" : ""}`
3962
4074
  )
3963
4075
  ] : [],
3964
- // 画板模式:你在通用 runtime 上、CLI 齐全(design chat/propose/gap 都能用);引擎里的内层 agent 才没 CLI,那层不用你管
4076
+ // 画板模式:守则正文(甲方多轮/读产物再交/喂真数据/别本地手改板…)在人设 identity/AGENTS.md,此处不重复。
4077
+ // 只留内核锚点:你产哪一个、不归你定的别硬编。你在通用 runtime、CLI 齐全(design chat/propose/gap 都能用)。
3965
4078
  ...canvasMode ? [
3966
4079
  ``,
3967
4080
  `## \u751F\u4EA7\u8005\u5B88\u5219\uFF08\u753B\u677F\u7248\uFF09`,
3968
- `- \u4F60\u662F\u7532\u65B9\uFF1A\u591A\u8F6E \`oasis design chat\` \u9A71\u52A8\u5F15\u64CE\uFF0C\u6BCF\u8F6E**\u8BFB\u4EA7\u7269**\u518D\u51B3\u5B9A\u2014\u2014\u591F\u597D\u624D \`oasis propose\`\uFF0C\u522B\u628A\u7B2C\u4E00\u7248\u76F2\u76EE\u4EA4\u51FA\u53BB\u3002`,
4081
+ `- **\u5B8C\u6574\u8BBE\u8BA1\u5DE5\u4F5C\u6D41\u5B88\u5219\u89C1\u4F60\u7684\u4EBA\u8BBE \`identity/AGENTS.md\`\u300C\u4EA7\u51FA\u6D41\u7A0B\u300D**\uFF08\u600E\u4E48\u53D6\u8D27/\u8D77\u8F6E/\u8BFB\u4EA7\u7269/\u4EA4\u7A3F/\u522B\u624B\u6539\u677F\u6587\u4EF6\u7B49\uFF09\u3002`,
3969
4082
  `- \u4F60\u53EA\u4EA7**\u8FD9\u4E00\u4E2A**\u8BBE\u8BA1\u4EA7\u7269\uFF1B**\u4E0D\u5F52\u4F60\u5B9A\u7684\u51B3\u7B56\u522B\u786C\u7F16**\uFF08\u4F1A\u88AB\u6253\u56DE\uFF09\u2014\u2014\u62FF\u4E0D\u51C6\u7684\u53D6\u820D\u5199\u8FDB acceptance.md \u7684\u300C\u5F00\u653E\u95EE\u9898\u300D\uFF0C\u6216 \`oasis gap\` \u5192\u6CE1\u7ED9\u4EBA\u3002`,
3970
4083
  `- \u5FE0\u5B9E\u4E8E task-context/ \u91CC\u7684\u4E0A\u6E38\u8F93\u5165\uFF08PRD/\u603B\u7EB2\uFF09\uFF1B\u4E0E\u4E0A\u6E38\u51B2\u7A81\u5904\u4EE5\u4E0A\u6E38\u4E3A\u51C6\uFF0C\u5E76\u5728\u5F00\u653E\u95EE\u9898\u91CC\u6807\u6CE8\u5F02\u8BAE\u3002`,
3971
- `- \u771F\u5B9E\u793A\u4F8B\u6570\u636E\uFF08\u4EA7\u54C1/\u4E70\u5BB6/\u8BA2\u5355\u7B49\uFF09**\u5FC5\u987B\u5728 \`--message\` \u91CC\u5582\u7ED9\u5F15\u64CE**\u2014\u2014\u5B83\u62FF\u4E0D\u5230\u7684\u4E1C\u897F\u4F1A\u81EA\u5DF1\u7F16\uFF0C\u522B\u8BA9\u5B83\u66FF\u4F60\u9020\u6570\u636E\u3002`,
3972
4084
  `- \u754C\u9762\u7A3F\u5FC5\u987B**\u771F\u5B9E\u53EF\u6253\u5F00**\uFF08\u81EA\u5305\u542B HTML\uFF09\uFF0C\u522B\u4EA4\u793A\u610F\u63CF\u8FF0\u3002`
3973
4085
  ] : [],
3974
4086
  // review 会话不产出、不该给生产者守则(它只投票,守则的 propose/conclude/gap/add-revision 全不适用)
@@ -135279,6 +135391,100 @@ var init_workorder_drafts = __esm({
135279
135391
  });
135280
135392
 
135281
135393
  // ../server/src/governance/draft-edit.ts
135394
+ function assertNotDeliverySummary(draft, ids, action) {
135395
+ const summary = ids.map((id) => spawnById(draft, id)).find((o) => o?.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
135396
+ if (!summary) return;
135397
+ throw new Error(`\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u4EBA\u5DE5${action}\uFF1B\u8BF7\u4FEE\u6539\u5176\u5B83\u4E1A\u52A1\u8282\u70B9\uFF0C\u7CFB\u7EDF\u4F1A\u91CD\u65B0\u5224\u65AD\u672B\u7AEF\u95ED\u73AF\u3002`);
135398
+ }
135399
+ function reconcileDeliverySummaryPlan(draft) {
135400
+ const summaries = spawnOps(draft).filter((op) => op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
135401
+ const business = spawnOps(draft).filter((op) => op.type !== "brief" && op.type !== DELIVERY_SUMMARY_ARTIFACT_TYPE);
135402
+ const businessIds = new Set(business.map((op) => op.id));
135403
+ const consumed = /* @__PURE__ */ new Set();
135404
+ for (const op of business) {
135405
+ for (const edge of op.inputs ?? []) if (businessIds.has(edge.to)) consumed.add(edge.to);
135406
+ }
135407
+ for (const op of draft.plan.ops) {
135408
+ if (op.action === "link" && businessIds.has(op.artifactId) && businessIds.has(op.to)) consumed.add(op.to);
135409
+ }
135410
+ const terminals = business.filter((op) => !consumed.has(op.id));
135411
+ const finals = business.filter((op) => op.isFinalOutput === true);
135412
+ const directFinal = finals.length === 1 && terminals.length === 1 && finals[0].id === terminals[0].id;
135413
+ if (business.length === 0 || directFinal) {
135414
+ const summaryIds2 = new Set(summaries.map((op) => op.id));
135415
+ draft.plan.ops = draft.plan.ops.filter((op) => {
135416
+ if (op.action === "spawn" && op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE) return false;
135417
+ if (op.action === "link" || op.action === "unlink") return !summaryIds2.has(op.artifactId) && !summaryIds2.has(op.to);
135418
+ if (op.action === "annotate" || op.action === "edit" || op.action === "assign") return !summaryIds2.has(op.artifactId);
135419
+ return true;
135420
+ });
135421
+ return;
135422
+ }
135423
+ const existing = summaries[0];
135424
+ const used = new Set(spawnOps(draft).map((op) => op.id));
135425
+ const brief = spawnOps(draft).find((op) => op.type === "brief");
135426
+ const manager = typeof brief?.fields?.["manager"] === "string" ? brief.fields["manager"] : void 0;
135427
+ const owner = existing?.owner ?? manager ?? (draft.draftedBy.startsWith("actor:agent:") ? draft.draftedBy : PENDING_UNASSIGNED);
135428
+ const summary = {
135429
+ action: "spawn",
135430
+ id: existing?.id ?? deriveArtifactId(DELIVERY_SUMMARY_ARTIFACT_TYPE, draft.workspace, "\u4EA4\u4ED8\u6C47\u603B", used),
135431
+ type: DELIVERY_SUMMARY_ARTIFACT_TYPE,
135432
+ workspace: draft.workspace,
135433
+ owner,
135434
+ title: "\u4EA4\u4ED8\u6C47\u603B",
135435
+ description: existing?.description ?? SUMMARY_TASK_BOOK,
135436
+ inputs: terminals.map((op) => ({ to: op.id, required: true })),
135437
+ isFinalOutput: true
135438
+ };
135439
+ const summaryIds = new Set(summaries.map((op) => op.id));
135440
+ draft.plan.ops = draft.plan.ops.filter((op) => {
135441
+ if (op.action === "spawn" && op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE) return false;
135442
+ if (op.action === "link" || op.action === "unlink") return !summaryIds.has(op.artifactId) && !summaryIds.has(op.to);
135443
+ if (op.action === "annotate" || op.action === "edit" || op.action === "assign") return !summaryIds.has(op.artifactId);
135444
+ return true;
135445
+ });
135446
+ draft.plan.ops.push(summary);
135447
+ }
135448
+ function assertDeliverySummaryPlanIntegrity(submitted, trusted) {
135449
+ const summaries = (plan) => (plan?.ops ?? []).filter((op) => op.action === "spawn" && op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
135450
+ const actual = summaries(submitted);
135451
+ const expected = summaries(trusted);
135452
+ if (!trusted && actual.length === 0) return;
135453
+ if (!trusted) throw new Error("\u4EA4\u4ED8\u6C47\u603B\u5FC5\u987B\u6765\u81EA\u670D\u52A1\u7AEF\u751F\u6210\u7684\u5EFA\u5355\u8349\u6848\uFF0C\u4E0D\u80FD\u7531\u5BA2\u6237\u7AEF\u81EA\u884C\u521B\u5EFA\u3002");
135454
+ if (actual.length !== expected.length) throw new Error("\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u5220\u9664\u6216\u65B0\u589E\u3002");
135455
+ const protectedShape = (op) => ({
135456
+ id: op.id,
135457
+ type: op.type,
135458
+ owner: op.owner,
135459
+ workspace: op.workspace,
135460
+ title: op.title,
135461
+ description: op.description,
135462
+ fields: op.fields,
135463
+ inputs: op.inputs ?? [],
135464
+ isFinalOutput: op.isFinalOutput
135465
+ });
135466
+ for (const expectedSummary of expected) {
135467
+ const actualSummary = actual.find((op) => op.id === expectedSummary.id);
135468
+ if (!actualSummary || JSON.stringify(protectedShape(actualSummary)) !== JSON.stringify(protectedShape(expectedSummary))) {
135469
+ throw new Error("\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7684\u8282\u70B9\u3001\u8D1F\u8D23\u4EBA\u3001\u4EFB\u52A1\u4E66\u548C\u8FDE\u7EBF\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u4EBA\u5DE5\u4FEE\u6539\u3002");
135470
+ }
135471
+ const protectedIds = /* @__PURE__ */ new Set([expectedSummary.id]);
135472
+ for (const op of submitted.ops) {
135473
+ if ((op.action === "edit" || op.action === "assign") && protectedIds.has(op.artifactId)) {
135474
+ throw new Error("\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u8FFD\u52A0\u4EBA\u5DE5\u7F16\u8F91\u6216\u6539\u6D3E\u64CD\u4F5C\u3002");
135475
+ }
135476
+ if ((op.action === "link" || op.action === "unlink") && (protectedIds.has(op.artifactId) || protectedIds.has(op.to))) {
135477
+ throw new Error("\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7684\u8FDE\u7EBF\u7531\u7CFB\u7EDF\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u8FFD\u52A0\u4EBA\u5DE5\u8FDE\u7EBF\u64CD\u4F5C\u3002");
135478
+ }
135479
+ }
135480
+ }
135481
+ const expectedMarkers = new Map(trusted.ops.filter((op) => op.action === "spawn" && !!op.id).map((op) => [op.id, op.isFinalOutput === true]));
135482
+ for (const op of submitted.ops) {
135483
+ if (op.action === "spawn" && op.id && expectedMarkers.has(op.id) && op.isFinalOutput === true !== expectedMarkers.get(op.id)) {
135484
+ throw new Error("\u8282\u70B9\u7684\u6700\u7EC8\u7ED3\u679C\u89D2\u8272\u7531\u52A8\u6001\u89C4\u5212\u7EF4\u62A4\uFF0C\u4E0D\u80FD\u4EBA\u5DE5\u4FEE\u6539\u3002");
135485
+ }
135486
+ }
135487
+ }
135282
135488
  function resolveRef(ref2, draft) {
135283
135489
  const res = resolveNodeRef(ref2, candidatesOf(draft));
135284
135490
  if (res.ok) return res.id;
@@ -135289,6 +135495,9 @@ function resolveRef(ref2, draft) {
135289
135495
  function editDraftPlan(draft, op) {
135290
135496
  switch (op.action) {
135291
135497
  case "spawn": {
135498
+ if (op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE) {
135499
+ throw new Error("\u300C\u4EA4\u4ED8\u6C47\u603B\u300D\u7531\u7CFB\u7EDF\u6309\u52A8\u6001\u534F\u4F5C\u56FE\u81EA\u52A8\u521B\u5EFA\uFF0C\u4E0D\u80FD\u624B\u52A8\u65B0\u589E\u3002");
135500
+ }
135292
135501
  const title = op.title?.trim();
135293
135502
  if (!title) throw new Error("\u7ED9\u8349\u6848\u52A0\u8282\u70B9\u8981 --title\uFF08\u636E (type,title) \u6D3E\u751F id\uFF09");
135294
135503
  if (candidatesOf(draft).some((c) => c.type === op.type && (c.title ?? "").toLowerCase() === title.toLowerCase()))
@@ -135310,6 +135519,7 @@ function editDraftPlan(draft, op) {
135310
135519
  }
135311
135520
  case "edit": {
135312
135521
  const id = resolveRef(op.artifactId, draft);
135522
+ assertNotDeliverySummary(draft, [id], "\u7F16\u8F91\u8282\u70B9\u5B9A\u4E49");
135313
135523
  const target = spawnOps(draft).find((o) => o.id === id);
135314
135524
  if (!target) throw new Error(`\u8349\u6848\u91CC\u6CA1\u6709\u8282\u70B9 ${id} \u7684 spawn`);
135315
135525
  if (op.title !== void 0) target.title = op.title;
@@ -135320,12 +135530,14 @@ function editDraftPlan(draft, op) {
135320
135530
  case "link": {
135321
135531
  const consumer = resolveRef(op.artifactId, draft);
135322
135532
  const upstream = resolveRef(op.to, draft);
135533
+ assertNotDeliverySummary(draft, [consumer, upstream], "\u4FEE\u6539\u8FDE\u7EBF");
135323
135534
  draft.plan.ops.push({ action: "link", artifactId: consumer, to: upstream, ...op.required !== void 0 ? { required: op.required } : {} });
135324
135535
  break;
135325
135536
  }
135326
135537
  case "unlink": {
135327
135538
  const consumer = resolveRef(op.artifactId, draft);
135328
135539
  const upstream = resolveRef(op.to, draft);
135540
+ assertNotDeliverySummary(draft, [consumer, upstream], "\u4FEE\u6539\u8FDE\u7EBF");
135329
135541
  draft.plan.ops = draft.plan.ops.filter((o) => !(o.action === "link" && o.artifactId === consumer && o.to === upstream));
135330
135542
  for (const o of draft.plan.ops) if (o.action === "spawn" && o.id === consumer && o.inputs) o.inputs = o.inputs.filter((e) => e.to !== upstream);
135331
135543
  break;
@@ -135333,6 +135545,7 @@ function editDraftPlan(draft, op) {
135333
135545
  case "seal": {
135334
135546
  if (op.reason !== "cancelled") throw new Error(`\u8349\u6848\u8282\u70B9\u53EA\u652F\u6301 cancel\uFF08\u5220\u9664\uFF09\uFF0C\u4E0D\u652F\u6301 ${op.reason}\u2014\u2014\u8282\u70B9\u8FD8\u6CA1\u843D\u5E93`);
135335
135547
  const id = resolveRef(op.artifactId, draft);
135548
+ assertNotDeliverySummary(draft, [id], "\u5220\u9664\u8282\u70B9");
135336
135549
  draft.plan.ops = draft.plan.ops.filter((o) => {
135337
135550
  if (o.action === "spawn" && o.id === id) return false;
135338
135551
  if (o.action === "annotate" && o.artifactId === id) return false;
@@ -135345,14 +135558,24 @@ function editDraftPlan(draft, op) {
135345
135558
  default:
135346
135559
  throw new Error(`\u8349\u6848\u7F16\u8F91\u6682\u4E0D\u652F\u6301 ${op.action}\uFF08\u8349\u6848\u9636\u6BB5\u53EA\u652F\u6301 spawn / edit / link / unlink / cancel\uFF09`);
135347
135560
  }
135561
+ reconcileDeliverySummaryPlan(draft);
135348
135562
  }
135349
- var spawnOps, candidatesOf;
135563
+ var spawnOps, candidatesOf, spawnById, SUMMARY_TASK_BOOK;
135350
135564
  var init_draft_edit = __esm({
135351
135565
  "../server/src/governance/draft-edit.ts"() {
135352
135566
  "use strict";
135567
+ init_src();
135353
135568
  init_src2();
135354
135569
  spawnOps = (draft) => draft.plan.ops.filter((o) => o.action === "spawn" && !!o.id);
135355
135570
  candidatesOf = (draft) => spawnOps(draft).map((o) => ({ id: o.id, type: o.type, ...o.title ? { title: o.title } : {} }));
135571
+ spawnById = (draft, id) => spawnOps(draft).find((o) => o.id === id);
135572
+ SUMMARY_TASK_BOOK = [
135573
+ "# \u4EA4\u4ED8\u6C47\u603B\u4EFB\u52A1\u4E66",
135574
+ "",
135575
+ "\u4F60\u662F\u672C\u5DE5\u5355\u534F\u8C03\u8005\uFF0C\u8D1F\u8D23\u628A\u534F\u4F5C\u6D41\u5DF2\u7ECF\u4EA7\u51FA\u7684\u6700\u7EC8\u7ED3\u679C\u6574\u7406\u6210\u7528\u6237\u53EF\u4EE5\u76F4\u63A5\u9886\u53D6\u548C\u4F7F\u7528\u7684\u4E00\u4EFD\u5B8C\u6574\u4EA4\u4ED8\u3002",
135576
+ "\u8FD9\u4E0D\u662F\u9A8C\u6536\u8282\u70B9\uFF1A\u4E0D\u8981\u91CD\u65B0\u6D4B\u8BD5\u3001\u4E0D\u8981\u5224\u5B9A\u901A\u8FC7\u3001\u4E0D\u8981\u6C42\u7528\u6237\u7B7E\u6536\u3002",
135577
+ "\u8F93\u51FA\u987B\u8BF4\u660E\u7528\u6237\u62FF\u5230\u4E86\u4EC0\u4E48\u3001\u5728\u54EA\u91CC\u62FF\u3001\u600E\u4E48\u4F7F\u7528\u3001\u5F53\u524D\u662F\u54EA\u4E00\u7248\uFF1B\u53EA\u5F15\u7528\u8F93\u5165\u53CA\u5176\u4E0A\u6E38\u7684\u771F\u5B9E\u4EA7\u7269\u3002"
135578
+ ].join("\n");
135356
135579
  }
135357
135580
  });
135358
135581
 
@@ -135619,7 +135842,7 @@ async function plannerAvailableActors(registry2) {
135619
135842
  }
135620
135843
  async function plannerArtifactTypes(schema, registry2) {
135621
135844
  if (!registry2) {
135622
- return schema.filter((def) => def.name !== "brief").map((def) => ({
135845
+ return schema.filter((def) => def.name !== "brief" && def.name !== DELIVERY_SUMMARY_ARTIFACT_TYPE).map((def) => ({
135623
135846
  name: def.name,
135624
135847
  ...def.ownerRole ? { ownerRole: def.ownerRole } : {},
135625
135848
  contentType: def.contentType,
@@ -135628,7 +135851,7 @@ async function plannerArtifactTypes(schema, registry2) {
135628
135851
  }
135629
135852
  const actors = await plannerAvailableActors(registry2);
135630
135853
  const staffableRoles = new Set(actors.flatMap((actor) => actor.roles));
135631
- return schema.filter((def) => def.name !== "brief" && (def.ownerRole === void 0 || staffableRoles.has(def.ownerRole))).map((def) => ({
135854
+ return schema.filter((def) => def.name !== "brief" && def.name !== DELIVERY_SUMMARY_ARTIFACT_TYPE && (def.ownerRole === void 0 || staffableRoles.has(def.ownerRole))).map((def) => ({
135632
135855
  name: def.name,
135633
135856
  ...def.ownerRole ? { ownerRole: def.ownerRole } : {},
135634
135857
  contentType: def.contentType
@@ -135723,7 +135946,8 @@ async function buildDynamicWorkorderPlan(input) {
135723
135946
  ...def.ownerRole ? { role: def.ownerRole } : {},
135724
135947
  owner: ownerActor,
135725
135948
  dependsOn: node.dependsOn,
135726
- ...node.brief ? { brief: node.brief } : {}
135949
+ ...node.brief ? { brief: node.brief } : {},
135950
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135727
135951
  });
135728
135952
  }
135729
135953
  } else {
@@ -135738,11 +135962,12 @@ async function buildDynamicWorkorderPlan(input) {
135738
135962
  const ownerActor = owner?.actor ?? (!def.ownerRole ? PENDING_UNASSIGNED : type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
135739
135963
  if (enforceStaffing && def.ownerRole && !owner?.actor && type !== primaryType) continue;
135740
135964
  const id = type === primaryType ? primaryId : `artifact:${type}:${slug5}`;
135741
- availableNodes.push({ type, id, ...def.ownerRole ? { role: def.ownerRole } : {}, owner: ownerActor });
135965
+ availableNodes.push({ type, id, ...def.ownerRole ? { role: def.ownerRole } : {}, owner: ownerActor, ...type === primaryType ? { isFinalOutput: true } : {} });
135742
135966
  }
135743
135967
  }
135744
135968
  for (const node of availableNodes) {
135745
135969
  const inputs = agentGraph ? agentEdgesFor(node.dependsOn ?? [], availableNodes, briefId, issues) : ruleEdgesFor(node.type, availableNodes, briefId);
135970
+ node.inputs = inputs;
135746
135971
  const shortTitle = node.title ?? (!agentGraph && node.type === "prd" && issues.length > 0 ? "\u964D\u7EA7\u65B9\u6848\u6587\u6863" : labelForType(node.type));
135747
135972
  const briefBody = node.brief ?? taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes, shortTitle);
135748
135973
  ops.push({
@@ -135756,10 +135981,59 @@ async function buildDynamicWorkorderPlan(input) {
135756
135981
  // 所有类型(含 document)统一:description = 完整任务书,直接进 owner 的 TASK.md,**不再造 open 人类 annotation**
135757
135982
  //(那条 annotation 默认 kind=change-request、触发 hasOpenHumanChangeRequest 焊死自动合并 + 假审核,是迁移遗留)。
135758
135983
  // document 的 docType/title 走独立字段;老文档缺独立字段的仍由 decodeDocumentDescription 兜底解析 description(保留)。
135759
- description: briefBody
135984
+ description: briefBody,
135985
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135760
135986
  });
135761
135987
  spawned.push({ id: node.id, type: node.type, role: node.role, owner: node.owner });
135762
135988
  }
135989
+ if (availableNodes.length > 0) {
135990
+ const consumed = new Set(availableNodes.flatMap((node) => (node.inputs ?? []).map((edge) => edge.to)));
135991
+ const terminalNodes = availableNodes.filter((node) => !consumed.has(node.id));
135992
+ const finalNodes = availableNodes.filter((node) => node.isFinalOutput);
135993
+ const directFinal = finalNodes.length === 1 && terminalNodes.length === 1 && finalNodes[0].id === terminalNodes[0].id;
135994
+ if (agentGraph && finalNodes.length === 0) {
135995
+ issues.push({
135996
+ code: "planner_invalid",
135997
+ severity: "warning",
135998
+ message: "pm_agent \u6CA1\u6709\u6807\u8BB0\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u9700\u6C42\u7684\u6700\u7EC8\u7ED3\u679C\u8282\u70B9\uFF0C\u7CFB\u7EDF\u5DF2\u7528\u4EA4\u4ED8\u6C47\u603B\u515C\u5E95\u3002"
135999
+ });
136000
+ }
136001
+ if (directFinal) {
136002
+ } else {
136003
+ const summaryDef = schemaByName.get(DELIVERY_SUMMARY_ARTIFACT_TYPE);
136004
+ if (!summaryDef) {
136005
+ issues.push({
136006
+ code: "missing_artifact_type",
136007
+ severity: "blocking",
136008
+ artifactType: DELIVERY_SUMMARY_ARTIFACT_TYPE,
136009
+ message: `\u7F3A\u5C11\u7CFB\u7EDF ArtifactType ${DELIVERY_SUMMARY_ARTIFACT_TYPE}\uFF0C\u672C\u6B21\u65E0\u6CD5\u521B\u5EFA\u4EA4\u4ED8\u6C47\u603B\u8282\u70B9\u3002`
136010
+ });
136011
+ } else if (terminalNodes.length > 0) {
136012
+ const summaryId = deriveArtifactId(DELIVERY_SUMMARY_ARTIFACT_TYPE, input.workspace, "\u4EA4\u4ED8\u6C47\u603B", usedIds);
136013
+ const summaryOwner = input.aiActorId ?? (input.actor.startsWith("actor:agent:") ? input.actor : PENDING_UNASSIGNED);
136014
+ if (summaryOwner === PENDING_UNASSIGNED) {
136015
+ issues.push({
136016
+ code: "missing_owner",
136017
+ severity: "blocking",
136018
+ artifactType: DELIVERY_SUMMARY_ARTIFACT_TYPE,
136019
+ message: "\u4EA4\u4ED8\u6C47\u603B\u5FC5\u987B\u7531\u672C\u5DE5\u5355\u7BA1\u7406\u8005\u8D1F\u8D23\uFF0C\u4F46\u5F53\u524D brief \u6CA1\u6709 manager\uFF1B\u8BF7\u5148\u6307\u5B9A\u7BA1\u7406\u8005\u3002"
136020
+ });
136021
+ }
136022
+ ops.push({
136023
+ action: "spawn",
136024
+ id: summaryId,
136025
+ type: DELIVERY_SUMMARY_ARTIFACT_TYPE,
136026
+ owner: summaryOwner,
136027
+ workspace: input.workspace,
136028
+ title: "\u4EA4\u4ED8\u6C47\u603B",
136029
+ inputs: terminalNodes.map((node) => ({ to: node.id, required: true })),
136030
+ description: deliverySummaryTaskBook(finalNodes, terminalNodes),
136031
+ isFinalOutput: true
136032
+ });
136033
+ spawned.push({ id: summaryId, type: DELIVERY_SUMMARY_ARTIFACT_TYPE, owner: summaryOwner });
136034
+ }
136035
+ }
136036
+ }
135763
136037
  return {
135764
136038
  plan: {
135765
136039
  reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`,
@@ -135804,15 +136078,19 @@ function buildPmAgentPlannerPrompt(input) {
135804
136078
  "",
135805
136079
  JSON.stringify({
135806
136080
  nodes: [
135807
- { type: "prd", title: "\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E", dependsOn: ["brief"], brief: "\u8FD9\u4E2A\u8282\u70B9\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u7684\u89C4\u683C\uFF1A\u4EA7\u51FA\u4EC0\u4E48\u3001\u8981\u70B9\u3001\u7EA6\u675F" },
135808
- { type: "research", title: "\u7ADE\u54C1\u8C03\u7814", dependsOn: ["brief"], brief: "\u2026" },
135809
- { type: "dev", title: "\u540E\u7AEF\u5B9E\u73B0", dependsOn: ["prd:\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E"], brief: "\u2026" }
136081
+ { type: "prd", title: "\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E", dependsOn: ["brief"], brief: "\u8FD9\u4E2A\u8282\u70B9\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u7684\u89C4\u683C\uFF1A\u4EA7\u51FA\u4EC0\u4E48\u3001\u8981\u70B9\u3001\u7EA6\u675F", isFinalOutput: false },
136082
+ { type: "research", title: "\u7ADE\u54C1\u8C03\u7814", dependsOn: ["brief"], brief: "\u2026", isFinalOutput: false },
136083
+ { type: "dev", title: "\u4EA7\u54C1\u5B9E\u73B0", dependsOn: ["prd:\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E"], brief: "\u4EA7\u51FA\u7528\u6237\u9700\u8981\u7684\u4EA7\u54C1\u548C\u4F7F\u7528\u8BF4\u660E", isFinalOutput: true },
136084
+ { type: "qa", title: "\u8D28\u91CF\u68C0\u67E5", dependsOn: ["dev:\u4EA7\u54C1\u5B9E\u73B0"], brief: "\u68C0\u67E5\u4EA7\u54C1\u662F\u5426\u53EF\u7528", isFinalOutput: false }
135810
136085
  ],
135811
136086
  notes: ["\u53EF\u9009\uFF1A\u89C4\u5212\u7406\u7531\u6216\u98CE\u9669\u63D0\u793A"]
135812
136087
  }, null, 2),
135813
136088
  "",
135814
136089
  "\u89C4\u5219\uFF1A",
135815
136090
  "- brief \u6839\u8282\u70B9\u7531\u7CFB\u7EDF\u56FA\u5B9A\u521B\u5EFA\uFF0C\u4F60\u4E0D\u8981\u628A brief \u5199\u8FDB nodes\u3002",
136091
+ `- ${DELIVERY_SUMMARY_ARTIFACT_TYPE}\uFF08\u4EA4\u4ED8\u6C47\u603B\uFF09\u7531\u7CFB\u7EDF\u56FA\u5B9A\u521B\u5EFA\uFF0C\u4F60\u4E0D\u8981\u628A\u5B83\u5199\u8FDB nodes\u3002`,
136092
+ "- \u6BCF\u4E2A\u8282\u70B9\u5FC5\u987B\u7ED9 isFinalOutput\u3002true \u8868\u793A\u8BE5\u8282\u70B9\u7684\u4EA7\u7269\u76F4\u63A5\u56DE\u7B54\u7528\u6237\u9700\u6C42\uFF1B\u5B83\u4E0D\u662F\u8BC4\u5BA1\u3001QA \u6216\u662F\u5426\u5B8C\u6210\u7684\u72B6\u6001\u3002\u8FC7\u7A0B/\u652F\u6491/\u95F8\u95E8\u8282\u70B9\u5199 false\u3002",
136093
+ "- \u81F3\u5C11\u4E00\u4E2A\u8282\u70B9\u5E94\u4E3A isFinalOutput:true\u3002\u4F8B\uFF1A\u53EA\u505A\u4E00\u4EFD PRD \u65F6 PRD=true\uFF1B\u4E09\u4EFD\u5E73\u884C\u8C03\u7814\u6C47\u5165\u4E00\u4EFD\u7EFC\u5408\u62A5\u544A\u65F6\u53EA\u6807\u7EFC\u5408\u62A5\u544A=true\uFF1B\u505A\u4EA7\u54C1\u65F6\u4EA7\u51FA\u4EA7\u54C1\u548C\u4F7F\u7528\u8BF4\u660E\u7684\u5B9E\u73B0\u8282\u70B9=true\uFF0C\u800C\u90E8\u7F72/QA=false\u3002",
135816
136094
  "- type \u53EA\u80FD\u4ECE\u4E0A\u9762\u3010\u53EF\u7528 ArtifactType\u3011\u76EE\u5F55\u91CC\u9009\uFF08\u4EE5\u6CE8\u5165\u76EE\u5F55\u4E3A\u51C6\uFF09\u3002",
135817
136095
  "- \u6BCF\u4E2A\u8282\u70B9\u5FC5\u7ED9 title\uFF08\u226420\u5B57\u3001\u7528\u6765\u533A\u5206\u8282\u70B9\uFF09+ brief\uFF08\u8FD9\u4E2A\u8282\u70B9\u8981\u4EA7\u51FA\u4EC0\u4E48\u7684\u89C4\u683C\uFF0C\u522B\u5957\u6A21\u677F\u3001\u522B\u53EA\u5199\u7C7B\u578B\u540D\uFF09\u3002",
135818
136096
  '- \u3010\u53EF\u6709\u591A\u4E2A\u540C type \u8282\u70B9\u3011\uFF0C\u7528\u4E0D\u540C title \u533A\u5206\uFF08\u5982\u4E24\u4E2A prd\uFF1A"\u524D\u7AEF\u9700\u6C42"\u3001"\u540E\u7AEF\u9700\u6C42"\uFF1B\u591A\u4EFD\u8C03\u7814\u62A5\u544A\u5404\u53D6\u5176\u540D\uFF09\u3002',
@@ -135839,7 +136117,8 @@ function parseAgentWorkorderPlan(text) {
135839
136117
  type: n.type.trim().toLowerCase(),
135840
136118
  ...Array.isArray(n.dependsOn) ? { dependsOn: n.dependsOn.filter((x2) => typeof x2 === "string").map((x2) => x2.trim().toLowerCase()).filter(Boolean) } : {},
135841
136119
  ...typeof n.title === "string" && n.title.trim() ? { title: n.title.trim() } : {},
135842
- ...typeof n.brief === "string" && n.brief.trim() ? { brief: n.brief.trim() } : {}
136120
+ ...typeof n.brief === "string" && n.brief.trim() ? { brief: n.brief.trim() } : {},
136121
+ ...n.isFinalOutput === true ? { isFinalOutput: true } : {}
135843
136122
  });
135844
136123
  }
135845
136124
  if (nodes.length === 0) return null;
@@ -135915,13 +136194,14 @@ function desiredArtifacts(primaryType, description) {
135915
136194
  return ["prd", "research", "design", "adr", "dev", "qa"].filter((type) => wanted.has(type));
135916
136195
  }
135917
136196
  function normalizeAgentPlan(plan, validTypes) {
135918
- const allowed = validTypes.map((t) => t.trim().toLowerCase()).filter((t) => t !== "brief");
136197
+ const allowed = validTypes.map((t) => t.trim().toLowerCase()).filter((t) => t !== "brief" && t !== DELIVERY_SUMMARY_ARTIFACT_TYPE);
135919
136198
  const nodes = [];
135920
136199
  const seen = /* @__PURE__ */ new Set();
135921
136200
  const dupTitles = [];
135922
136201
  for (const node of plan.nodes) {
135923
136202
  const type = node.type.trim().toLowerCase();
135924
136203
  if (type === "brief") continue;
136204
+ if (type === DELIVERY_SUMMARY_ARTIFACT_TYPE) continue;
135925
136205
  if (!allowed.includes(type)) continue;
135926
136206
  const title = (node.title?.trim() || labelForType(type)).slice(0, 40);
135927
136207
  const key = `${type}:${title.toLowerCase()}`;
@@ -135934,7 +136214,8 @@ function normalizeAgentPlan(plan, validTypes) {
135934
136214
  type,
135935
136215
  title,
135936
136216
  dependsOn: node.dependsOn?.map((d) => d.trim()).filter(Boolean) ?? [],
135937
- ...node.brief ? { brief: node.brief } : {}
136217
+ ...node.brief ? { brief: node.brief } : {},
136218
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135938
136219
  });
135939
136220
  }
135940
136221
  return nodes.length > 0 ? { nodes, dupTitles, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
@@ -136012,6 +136293,29 @@ ${plannerNotes.map((note) => `- ${note}`).join("\n")}` : "";
136012
136293
  notes
136013
136294
  ].join("\n");
136014
136295
  }
136296
+ function deliverySummaryTaskBook(finalNodes, terminalNodes) {
136297
+ const labels = (nodes) => nodes.map((node) => node.title ?? labelForType(node.type));
136298
+ return [
136299
+ "# \u4EA4\u4ED8\u6C47\u603B\u4EFB\u52A1\u4E66",
136300
+ "",
136301
+ "\u4F60\u662F\u672C\u5DE5\u5355\u534F\u8C03\u8005\uFF0C\u8D1F\u8D23\u628A\u534F\u4F5C\u6D41\u5DF2\u7ECF\u4EA7\u51FA\u7684\u6700\u7EC8\u7ED3\u679C\u6574\u7406\u6210\u7528\u6237\u53EF\u4EE5\u76F4\u63A5\u9886\u53D6\u548C\u4F7F\u7528\u7684\u4E00\u4EFD\u5B8C\u6574\u4EA4\u4ED8\u3002",
136302
+ "\u8FD9\u4E0D\u662F\u9A8C\u6536\u8282\u70B9\uFF1A\u4E0D\u8981\u91CD\u65B0\u6D4B\u8BD5\u3001\u4E0D\u8981\u5224\u5B9A\u901A\u8FC7\u3001\u4E0D\u8981\u6C42\u7528\u6237\u7B7E\u6536\uFF0C\u4E5F\u4E0D\u8981\u628A\u6240\u6709\u8FC7\u7A0B\u8282\u70B9\u90FD\u585E\u8FDB\u6765\u3002",
136303
+ "",
136304
+ "## \u89C4\u5212\u8BC6\u522B\u7684\u6700\u7EC8\u7ED3\u679C\u8282\u70B9",
136305
+ ...finalNodes.length > 0 ? labels(finalNodes).map((item) => `- ${item}`) : ["- \u89C4\u5212\u672A\u660E\u786E\u6807\u8BB0\uFF1B\u8BF7\u6839\u636E\u5DE5\u5355\u9700\u6C42\u548C\u8F93\u5165\u4EA7\u7269\u6574\u7406\u771F\u5B9E\u4EA4\u4ED8\u3002"],
136306
+ "",
136307
+ "## \u672C\u8282\u70B9\u76F4\u63A5\u627F\u63A5\u7684\u534F\u4F5C\u6D41\u7EC8\u70B9",
136308
+ ...labels(terminalNodes).map((item) => `- ${item}`),
136309
+ "",
136310
+ "## \u8F93\u51FA\u5FC5\u987B\u56DE\u7B54",
136311
+ "- \u6211\u62FF\u5230\u4E86\u4EC0\u4E48\uFF1A\u4EA7\u54C1\u3001\u6587\u6863\u3001\u65B9\u6848\u3001\u4ED3\u5E93\u6216\u5176\u5B83\u76F4\u63A5\u56DE\u7B54\u9700\u6C42\u7684\u7ED3\u679C\u3002",
136312
+ "- \u5728\u54EA\u91CC\u62FF\uFF1A\u7ED9\u51FA\u53EF\u8BBF\u95EE\u5730\u5740\u3001\u4E0B\u8F7D\u4F4D\u7F6E\u3001\u4ED3\u5E93/\u7248\u672C\u6216\u5176\u5B83\u51C6\u786E\u5B9A\u4F4D\u3002",
136313
+ "- \u63A5\u4E0B\u6765\u600E\u4E48\u7528\uFF1A\u7ED9\u51FA\u5B89\u88C5\u3001\u6253\u5F00\u3001\u4E0A\u4F20\u3001\u6388\u6743\u6216\u4E3B\u8981\u64CD\u4F5C\u6B65\u9AA4\u3002",
136314
+ "- \u5F53\u524D\u662F\u54EA\u4E00\u7248\uFF1A\u5F15\u7528\u5404\u4E0A\u6E38\u5F53\u524D\u4EA4\u4ED8\u7248\u672C\uFF1B\u4E0A\u6E38\u53D8\u5316\u540E\u751F\u6210\u65B0\u7684\u5B8C\u6574\u6C47\u603B\u7248\u672C\uFF0C\u4E0D\u8986\u76D6\u5386\u53F2\u3002",
136315
+ "",
136316
+ "\u53EA\u5F15\u7528\u8F93\u5165\u8282\u70B9\u53CA\u5176\u4E0A\u6E38\u91CC\u7684\u771F\u5B9E\u4EA7\u7269\uFF0C\u4E0D\u8981\u81C6\u9020\u94FE\u63A5\u6216\u5B8C\u6210\u60C5\u51B5\u3002\u7F3A\u5C11\u53EF\u9886\u53D6\u7ED3\u679C\u6216\u5FC5\u8981\u4F7F\u7528\u8BF4\u660E\u65F6\uFF0C\u7528 `oasis gap` \u660E\u786E\u62A5\u544A\uFF0C\u8865\u9F50\u524D\u4E0D\u8981 conclude\u3002"
136317
+ ].join("\n");
136318
+ }
136015
136319
  function dedupeIssues(issues) {
136016
136320
  const seen = /* @__PURE__ */ new Set();
136017
136321
  const out = [];
@@ -136031,7 +136335,8 @@ function labelForType(type) {
136031
136335
  design: "\u8BBE\u8BA1",
136032
136336
  adr: "\u6280\u672F\u65B9\u6848",
136033
136337
  dev: "\u5B9E\u73B0",
136034
- qa: "\u6D4B\u8BD5\u9A8C\u6536"
136338
+ qa: "\u6D4B\u8BD5\u9A8C\u6536",
136339
+ [DELIVERY_SUMMARY_ARTIFACT_TYPE]: "\u4EA4\u4ED8\u6C47\u603B"
136035
136340
  };
136036
136341
  return labels[type] ?? type;
136037
136342
  }
@@ -136043,16 +136348,26 @@ var DEFAULT_TYPES, WORKORDER_SPEC_PREFIX;
136043
136348
  var init_planner = __esm({
136044
136349
  "../server/src/domains/collab/planner.ts"() {
136045
136350
  "use strict";
136351
+ init_src();
136046
136352
  init_src2();
136047
136353
  init_identity();
136048
136354
  DEFAULT_TYPES = [
136049
- // brief 的 goal/acceptanceCriteria 现在是【自定义字段】(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)
136355
+ // brief 的 goal/acceptanceCriteria 现在是【自定义字段】
136356
+ //(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)。
136050
136357
  // brief 不绑岗位:内置产物,owner 恒 = 发起人本人(决策 0026)——ownerRole 从不被解析。
136051
136358
  { name: "brief", contentType: "text", fieldDefs: [
136052
136359
  { name: "goal", type: "string", description: "\u4E00\u53E5\u8BDD\u76EE\u6807\uFF1A\u8FD9\u4E2A\u5DE5\u5355\u8981\u8FBE\u6210\u4EC0\u4E48" },
136053
136360
  { name: "acceptanceCriteria", type: "string[]", description: "\u9A8C\u6536\u6761\u76EE\uFF08\u6EE1\u8DB3\u5373\u7B97\u5B8C\u6210\uFF09" },
136054
136361
  { name: "manager", type: "string", description: "\u672C\u5DE5\u5355\u7BA1\u7406\u8005 agent\uFF08\u966A\u53D1\u8D77\u4EBA\u804A\u51FA\u6B64\u5355\u7684 AI \u5B9E\u4F8B\uFF09\uFF1A\u8D1F\u8D23\u672C\u5DE5\u5355\u7684\u534F\u8C03/\u515C\u5E95\uFF0C\u641E\u4E0D\u5B9A\u5347\u7EA7\u7ED9 owner\uFF08\u51B3\u7B56 0026\uFF09" }
136055
136362
  ] },
136363
+ {
136364
+ name: DELIVERY_SUMMARY_ARTIFACT_TYPE,
136365
+ contentType: "text",
136366
+ description: "\u7CFB\u7EDF\u751F\u6210\u7684\u5DE5\u5355\u672B\u7AEF\u95ED\u73AF\uFF1A\u7531\u672C\u5DE5\u5355\u7BA1\u7406\u8005\u628A\u534F\u4F5C\u6D41\u91CC\u7684\u6700\u7EC8\u7ED3\u679C\u6574\u7406\u6210\u4E00\u4EFD\u53EF\u9886\u53D6\u3001\u53EF\u5B9A\u4F4D\u3001\u53EF\u4F7F\u7528\u7684\u4EA4\u4ED8\uFF1B\u4E0D\u627F\u62C5\u9A8C\u6536\u3002",
136367
+ // 交付汇总是普通文本产物节点,视觉沿用 PRD 的 blue;不另造“封箱/验收”身份。
136368
+ appearance: { accent: "blue" },
136369
+ cascade: "auto"
136370
+ },
136056
136371
  { name: "prd", contentType: "text", ownerRole: "pm" },
136057
136372
  { name: "research", contentType: "text", ownerRole: "research" },
136058
136373
  { name: "design", contentType: "text", ownerRole: "designer" },
@@ -136063,8 +136378,22 @@ var init_planner = __esm({
136063
136378
  name: "provision",
136064
136379
  contentType: "text",
136065
136380
  ownerRole: "devops",
136066
- description: "\u4EE3\u7801\u4ED3\u5E93\u4F9B\u7ED9\uFF08\u5EFA\u4ED3\uFF09\uFF1A\u5F53\u9879\u76EE\u91CC\u6CA1\u6709\u4E0B\u6E38\u5B9E\u73B0\u6240\u9700\u7684\u4EE3\u7801\u4ED3\u5E93\u65F6\uFF0C\u7528\u6B64\u8282\u70B9\u5EFA\u4ED3\u2014\u2014\u6309\u7ED3\u6784\uFF08\u5355\u4ED3/\u4F1E\u4ED3\uFF09\u4E0E\u547D\u540D\u5EFA\u597D\u4ED3\u5E93\uFF0C\u521D\u59CB\u5316\u9879\u76EE\u6846\u67B6\u3001\u5FC5\u5907\u6587\u6863\u3001CI/CD \u57FA\u5EFA\u4E0E dev/prod \u73AF\u5883\u914D\u7F6E\uFF0C\u5E76\u767B\u8BB0\u5230\u9879\u76EE\u3002**\u5EFA\u56FE/\u8865\u56FE\u65F6\uFF0C\u82E5\u53D1\u73B0\u9879\u76EE\u7F3A\u67D0\u4E2A dev\uFF08\u4EE3\u7801\u5B9E\u73B0\uFF09\u8282\u70B9\u6240\u9700\u7684\u4EE3\u7801\u4ED3\u5E93\uFF0C\u5E94\u65B0\u589E\u4E00\u4E2A\u6B64\u7C7B\u578B\u8282\u70B9\uFF0C\u4F5C\u4E3A\u90A3\u4E2A dev \u8282\u70B9\u7684\u524D\u7F6E\u3001\u6392\u5728\u5B83\u4E4B\u524D\u3002** \u4EA7\u51FA\u4E3A\u5EFA\u4ED3\u8BB0\u5F55\uFF08\u8BF4\u660E\u5EFA\u4E86\u54EA\u4E9B\u4ED3\u3001\u903B\u8F91 id \u4E0E\u7ED3\u6784\uFF09\uFF0C\u67B6\u6784\u5E08\u8BC4\u5BA1\u3002",
136067
- reviews: { gates: [{ on: "conclude", reviewers: ["architect"], quorum: 1 }] }
136381
+ description: "\u4EE3\u7801\u4ED3\u5E93\u4F9B\u7ED9\uFF08\u5EFA\u4ED3\uFF09\uFF1A\u5F53\u9879\u76EE\u91CC\u6CA1\u6709\u4E0B\u6E38\u5B9E\u73B0\u6240\u9700\u7684\u4EE3\u7801\u4ED3\u5E93\u65F6\uFF0C\u7528\u6B64\u8282\u70B9\u5EFA\u4ED3\u2014\u2014\u6309\u7ED3\u6784\uFF08\u5355\u4ED3/\u4F1E\u4ED3\uFF09\u4E0E\u547D\u540D\u5EFA\u597D\u4ED3\u5E93\uFF0C\u521D\u59CB\u5316\u9879\u76EE\u6846\u67B6\u3001\u5FC5\u5907\u6587\u6863\u3001CI/CD \u57FA\u5EFA\uFF08GitHub Actions ci/cd workflow\uFF09\u4E0E dev/prod \u73AF\u5883\u914D\u7F6E\uFF0C\u5E76**\u767B\u8BB0\u5230\u9879\u76EE\u4E14\u6838\u9A8C\u767B\u8BB0\u771F\u7684\u843D\u8FDB\u5185\u6838**\uFF08\u4E0D\u662F\u5199\u7BC7\u6587\u6863\u8BF4\u914D\u597D\u4E86\u5C31\u7B97\uFF09\u3002**\u5EFA\u56FE/\u8865\u56FE\u65F6\uFF0C\u82E5\u53D1\u73B0\u9879\u76EE\u7F3A\u67D0\u4E2A dev\uFF08\u4EE3\u7801\u5B9E\u73B0\uFF09\u8282\u70B9\u6240\u9700\u7684\u4EE3\u7801\u4ED3\u5E93\uFF0C\u5E94\u65B0\u589E\u4E00\u4E2A\u6B64\u7C7B\u578B\u8282\u70B9\uFF0C\u4F5C\u4E3A\u90A3\u4E2A dev \u8282\u70B9\u7684\u524D\u7F6E\u3001\u6392\u5728\u5B83\u4E4B\u524D\u3002** \u4EA7\u51FA\u4E3A\u5EFA\u4ED3\u8BB0\u5F55\uFF08\u8BF4\u660E\u5EFA\u4E86\u54EA\u4E9B\u4ED3\u3001\u903B\u8F91 id \u4E0E\u7ED3\u6784\u3001CI/CD \u9AA8\u67B6\uFF09\uFF0C\u67B6\u6784\u5E08\u8BC4\u5BA1\u3002\u6536\u5C3E\u524D\u987B `oasis record-action <\u672C\u8282\u70B9> --name registration_verified --pass true` \u7559\u75D5\uFF08\u5148\u771F\u6838\u9A8C\u767B\u8BB0\u843D\u5E93\uFF09\u3002",
136382
+ reviews: { gates: [{ on: "conclude", reviewers: ["architect"], quorum: 1 }] },
136383
+ // grounding:登记必须真落库才收口(ADR-0070/§9.2)。防「写篇文档说仓配好了就算交付」(delivery C-2)。
136384
+ grounding: { requiredActions: [{ name: "registration_verified", mustPass: true }] }
136385
+ },
136386
+ {
136387
+ // deploy 此前只在生产经 API 手工新增、不在代码常量里(零审计,见 ADR-0067 背景与 ADR-0070)。
136388
+ // 收编进 DEFAULT_TYPES:给它一份规范定义作为新环境/测试/文档的事实源。
136389
+ // 注意 ensureDefaultWorkorderTypes 只“补缺”——生产已存在的 deploy 条目不会被此定义覆盖。
136390
+ name: "deploy",
136391
+ contentType: "text",
136392
+ ownerRole: "devops",
136393
+ description: "\u771F\u5B9E\u90E8\u7F72\u4E0E\u8054\u8C03\uFF1A\u628A\u5BF9\u5E94 dev\uFF08\u4EE3\u7801\u5B9E\u73B0\uFF09\u4EA7\u51FA\u90E8\u7F72\u5230\u76EE\u6807\u73AF\u5883\u5E76\u8DD1\u901A\u7AEF\u5230\u7AEF\u8054\u8C03\uFF0C\u4EA7\u51FA\u53EF\u8BBF\u95EE\u7684\u90E8\u7F72\u5730\u5740 + \u90E8\u7F72\u8BB0\u5F55\uFF08\u73AF\u5883/\u670D\u52A1/\u7AEF\u53E3/\u5065\u5EB7\u68C0\u67E5\u8BC1\u636E\uFF09\u3002**\u4F9D\u8D56\u5BF9\u5E94\u7684 dev \u8282\u70B9\uFF08required\uFF09\uFF0C\u6392\u5728\u5B83\u4E4B\u540E\uFF1B\u82E5\u672C\u5DE5\u5355\u6709 adr\uFF08\u6280\u672F\u65B9\u6848\uFF0C\u542B CI/CD \u4E0E\u90E8\u7F72\u73AF\u5883\u51B3\u7B56\uFF09\uFF0C\u4E5F\u5E94\u4F9D\u8D56\u8BE5 adr\u2014\u2014\u5982\u6B64\u6280\u672F\u65B9\u6848\u6216\u4EE3\u7801\u4E00\u51FA\u65B0\u91CC\u7A0B\u7891\uFF0C\u672C\u8282\u70B9\u5373\u88AB\u7EA7\u8054\u8FD4\u5DE5\u91CD\u65B0\u90E8\u7F72\uFF08ADR-0067\uFF09\u3002** \u5EFA\u56FE/\u8865\u56FE\u65F6\uFF0C\u5F53\u5DE5\u5355\u9700\u8981\u771F\u90E8\u7F72/\u4E0A\u7EBF\u800C\u975E\u4EC5\u4EA4\u4ED8\u4EE3\u7801\u65F6\u65B0\u589E\u6B64\u8282\u70B9\u3002\u5065\u5EB7\u68C0\u67E5\u987B\u5916\u90E8\u53EF\u8FBE\u3001\u7981\u5047\u7EFF\uFF1B\u4E0B\u6E38\uFF08\u5982 QA\uFF09\u53D1\u73B0\u90E8\u7F72\u7ED3\u679C\u4E0D\u5BF9\u65F6\u5BF9\u672C\u8282\u70B9\u56DE\u538B\uFF08annotate --from\uFF09\u3002\u6536\u5C3E\u524D\u987B\u5148\u771F\u8DD1\u5916\u90E8\u5065\u5EB7\u63A2\u9488\u3001\u518D `oasis record-action <\u672C\u8282\u70B9> --name health_probe --pass true` \u7559\u75D5\uFF0C\u5426\u5219\u6536\u4E0D\u4E86\u53E3\u3002",
136394
+ reviews: { gates: [{ on: "conclude", reviewers: ["qa"], quorum: 1 }] },
136395
+ // grounding:必须有「通过的健康探针」留痕才收口(ADR-0070/§9.2)。机械堵假绿——force 也豁免不了。
136396
+ grounding: { requiredActions: [{ name: "health_probe", mustPass: true }] }
136068
136397
  }
136069
136398
  ];
136070
136399
  WORKORDER_SPEC_PREFIX = "OASIS_WORKORDER_SPEC ";
@@ -136189,6 +136518,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
136189
136518
  };
136190
136519
  };
136191
136520
  const runtimeByArtifact = buildRuntimeByArtifact(dispatchJournal, ref2);
136521
+ const hasDeliverySummary = arts.some((a) => a.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
136192
136522
  const nodes = arts.map((a) => {
136193
136523
  const info = nodeInfo(model, a);
136194
136524
  const observedRuntime = runtimeByArtifact.get(a.id);
@@ -136217,6 +136547,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
136217
136547
  queue: info.queueLen,
136218
136548
  blocked: blockedOf(model, a.id).blocked,
136219
136549
  owner: ref2(a.owner),
136550
+ ...(hasDeliverySummary ? a.type === DELIVERY_SUMMARY_ARTIFACT_TYPE : a.isFinalOutput === true) ? { isFinalOutput: true } : {},
136220
136551
  // 封存原因透出:stage 只到 "sealed" 粒度,原因(accepted/cancelled/frozen)另给,供前端区分展示。
136221
136552
  ...lifecycle !== "active" ? { sealedReason: lifecycle.slice("sealed:".length) } : {},
136222
136553
  ...runtime ? { runtime } : {},
@@ -136290,6 +136621,7 @@ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
136290
136621
  return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
136291
136622
  };
136292
136623
  const spawns = draft.plan.ops.filter((op) => op.action === "spawn" && !!op.id);
136624
+ const hasDeliverySummary = spawns.some((op) => op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
136293
136625
  const nodes = spawns.map((op) => {
136294
136626
  const manual = draft.plan.ops.find((o) => o.action === "edit" && o.artifactId === op.id)?.reviewers?.filter((r) => r.source === "manual").map((r) => r.actor) ?? [];
136295
136627
  const typePv = typeReviewerPreview?.(op.type) ?? null;
@@ -136303,6 +136635,7 @@ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
136303
136635
  queue: 0,
136304
136636
  blocked: false,
136305
136637
  owner: ref2(op.owner ?? draft.draftedBy),
136638
+ ...(hasDeliverySummary ? op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE : op.isFinalOutput === true) ? { isFinalOutput: true } : {},
136306
136639
  ...pv ? { reviewers: pv.reviewers.map((r) => ({ actor: ref2(r.actor), source: r.source })), quorum: pv.quorum, typeName: pv.typeName } : {}
136307
136640
  };
136308
136641
  });
@@ -136581,6 +136914,7 @@ var init_workorder_detail = __esm({
136581
136914
  "../server/src/domains/collab/workorder-detail.ts"() {
136582
136915
  "use strict";
136583
136916
  init_src2();
136917
+ init_src();
136584
136918
  init_node_state();
136585
136919
  init_stall();
136586
136920
  init_workorders();
@@ -137478,10 +137812,11 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
137478
137812
  const goal = optStr(args, "goal");
137479
137813
  const rawCriteria = args["acceptance_criteria"] ?? args["acceptanceCriteria"];
137480
137814
  const acceptanceCriteria = Array.isArray(rawCriteria) ? rawCriteria.filter((x2) => typeof x2 === "string" && x2.trim().length > 0).map((x2) => x2.trim()) : void 0;
137481
- const briefForPlanner = acceptanceCriteria && acceptanceCriteria.length > 0 ? `${brief}
137482
-
137483
- \u9A8C\u6536\u6807\u51C6\uFF1A
137484
- ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137815
+ const briefForPlanner = [
137816
+ brief,
137817
+ ...acceptanceCriteria && acceptanceCriteria.length > 0 ? [`\u9A8C\u6536\u6807\u51C6\uFF1A
137818
+ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}`] : []
137819
+ ].join("\n\n");
137485
137820
  const agentIssues = [];
137486
137821
  let agentPlan = null;
137487
137822
  const inlinePlan = args["plan"];
@@ -137629,7 +137964,6 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137629
137964
  return annotation !== void 0 && isHumanChangeRequest(annotation);
137630
137965
  }) : hasOpenHumanChangeRequest2(kernel, artifactId));
137631
137966
  const changeRequestParent = resolves !== void 0 ? latestDeclaredRevisionAnchor(kernel, resolves) : isAgent(actor) ? latestOpenHumanChangeRequestParent(kernel, artifactId) : void 0;
137632
- const holdForReview = args["holdForReview"] === true || args["review"] === true;
137633
137967
  const continuation = args["continuation"];
137634
137968
  if (continuation !== void 0 && (continuation === null || typeof continuation !== "object" || continuation.checkpoint !== true || continuation.continue !== true)) {
137635
137969
  throw new Error("continuation \u5FC5\u987B\u662F { checkpoint: true, continue: true }");
@@ -137653,7 +137987,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137653
137987
  const typeDefForLand = artForLand ? ctx.schema?.find((t) => t.name === artForLand.type) : void 0;
137654
137988
  const isExternalFF = handlerForType(typeDefForLand?.contentType)?.mergeMode === "external-ff";
137655
137989
  let landed = false;
137656
- if (!holdForReview && !requiresHumanReview && qBefore === 0 && (revision.part === void 0 || parts.length <= 1 || isExternalFF)) {
137990
+ if (!requiresHumanReview && qBefore === 0 && (revision.part === void 0 || parts.length <= 1 || isExternalFF)) {
137657
137991
  try {
137658
137992
  const r = await kernel.advanceQueue(artifactId, actor);
137659
137993
  landed = r.landed.some((x2) => x2.id === revision.id);
@@ -137661,7 +137995,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137661
137995
  } catch {
137662
137996
  }
137663
137997
  }
137664
- return { message: `proposed ${revision.id} base=${revision.base ?? "\u2205"}\uFF08${revision.contentKind}\uFF09${revision.part ? ` part=${revision.part}` : ""}${landed ? " \u2192 \u5DF2\u843D\u5730\u4E3A head" : requiresHumanReview || holdForReview ? " \u2192 \u7B49\u5F85\u4EBA\u5DE5\u5BA1\u6838" : ""}`, data: revision };
137998
+ const tail = landed ? " \u2192 \u5DF2\u843D\u5730\u4E3A head" : requiresHumanReview ? " \u2192 \u7B49\u5F85\u4EBA\u5DE5\u5BA1\u6838" : pendingConcludeAttempt(kernel.model, artifactId) ? " \u2192 \u5DF2\u5165\u961F\uFF1B\u5F53\u524D\u4EA7\u7269\u6B63\u5728\u6536\u5C3E\u8BC4\u5BA1\uFF08\u6682\u51BB\u7ED3\uFF09\uFF0C\u8BC4\u5BA1\u51FA\u7ED3\u679C\u3001\u89E3\u51BB\u540E\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u5B83\u843D\u4E3A head\u2014\u2014\u65E0\u9700\u91CD\u8BD5" : kernel.isFrozen(artifactId) ? " \u2192 \u5DF2\u5165\u961F\uFF1B\u5F53\u524D\u4EA7\u7269\u88AB\u51BB\u7ED3\uFF08\u96C6\u6210/\u6539\u56FE\u4E2D\uFF09\uFF0C\u89E3\u51BB\u540E\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u63A8\u8FDB\u2014\u2014\u65E0\u9700\u91CD\u8BD5" : "";
137999
+ return { message: `proposed ${revision.id} base=${revision.base ?? "\u2205"}\uFF08${revision.contentKind}\uFF09${revision.part ? ` part=${revision.part}` : ""}${tail}`, data: revision };
137665
138000
  }
137666
138001
  case "merge": {
137667
138002
  const revisionId = optStr(args, "revisionId");
@@ -138505,6 +138840,8 @@ async function startOasisServer(opts) {
138505
138840
  );
138506
138841
  return;
138507
138842
  }
138843
+ const trustedDraft = targetWs && opts.workorderDrafts ? await opts.workorderDrafts.forWorkspace(targetWs) : void 0;
138844
+ assertDeliverySummaryPlanIntegrity(body.plan, trustedDraft?.plan);
138508
138845
  const principalRegistry = engine.registry ?? opts.registry;
138509
138846
  if (principalRegistry) {
138510
138847
  const principalErrors = await validateWorkorderPrincipals(body.plan, body.briefArtifactId, (id) => principalRegistry.getActor(id));
@@ -138660,71 +138997,26 @@ async function startOasisServer(opts) {
138660
138997
  try {
138661
138998
  const body = JSON.parse(rawBody || "{}");
138662
138999
  const ctx = resolveDiscussContext(engine.kernel.model, body, actor ?? void 0);
138663
- let retainedRt = opts.retainedSession?.(
138664
- body.annotationId ? { annotationId: body.annotationId, ...ctx.anchorArtifactId ? { artifactId: ctx.anchorArtifactId } : {} } : body
138665
- );
138666
- if (!retainedRt && opts.escalateDiscussRetain && body.artifactId) {
138667
- const raw = opts.escalateDiscussRetain.get(`esc:${body.artifactId}`);
138668
- if (raw) {
138669
- try {
138670
- const rec = JSON.parse(raw);
138671
- if (rec.runtimeSessionId && rec.human === ctx.ownerId) retainedRt = rec.runtimeSessionId;
138672
- } catch {
138673
- }
138674
- }
138675
- }
138676
- if (retainedRt && body.draftId && opts.escalateDiscussRetain) {
138677
- const claimKey = `draftclaim:${body.draftId}`;
138678
- const claimer = opts.escalateDiscussRetain.get(claimKey);
138679
- if (claimer && claimer !== ctx.ownerId) retainedRt = void 0;
138680
- else if (!claimer) opts.escalateDiscussRetain.set(claimKey, ctx.ownerId);
138681
- }
138682
139000
  const chatStore = opts.chatSession;
138683
139001
  const baseItemKey = body.draftId ? `draft:${body.draftId}` : body.annotationId ? `ann:${body.annotationId}` : `esc:${body.artifactId}`;
138684
139002
  const itemKey = `${baseItemKey}:${ctx.ownerId}`;
138685
139003
  let chatSessionId;
138686
- let createdThisCall = false;
138687
139004
  if (chatStore) {
138688
139005
  const { randomUUID: randomUUID22 } = await import("node:crypto");
138689
139006
  const known = discussSessions.get(itemKey);
138690
139007
  if (known && await chatStore.getSession(known).catch(() => null)) chatSessionId = known;
138691
139008
  if (!chatSessionId) {
138692
139009
  chatSessionId = randomUUID22();
138693
- createdThisCall = true;
138694
139010
  const now = (/* @__PURE__ */ new Date()).toISOString();
138695
139011
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
138696
139012
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
138697
139013
  const mgrBinding = reg0 ? await reg0.getBinding(ctx.managerId).catch(() => null) : null;
138698
139014
  const managerNodeId = mgrBinding?.status === "active" ? mgrBinding.nodeId : "";
138699
139015
  const managerRuntimeKind = mgrBinding?.status === "active" ? mgrBinding.runtimeKind : void 0;
138700
- await chatStore.createSession({ id: chatSessionId, humanActorId: ctx.ownerId, aiActorId: ctx.managerId, runtimeId: managerNodeId, ...managerRuntimeKind ? { runtimeKind: managerRuntimeKind } : {}, runtimeSessionId: retainedRt ?? null, title, touchedAt: now, createdAt: now, workspace: ctx.workspace }).catch(() => void 0);
139016
+ await chatStore.createSession({ id: chatSessionId, humanActorId: ctx.ownerId, aiActorId: ctx.managerId, runtimeId: managerNodeId, ...managerRuntimeKind ? { runtimeKind: managerRuntimeKind } : {}, runtimeSessionId: null, title, touchedAt: now, createdAt: now, workspace: ctx.workspace }).catch(() => void 0);
138701
139017
  await chatStore.linkWorkOrder(chatSessionId, ctx.workspace).catch(() => void 0);
138702
139018
  }
138703
139019
  discussSessions.set(itemKey, chatSessionId);
138704
- await chatStore.appendMessage({ id: randomUUID22(), sessionId: chatSessionId, role: "user", content: ctx.seed, createdAt: (/* @__PURE__ */ new Date()).toISOString() }).catch(() => void 0);
138705
- }
138706
- let session;
138707
- try {
138708
- session = await opts.dispatchChat({ actorId: ctx.managerId, message: ctx.seed, workspace: ctx.workspace, ...retainedRt ? { sessionId: retainedRt } : {}, ...chatSessionId ? { chatSessionId } : {} });
138709
- } catch (err) {
138710
- if (createdThisCall && chatStore && chatSessionId) {
138711
- await chatStore.deleteSession(chatSessionId).catch(() => void 0);
138712
- discussSessions.delete(itemKey);
138713
- }
138714
- throw err;
138715
- }
138716
- if (chatStore && chatSessionId) {
138717
- const sid = chatSessionId;
138718
- let reply = "";
138719
- session.onOutput((chunk) => {
138720
- reply += chunk;
138721
- });
138722
- void session.done.then(async () => {
138723
- const { randomUUID: randomUUID22 } = await import("node:crypto");
138724
- if (reply || session.runId) {
138725
- await chatStore.appendMessage({ id: randomUUID22(), sessionId: sid, role: "assistant", content: reply, createdAt: (/* @__PURE__ */ new Date()).toISOString(), ...session.runId ? { runId: session.runId } : {} }).catch(() => void 0);
138726
- }
138727
- }).catch(() => void 0);
138728
139020
  }
138729
139021
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ sessionId: chatSessionId ?? null, managerId: ctx.managerId, workspace: ctx.workspace }));
138730
139022
  } catch (err) {
@@ -139313,8 +139605,8 @@ async function startOasisServer(opts) {
139313
139605
  if (actorCtx) {
139314
139606
  const { mkdtempSync: mkdtempSync5, mkdirSync: mkdirSync17, writeFileSync: writeFileSync15 } = await import("node:fs");
139315
139607
  const { join: join24, dirname: dirname20 } = await import("node:path");
139316
- const { tmpdir: tmpdir8 } = await import("node:os");
139317
- const dir = mkdtempSync5(join24(tmpdir8(), "oasis-chat-"));
139608
+ const { tmpdir: tmpdir9 } = await import("node:os");
139609
+ const dir = mkdtempSync5(join24(tmpdir9(), "oasis-chat-"));
139318
139610
  if (actorCtx.config?.prompt) {
139319
139611
  for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
139320
139612
  const file = join24(dir, rel);
@@ -143613,6 +143905,7 @@ async function seedRoleCatalog(store, src) {
143613
143905
  await store.put({
143614
143906
  name,
143615
143907
  label: known?.label ?? fallbackLabel(name),
143908
+ ...known?.description ? { description: known.description } : {},
143616
143909
  ...known?.accent ? { appearance: { accent: known.accent } } : {},
143617
143910
  status: "active"
143618
143911
  });
@@ -143627,13 +143920,17 @@ var init_roles_seed = __esm({
143627
143920
  "use strict";
143628
143921
  KNOWN = {
143629
143922
  founder: { label: "\u521B\u59CB\u4EBA", accent: "slate" },
143630
- pm: { label: "\u9700\u6C42\u5206\u6790", accent: "blue" },
143631
- architect: { label: "\u67B6\u6784\u8BBE\u8BA1", accent: "violet" },
143632
- designer: { label: "\u754C\u9762\u8BBE\u8BA1", accent: "fuchsia" },
143633
- research: { label: "\u5E02\u573A\u8C03\u7814", accent: "cyan" },
143634
- dev: { label: "\u4EE3\u7801\u5B9E\u73B0", accent: "emerald" },
143635
- qa: { label: "\u8D28\u91CF\u4FDD\u969C", accent: "rose" },
143636
- devops: { label: "\u8FD0\u7EF4\u90E8\u7F72", accent: "amber" },
143923
+ pm: { label: "\u9700\u6C42\u5206\u6790", accent: "blue", description: "\u628A\u8BC9\u6C42\u62C6\u6210 PRD \u4E0E\u9A8C\u6536\u6807\u51C6\uFF0C\u505A\u4EA7\u54C1\u51B3\u7B56\u4E0E\u4F18\u5148\u7EA7\u3002" },
143924
+ architect: { label: "\u67B6\u6784\u8BBE\u8BA1", accent: "violet", description: "\u6280\u672F\u9009\u578B\u3001\u7CFB\u7EDF\u67B6\u6784\u3001\u63A5\u53E3\u5951\u7EA6\u3001ADR\uFF1B\u628A\u6280\u672F\u65B9\u6848\u843D\u6210\u53EF\u5B9E\u73B0\u7684\u51B3\u7B56\u3002" },
143925
+ designer: { label: "\u754C\u9762\u8BBE\u8BA1", accent: "fuchsia", description: "\u754C\u9762\u4E0E\u4EA4\u4E92\u8BBE\u8BA1\u3001\u8BBE\u8BA1\u7CFB\u7EDF\u4E0E\u89C6\u89C9\u89C4\u8303\u3002" },
143926
+ research: { label: "\u5E02\u573A\u8C03\u7814", accent: "cyan", description: "\u7ADE\u54C1\u3001\u5E02\u573A\u3001\u7528\u6237\u4E0E\u5B9A\u4EF7\u7B49\u8C03\u7814\u3002" },
143927
+ dev: { label: "\u4EE3\u7801\u5B9E\u73B0", accent: "emerald", description: "\u6309\u63A5\u53E3\u5951\u7EA6\u5B9E\u73B0\u524D\u540E\u7AEF\u4EE3\u7801\u3002" },
143928
+ qa: { label: "\u8D28\u91CF\u4FDD\u969C", accent: "rose", description: "\u6D4B\u8BD5\u89C4\u5212\u3001\u771F\u673A\u9A8C\u6536\u4E0E\u56DE\u5F52\uFF1B\u53D1\u73B0\u95EE\u9898\u56DE\u538B\u5BF9\u5E94\u4EA7\u51FA\u8282\u70B9\u3002" },
143929
+ devops: {
143930
+ label: "\u8FD0\u7EF4\u90E8\u7F72",
143931
+ accent: "amber",
143932
+ description: "\u5E73\u53F0\u5DE5\u7A0B\uFF1A\u4EE3\u7801\u4ED3\u5E93\u4F9B\u7ED9\uFF08\u5EFA\u4ED3\uFF09\u3001CI/CD \u6D41\u6C34\u7EBF\u4E0E GitHub Actions\u3001\u7EC4\u7EC7\u8D44\u4EA7\u53F0\u8D26\u7EF4\u62A4\uFF0C\u4EE5\u53CA\u771F\u5B9E\u90E8\u7F72\u4E0E\u8054\u8C03\uFF1B\u5176\u5B83\u73AF\u8282\u51FA\u73B0\u90E8\u7F72/\u8FD0\u7EF4\u7C7B\u95EE\u9898\u65F6\u515C\u5E95\u3002\u8D1F\u8D23 provision \u4E0E deploy \u4EA4\u4ED8\u7269\u7C7B\u578B\u3002"
143933
+ },
143637
143934
  writer: { label: "\u6587\u6863\u64B0\u5199", accent: "slate" },
143638
143935
  "design-lead": { label: "\u8BBE\u8BA1\u8D1F\u8D23\u4EBA", accent: "fuchsia" },
143639
143936
  "eng-lead": { label: "\u6280\u672F\u8D1F\u8D23\u4EBA", accent: "violet" }
@@ -146792,13 +147089,46 @@ async function startDesignRun(base, fetchImpl, args) {
146792
147089
  async function fetchResultPackage(base, fetchImpl, runId) {
146793
147090
  const body = await odApi(base, fetchImpl, "GET", `/api/runs/${encodeURIComponent(runId)}/result-package`);
146794
147091
  const artifacts = (body.artifacts ?? []).map((a) => ({
146795
- path: a.path ?? "",
147092
+ path: a.path ?? a.file ?? "",
146796
147093
  kind: a.kind ?? null,
146797
147094
  title: a.title ?? null,
146798
147095
  status: a.status ?? null
146799
147096
  }));
146800
147097
  return { artifacts, fileCount: body.project?.fileCount ?? artifacts.length };
146801
147098
  }
147099
+ async function fetchBoardStatus(base, fetchImpl, artifactId) {
147100
+ const boardKey = deriveBoardKey(artifactId);
147101
+ let boardExists = true;
147102
+ let artifacts = [];
147103
+ try {
147104
+ const listing = await odApi(
147105
+ base,
147106
+ fetchImpl,
147107
+ "GET",
147108
+ `/api/projects/${encodeURIComponent(boardKey)}/files`
147109
+ );
147110
+ artifacts = (listing.files ?? []).filter((f2) => !f2.path.startsWith("task-context/") && f2.artifactManifest !== void 0).map((f2) => ({ path: f2.path, kind: f2.kind ?? null, status: f2.artifactManifest?.status ?? null, mtime: f2.mtime ?? null })).sort((a, b2) => (b2.mtime ?? 0) - (a.mtime ?? 0));
147111
+ } catch {
147112
+ boardExists = false;
147113
+ }
147114
+ let runs = [];
147115
+ if (boardExists) {
147116
+ try {
147117
+ const body = await odApi(
147118
+ base,
147119
+ fetchImpl,
147120
+ "GET",
147121
+ `/api/runs?projectId=${encodeURIComponent(boardKey)}`
147122
+ );
147123
+ runs = (body.runs ?? []).map((r) => ({
147124
+ runId: String(r["runId"] ?? r["id"] ?? ""),
147125
+ status: String(r["status"] ?? "unknown")
147126
+ }));
147127
+ } catch {
147128
+ }
147129
+ }
147130
+ return { boardKey, boardExists, runs, artifacts };
147131
+ }
146802
147132
  async function pumpDesignEvents(base, fetchImpl, runId, onLine) {
146803
147133
  const res = await fetchImpl(`${base}/api/runs/${runId}/events`);
146804
147134
  if (!res.ok || !res.body) return;
@@ -146852,11 +147182,16 @@ async function harvestBoard(base, fetchImpl, publicBase, workRoot, boardKey, run
146852
147182
  if (!res.ok) throw new Error(`od export ${rel} \u2192 ${res.status}`);
146853
147183
  return await res.text();
146854
147184
  };
147185
+ const fetchRaw = async (rel) => {
147186
+ const res = await fetchImpl(`${base}/api/projects/${encodeURIComponent(boardKey)}/raw/${rel.split("/").map(encodeURIComponent).join("/")}`);
147187
+ if (!res.ok) throw new Error(`od raw ${rel} \u2192 ${res.status}`);
147188
+ return await res.text();
147189
+ };
146855
147190
  fs8.writeFileSync(path7.join(dir, "index.html"), await fetchInline(entry));
146856
147191
  for (const f2 of listing.files ?? []) {
146857
147192
  if (f2.path.endsWith(".md") && !f2.path.includes("/")) {
146858
147193
  try {
146859
- fs8.writeFileSync(path7.join(dir, f2.path), await fetchInline(f2.path));
147194
+ fs8.writeFileSync(path7.join(dir, f2.path), await fetchRaw(f2.path));
146860
147195
  } catch {
146861
147196
  }
146862
147197
  }
@@ -146895,7 +147230,7 @@ async function runOpenDesignSession(opts) {
146895
147230
  const { dir, entry } = await harvestBoard(opts.base, fetchImpl, opts.publicBase, workRoot, boardKey, runId);
146896
147231
  return { dir, entry, boardKey, runId };
146897
147232
  }
146898
- async function runDesignChatTurn(opts) {
147233
+ async function startDesignChatTurn(opts) {
146899
147234
  const fetchImpl = opts.fetchImpl ?? fetch;
146900
147235
  const boardKey = deriveBoardKey(opts.artifactId);
146901
147236
  const { conversationId, resolvedDir } = await ensureProject(opts.base, fetchImpl, boardKey, opts.title);
@@ -146908,6 +147243,11 @@ async function runDesignChatTurn(opts) {
146908
147243
  message: opts.message,
146909
147244
  ...opts.systemPrompt !== void 0 ? { systemPrompt: opts.systemPrompt } : {}
146910
147245
  });
147246
+ return { boardKey, conversationId, runId, userMessageId };
147247
+ }
147248
+ async function runDesignChatTurn(opts) {
147249
+ const fetchImpl = opts.fetchImpl ?? fetch;
147250
+ const { boardKey, conversationId, runId, userMessageId } = await startDesignChatTurn(opts);
146911
147251
  if (opts.onLine) void pumpDesignEvents(opts.base, fetchImpl, runId, opts.onLine).catch(() => {
146912
147252
  });
146913
147253
  const status = await pollDesignRun(opts.base, fetchImpl, runId, {
@@ -147300,12 +147640,12 @@ function scrubLeakyEnv(env) {
147300
147640
  }
147301
147641
  function classifyExit(result) {
147302
147642
  if (!result) return void 0;
147303
- const text = `${String(result.subtype ?? "")} ${String(result.result ?? "")}`.toLowerCase();
147304
- if (text.includes("output token") || text.includes("max_tokens") || text.includes("output_token")) return "output-limit";
147305
- if (text.includes("rate") || text.includes("overloaded") || text.includes("429") || text.includes("529")) return "rate-limit";
147306
- if (result.subtype === "success" && result.is_error !== true) return "clean";
147307
- if (result.is_error === true || String(result.subtype ?? "").startsWith("error")) return "error";
147308
- return void 0;
147643
+ if (result.stop_reason === "max_tokens") return "output-limit";
147644
+ const subtypeSaysError = String(result.subtype ?? "").startsWith("error");
147645
+ if (result.is_error !== true && !subtypeSaysError) return "clean";
147646
+ const status = Number(result.api_error_status);
147647
+ if (status === 429 || status === 529) return "rate-limit";
147648
+ return "error";
147309
147649
  }
147310
147650
  var import_node_child_process7, import_node_crypto13, fs10, path9, readline, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
147311
147651
  var init_claude_code = __esm({
@@ -157335,7 +157675,7 @@ ${body || "\uFF08\u672A\u586B\u5199\uFF09"}`;
157335
157675
  "\u7531\u300C\u5267\u672C \xB7 Bug \u4FEE\u590D\u300D\u53D1\u8D77\u3002"
157336
157676
  ].join("\n");
157337
157677
  const plan = {
157338
- nodes: BUG_FIX_STRUCTURE.map((n) => ({ type: n.type, title: n.title, dependsOn: n.dependsOn, brief: n.brief(title) }))
157678
+ nodes: BUG_FIX_STRUCTURE.map((n) => ({ type: n.type, title: n.title, dependsOn: n.dependsOn, brief: n.brief(title), ...n.isFinalOutput ? { isFinalOutput: true } : {} }))
157339
157679
  };
157340
157680
  return {
157341
157681
  produces: "workorder",
@@ -157397,7 +157737,8 @@ var init_registry2 = __esm({
157397
157737
  title: "\u590D\u73B0\u9A8C\u8BC1\u4E0E\u56DE\u5F52",
157398
157738
  dependsOn: ["dev:\u4FEE\u590D\u5B9E\u73B0"],
157399
157739
  role: "\u6D4B\u8BD5",
157400
- brief: () => `\u6309 brief \u91CC\u7684\u590D\u73B0\u6B65\u9AA4\u9A8C\u8BC1 bug \u5DF2\u4FEE\u590D\uFF08\u4E0D\u518D\u590D\u73B0\uFF09\uFF1B\u8865\u5145\u5E76\u8FD0\u884C\u56DE\u5F52\u7528\u4F8B\uFF0C\u786E\u8BA4\u6CA1\u6709\u5F15\u5165\u65B0\u56DE\u5F52\u3002\u4EA7\u51FA\u6D4B\u8BD5\u62A5\u544A\u3002`
157740
+ brief: () => `\u6309 brief \u91CC\u7684\u590D\u73B0\u6B65\u9AA4\u9A8C\u8BC1 bug \u5DF2\u4FEE\u590D\uFF08\u4E0D\u518D\u590D\u73B0\uFF09\uFF1B\u8865\u5145\u5E76\u8FD0\u884C\u56DE\u5F52\u7528\u4F8B\uFF0C\u786E\u8BA4\u6CA1\u6709\u5F15\u5165\u65B0\u56DE\u5F52\u3002\u4EA7\u51FA\u6D4B\u8BD5\u62A5\u544A\u3002`,
157741
+ isFinalOutput: true
157401
157742
  }
157402
157743
  ];
157403
157744
  BUILTIN2 = {
@@ -158999,6 +159340,94 @@ var init_builtin_skills = __esm({
158999
159340
  }
159000
159341
  });
159001
159342
 
159343
+ // ../server/src/design/board-watch.ts
159344
+ function decideBoardLetter(c, nowMs, quietMs) {
159345
+ if (c.boardMtime === null) return false;
159346
+ if (!c.hasRevision) return false;
159347
+ if (c.lastOpAtMs === null || c.boardMtime <= c.lastOpAtMs) return false;
159348
+ if (nowMs - c.boardMtime < quietMs) return false;
159349
+ if (c.hasOpenWatchLetter) return false;
159350
+ if (c.lastWatchMtime !== null && c.boardMtime <= c.lastWatchMtime) return false;
159351
+ return true;
159352
+ }
159353
+ var MARKER_PREFIX, boardWatchMarker, parseMarker, BoardWatcher;
159354
+ var init_board_watch = __esm({
159355
+ "../server/src/design/board-watch.ts"() {
159356
+ "use strict";
159357
+ init_src2();
159358
+ MARKER_PREFIX = "[od-board-watch mtime:";
159359
+ boardWatchMarker = (mtimeMs) => `${MARKER_PREFIX}${mtimeMs}]`;
159360
+ parseMarker = (body) => {
159361
+ const i = body.indexOf(MARKER_PREFIX);
159362
+ if (i < 0) return null;
159363
+ const n = Number.parseInt(body.slice(i + MARKER_PREFIX.length), 10);
159364
+ return Number.isFinite(n) ? n : null;
159365
+ };
159366
+ BoardWatcher = class {
159367
+ constructor(deps) {
159368
+ this.deps = deps;
159369
+ }
159370
+ /** 查一块板的交付物(排 task-context/、只算带 artifactManifest 的)。404/网络错 → null(板不存在)。 */
159371
+ async boardFiles(boardKey) {
159372
+ const fetchImpl = this.deps.fetchImpl ?? fetch;
159373
+ try {
159374
+ const res = await fetchImpl(`${this.deps.odBase}/api/projects/${encodeURIComponent(boardKey)}/files`);
159375
+ if (!res.ok) return null;
159376
+ const body = await res.json();
159377
+ return (body.files ?? []).filter((f2) => !f2.path.startsWith("task-context/") && f2.artifactManifest !== void 0).map((f2) => ({ path: f2.path, kind: f2.kind ?? null, mtime: f2.mtime ?? null }));
159378
+ } catch {
159379
+ return null;
159380
+ }
159381
+ }
159382
+ async tick(now = Date.now()) {
159383
+ const { kernel, boardKeyOf, log: log3 } = this.deps;
159384
+ const quietMs = this.deps.quietMs ?? 10 * 6e4;
159385
+ const maxPerTick = this.deps.maxLettersPerTick ?? 2;
159386
+ const actor = this.deps.actor ?? "actor:system";
159387
+ const model = kernel.model;
159388
+ let written = 0;
159389
+ for (const a of model.artifacts.values()) {
159390
+ if (a.type !== "design") continue;
159391
+ if (lifecycleOf(model, a.id) !== "active") continue;
159392
+ if (written >= maxPerTick) {
159393
+ log3?.(`[board-watch] \u672C\u8F6E\u5199\u4FE1\u5DF2\u8FBE\u4E0A\u9650 ${maxPerTick}\uFF0C\u5176\u4F59\u677F\u4E0B\u8F6E\u518D\u770B`);
159394
+ break;
159395
+ }
159396
+ const boardKey = boardKeyOf(a.id);
159397
+ const files = await this.boardFiles(boardKey);
159398
+ if (!files || files.length === 0) continue;
159399
+ const boardMtime = files.reduce((mx, f2) => f2.mtime !== null && (mx === null || f2.mtime > mx) ? f2.mtime : mx, null);
159400
+ const watchLetters = annotationsOf(model, a.id).filter((x2) => x2.author === actor && parseMarker(x2.body) !== null);
159401
+ const lastOpAt = model.lastOpAt.get(a.id);
159402
+ const candidate = {
159403
+ boardMtime,
159404
+ hasRevision: revisionsOf(model, a.id).length > 0,
159405
+ lastOpAtMs: lastOpAt ? Date.parse(lastOpAt) : null,
159406
+ hasOpenWatchLetter: watchLetters.some((x2) => x2.state === "open"),
159407
+ lastWatchMtime: watchLetters.reduce((mx, x2) => {
159408
+ const m2 = parseMarker(x2.body);
159409
+ return m2 !== null && (mx === null || m2 > mx) ? m2 : mx;
159410
+ }, null)
159411
+ };
159412
+ if (!decideBoardLetter(candidate, now, quietMs)) continue;
159413
+ const newest = files.filter((f2) => f2.mtime !== null && candidate.lastOpAtMs !== null && f2.mtime > candidate.lastOpAtMs).sort((x2, y) => (y.mtime ?? 0) - (x2.mtime ?? 0));
159414
+ const listed = (newest.length > 0 ? newest : files).slice(0, 5).map((f2) => `- ${f2.path}\uFF08${f2.kind ?? "?"}\uFF09\xB7 ${f2.mtime ? new Date(f2.mtime).toISOString() : "?"}`).join("\n");
159415
+ const body = [
159416
+ boardWatchMarker(boardMtime),
159417
+ `\u753B\u677F ${boardKey} \u4E0A\u6709 Oasis \u4E4B\u5916\u7684\u65B0\u52A8\u9759\uFF08\u4EBA\u5728\u677F\u4E0A\u5BF9\u8BDD/\u6539\u7A3F\uFF0C\u6216\u5F15\u64CE\u4EA7\u4E86\u6CA1\u4EBA\u6536\u7684\u65B0\u7248\uFF09\uFF0C\u6BD4\u672C\u4EA7\u7269\u6700\u8FD1\u4E00\u6B21\u64CD\u4F5C\uFF08${lastOpAt ?? "?"}\uFF09\u65B0\u3001\u4E14\u5DF2\u9759\u9ED8 ${Math.round(quietMs / 6e4)} \u5206\u949F\uFF1A`,
159418
+ listed,
159419
+ ``,
159420
+ `\u8BF7\u53D6\u8D27\u6838\u5BF9\uFF1A\`oasis design status ${a.id} --pull\` \u2192 \u5185\u5BB9\u5408\u610F\u5C31 \`oasis propose ${a.id} --from-dir <\u76EE\u5F55> --reason "<\u6536\u5F55\u677F\u4E0A\u4EBA\u5DE5\u8FED\u4EE3>"\` \u6536\u5F55\u4E3A\u65B0\u7248\u672C\uFF1B\u4E0D\u5408\u610F/\u4E0D\u5FC5\u6536\u5F55\uFF0C\u56DE\u5E16\u8BF4\u660E\u540E\u6309 wontfix \u5904\u7406\u5373\u53EF\uFF08\u540C\u4E00\u7248\u4E0D\u4F1A\u518D\u6765\u4FE1\uFF09\u3002`
159421
+ ].join("\n");
159422
+ await kernel.annotate({ artifactId: a.id, actor, body, kind: "comment" });
159423
+ written++;
159424
+ log3?.(`[board-watch] ${a.id} \u2190 \u677F ${boardKey} \u6709\u65B0\u5185\u5BB9\uFF08mtime=${new Date(boardMtime).toISOString()}\uFF09\u2192 \u5DF2\u5199\u4FE1\u5524 owner`);
159425
+ }
159426
+ }
159427
+ };
159428
+ }
159429
+ });
159430
+
159002
159431
  // ../server/src/index.ts
159003
159432
  var init_src7 = __esm({
159004
159433
  "../server/src/index.ts"() {
@@ -159055,6 +159484,7 @@ var init_src7 = __esm({
159055
159484
  init_workorder_drafts();
159056
159485
  init_builtin_skills();
159057
159486
  init_connector_skills();
159487
+ init_board_watch();
159058
159488
  }
159059
159489
  });
159060
159490
 
@@ -169772,6 +170202,14 @@ async function startServe(opts) {
169772
170202
  console.error(`[cascade] ${action.artifactId} \u5931\u8D25: ${String(err)}`);
169773
170203
  }
169774
170204
  }
170205
+ for (const { artifactId } of computeAdvanceableRevisions(kernel.model, schemaMap)) {
170206
+ try {
170207
+ const landed = await kernel.advanceOrphans(artifactId);
170208
+ for (const rev of landed) console.log(`[self-heal] ${artifactId} \u8865\u843D\u5B64\u513F\u961F\u9996 ${rev.id}\uFF08\u89E3\u51BB\u540E\u9057\u7559\uFF0C60s \u5FAA\u73AF\u515C\u5E95\uFF09`);
170209
+ } catch (err) {
170210
+ console.error(`[self-heal] ${artifactId} \u8865\u843D\u5931\u8D25: ${String(err)}`);
170211
+ }
170212
+ }
169775
170213
  for (const a of kernel.model.artifacts.values()) {
169776
170214
  if (!a.owner.startsWith("actor:human:")) continue;
169777
170215
  if (lifecycleOf(kernel.model, a.id) !== "active") continue;
@@ -170010,6 +170448,18 @@ async function startServe(opts) {
170010
170448
  const automationTimer = setInterval(() => {
170011
170449
  void automationScheduler.tick().catch((err) => console.error(`[automation-scheduler] tick \u5931\u8D25: ${String(err)}`));
170012
170450
  }, 3e4);
170451
+ let boardWatchTimer;
170452
+ const boardWatchFlag = (process.env["OASIS_BOARD_WATCH"] ?? "").trim().toLowerCase();
170453
+ if (!["0", "false", "off"].includes(boardWatchFlag)) {
170454
+ const odBase = (process.env["OASIS_OPEN_DESIGN_URL"] ?? "http://127.0.0.1:7457").replace(/\/$/, "");
170455
+ const boardWatcher = new BoardWatcher({ kernel, odBase, boardKeyOf: deriveBoardKey, log: (m2) => console.log(m2) });
170456
+ boardWatchTimer = setInterval(() => {
170457
+ void boardWatcher.tick().catch((err) => console.error(`[board-watch] tick \u5931\u8D25: ${String(err)}`));
170458
+ }, 6e4);
170459
+ console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u542F\u7528\uFF08od=${odBase}\uFF0C\u9759\u9ED8\u7A97 10min\uFF0C\u6BCF\u8F6E\u4E0A\u9650 2 \u5C01\u4FE1\uFF1BOASIS_BOARD_WATCH=0 \u53EF\u5173\uFF09`);
170460
+ } else {
170461
+ console.log(`[serve] \u753B\u677F\u76D1\u89C6\u5668\u5DF2\u88AB OASIS_BOARD_WATCH=${boardWatchFlag} \u663E\u5F0F\u5173\u95ED`);
170462
+ }
170013
170463
  return {
170014
170464
  baseUrl: server.baseUrl,
170015
170465
  operatorToken,
@@ -170022,6 +170472,7 @@ async function startServe(opts) {
170022
170472
  if (livenessTimer) clearInterval(livenessTimer);
170023
170473
  if (orphanSweepTimer) clearInterval(orphanSweepTimer);
170024
170474
  clearInterval(automationTimer);
170475
+ if (boardWatchTimer) clearInterval(boardWatchTimer);
170025
170476
  clearInterval(serverHeartbeatTimer);
170026
170477
  hub?.close();
170027
170478
  await server.close();
@@ -170681,9 +171132,15 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
170681
171132
  design run <artifactId> [--brief <t>] [--reason <t>] [--od-url <url>] [--title <t>] [--agent <id>]
170682
171133
  # design \u4EA7\u7269\uFF1A\u5728\u901A\u7528 runtime \u4E0A\u9A71\u52A8 open-design \u753B\u677F\u5F15\u64CE\u4EA7\u754C\u9762\u7A3F\u5E76\u4EA4\u7A3F\uFF08\u63D0\u6848 design-node-runtime-to-cli-tool\uFF09\u3002
170683
171134
  # --brief \u7F3A\u7701\u8BFB\u5F53\u524D\u76EE\u5F55 DESIGN_BRIEF.md\uFF08\u5176\u6B21 TASK.md\uFF09\uFF1B\u4E0A\u4E0B\u6587\u53D6\u5F53\u524D\u76EE\u5F55 inputs/\uFF1Bod \u5730\u5740\u7F3A\u7701 $OASIS_OPEN_DESIGN_URL \u6216\u672C\u673A 7457\u3002
171135
+ design chat <artifactId> --message <t> [--turn first|continue] [--async] [--system <t>]
171136
+ # \u591A\u8F6E\u5BF9\u8BDD\u9A71\u52A8\u753B\u677F\uFF08\u63D0\u6848 design-conversational-driving\uFF09\uFF1A\u4E0D\u81EA\u52A8\u4EA4\u7A3F\uFF0C\u8BFB\u4EA7\u7269\u540E\u81EA\u884C propose/gap\u3002
171137
+ # --async \u8D77\u5B8C\u5373\u8FD4\uFF08\u5F15\u64CE\u5728\u670D\u52A1\u7AEF\u8DD1 13\u201344min\uFF0C\u4E0E\u672C\u8FDB\u7A0B\u65E0\u5173\uFF09\uFF1B\u8FDB\u5EA6/\u53D6\u8D27\u7528 design status\u3002
171138
+ design status <artifactId> [--pull]
171139
+ # \u67E5\u753B\u677F\u73B0\u72B6\uFF08\u6062\u590D\u4F18\u5148\uFF09\uFF1A\u88AB\u6D3E\u53D1\u5148\u8DD1\u5B83\u2014\u2014\u5F15\u64CE\u5728\u8DD1\u5C31\u522B\u8D77\u65B0\u8F6E\uFF1B\u677F\u4E0A\u6709\u8D27 --pull \u62C9\u672C\u5730\u8BFB\uFF0C\u8BFB\u5B8C propose --from-dir\u3002
170684
171140
 
170685
171141
  \u56FE\u673A\u5236\uFF08\xA76/\xA77\uFF09
170686
171142
  annotate <artifactId> --body <text> [--from <\u4E0B\u6E38> --blocks true]
171143
+ record-action <artifactId> --name <\u52A8\u4F5C\u540D> [--pass true] [--result <json>] [--params <json>] # \xA79.2 grounding \u7559\u75D5\uFF1A\u4E3A action_vs_oplog \u8BB0\u4E00\u6761\u901A\u8FC7\u7684 tool_call\uFF08\u771F\u8DD1\u8FC7\u5065\u5EB7\u63A2\u9488\u540E record-action --name health_probe --pass true\uFF09\uFF1B\u58F0\u660E\u4E86 requiredActions \u7684\u7C7B\u578B\u6CA1\u6709\u5B83 conclude \u4F1A\u88AB\u673A\u68B0\u62E6
170687
171144
  resolve <annotationId> [--as resolved|wontfix|acknowledged]
170688
171145
  gap <artifactId> --desc <text>
170689
171146
  add-revision <artifactId> --desc <\u8981\u5B83\u5B8C\u6210\u4EC0\u4E48> [--role <\u8C01\u6765\u505A\uFF0C\u9ED8\u8BA4owner\u81EA\u5DF1>] # \xA79 \u592A\u5927\u5C31\u6D3E\u4E00\u6761\u5B50revision\u51FA\u53BB\u5E76\u884C\uFF0C\u6D3E\u5B8C\u53EF\u6536\u5DE5\u7B49\u7ED3\u679C
@@ -170742,7 +171199,7 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
170742
171199
  function parseArgs(argv) {
170743
171200
  const positional = [];
170744
171201
  const flags = /* @__PURE__ */ new Map();
170745
- const booleanFlags = /* @__PURE__ */ new Set(["checkpoint", "continue"]);
171202
+ const booleanFlags = /* @__PURE__ */ new Set(["checkpoint", "continue", "async", "pull"]);
170746
171203
  for (let i = 0; i < argv.length; i++) {
170747
171204
  const a = argv[i];
170748
171205
  if (a.startsWith("--")) {
@@ -171594,15 +172051,25 @@ ${res.warning}`);
171594
172051
  }
171595
172052
  }
171596
172053
  }
171597
- println(`[design] \u753B\u677F ${deriveBoardKey(artifactId2)} \u2190 ${artifactId2}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odBase2} \u2026`);
171598
- const turn = await runDesignChatTurn({
172054
+ const baseTurnOpts = {
171599
172055
  base: odBase2,
171600
172056
  agentId: flags.get("agent") ?? "claude",
171601
172057
  artifactId: artifactId2,
171602
172058
  title: flags.get("title") ?? artifactId2,
171603
172059
  message: message2,
171604
172060
  ...flags.get("system") !== void 0 ? { systemPrompt: flags.get("system") } : {},
171605
- ...isContinue ? {} : { contextFiles: contextFiles2 },
172061
+ ...isContinue ? {} : { contextFiles: contextFiles2 }
172062
+ };
172063
+ if (flags.get("async") === "true") {
172064
+ println(`[design] \u753B\u677F ${deriveBoardKey(artifactId2)} \u2190 ${artifactId2}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\uFF08--async\uFF09\u2192 od \u5F15\u64CE ${odBase2} \u2026`);
172065
+ const started = await startDesignChatTurn(baseTurnOpts);
172066
+ println(`[design] \u5DF2\u4E0B\u5355\uFF1Arun ${started.runId.slice(0, 8)}\uFF08board ${started.boardKey}, \u53EF\u89C1 user \u56DE\u5408 ${started.userMessageId.slice(0, 8)}\uFF09\u3002\u5F15\u64CE\u901A\u5E38\u8981 13\u201344 \u5206\u949F\u3002`);
172067
+ println(`[design] \u4E4B\u540E\u7528 oasis design status ${artifactId2} \u67E5\u8FDB\u5EA6/\u53D6\u8D27\uFF08--pull \u62C9\u4EA7\u7269\u5230\u672C\u5730\u8BFB\uFF09\uFF1B\u4E0D\u5FC5\u5B88\u7740\u7B49\u3002`);
172068
+ break;
172069
+ }
172070
+ println(`[design] \u753B\u677F ${deriveBoardKey(artifactId2)} \u2190 ${artifactId2}\uFF1B${isContinue ? "\u7EED\u8F6E" : "\u9996\u8F6E"}\u5BF9\u8BDD\u9A71\u52A8 od \u5F15\u64CE ${odBase2} \u2026`);
172071
+ const turn = await runDesignChatTurn({
172072
+ ...baseTurnOpts,
171606
172073
  onLine: (line) => println(`[od] ${line.length > 500 ? line.slice(0, 500) + "\u2026" : line}`)
171607
172074
  });
171608
172075
  println(`[design] \u672C\u8F6E\u4EA4\u4ED8\uFF08board ${turn.boardKey}, run ${turn.runId.slice(0, 8)}, \u53EF\u89C1 user \u56DE\u5408 ${turn.userMessageId.slice(0, 8)}\uFF09\uFF1A`);
@@ -171610,7 +172077,39 @@ ${res.warning}`);
171610
172077
  println(`[design] \u8BFB\u4EA7\u7269\u540E\u81EA\u884C\u51B3\u5B9A\uFF1A\u591F\u597D \u2192 oasis propose\uFF1B\u6709\u5206\u6B67/\u7F3A\u4FE1\u606F \u2192 oasis gap\uFF1B\u4E0D\u591F \u2192 \u518D\u4E00\u8F6E oasis design chat ${artifactId2} --turn continue --message "<\u4E0B\u4E00\u53E5>"`);
171611
172078
  break;
171612
172079
  }
171613
- if (sub !== "run") throw new Error("\u7528\u6CD5\uFF1Aoasis design run <artifactId> \u2026\uFF0C\u6216 oasis design chat <artifactId> --message <t> [--turn first|continue]");
172080
+ if (sub === "status") {
172081
+ const artifactId2 = needPos(positional, 1, "oasis design status <artifactId> [--pull]");
172082
+ const odBase2 = (flags.get("od-url") ?? process.env["OASIS_OPEN_DESIGN_URL"] ?? "http://127.0.0.1:7457").replace(/\/$/, "");
172083
+ const st = await fetchBoardStatus(odBase2, fetch, artifactId2);
172084
+ if (!st.boardExists) {
172085
+ println(`[design] \u753B\u677F ${st.boardKey} \u5C1A\u4E0D\u5B58\u5728\u2014\u2014\u8FD8\u6CA1\u8D77\u8FC7\u4EFB\u4F55\u4E00\u8F6E\u3002\u5F00\u5DE5\uFF1Aoasis design chat ${artifactId2} --message "<\u8BBE\u8BA1\u9700\u6C42+\u771F\u5B9E\u6570\u636E>" --async`);
172086
+ break;
172087
+ }
172088
+ const live = st.runs.filter((r) => r.status === "running" || r.status === "queued");
172089
+ if (live.length > 0) {
172090
+ for (const r of live) println(`[design] \u5F15\u64CE\u5728\u8DD1\uFF1Arun ${r.runId.slice(0, 8)} \u72B6\u6001=${r.status}\u2014\u2014\u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u8FC7\u51E0\u5206\u949F\u518D status\uFF0C\u6216\u672C\u8F6E\u6536\u5DE5\u7B49\u91CD\u6D3E\u3002`);
172091
+ } else if (st.runs.length > 0) {
172092
+ for (const r of st.runs.slice(0, 3)) println(`[design] \u8FD1\u671F run\uFF1A${r.runId.slice(0, 8)} \u7EC8\u6001=${r.status}`);
172093
+ } else {
172094
+ println(`[design] \u65E0\u5728\u4E16 run \u8BB0\u5F55\uFF08od \u91CD\u542F\u540E\u4E3A\u7A7A\uFF0C\u5C5E\u6B63\u5E38\uFF09\u2014\u2014\u770B\u4E0B\u9762\u677F\u4E0A\u4EA7\u7269\u5373\u6301\u4E45\u771F\u76F8\u3002`);
172095
+ }
172096
+ if (st.artifacts.length === 0) {
172097
+ println(`[design] \u677F\u4E0A\u8FD8\u6CA1\u6709\u4EA4\u4ED8\u7269\u3002${live.length > 0 ? "\u7B49\u5F15\u64CE\u8DD1\u5B8C\u518D\u6765\u3002" : `\u8D77\u4E00\u8F6E\uFF1Aoasis design chat ${artifactId2} --message "<\u2026>" --async`}`);
172098
+ break;
172099
+ }
172100
+ println(`[design] \u677F\u4E0A\u4EA4\u4ED8\u7269\uFF08\u6301\u4E45\uFF0C\u65B0\u2192\u65E7\uFF09\uFF1A`);
172101
+ for (const a of st.artifacts) println(` - ${a.path}\uFF08${a.kind ?? "?"}\uFF09status=${a.status ?? "?"}${a.mtime ? ` \xB7 ${new Date(a.mtime).toISOString()}` : ""}`);
172102
+ if (flags.get("pull") === "true") {
172103
+ const odPublic2 = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
172104
+ const { dir: dir3, entry: entry2 } = await harvestBoard(odBase2, fetch, odPublic2, os8.tmpdir(), st.boardKey, st.runs[0]?.runId ?? "status-pull");
172105
+ println(`[design] \u5DF2\u62C9\u5230\u672C\u5730\uFF1A${dir3}\uFF08\u5165\u53E3 ${entry2}\uFF09`);
172106
+ println(`[design] \u8BFB\u5B8C\u6EE1\u610F\u5373\u4EA4\u7A3F\uFF1Aoasis propose ${artifactId2} --from-dir ${dir3} --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"`);
172107
+ } else {
172108
+ println(`[design] \u53D6\u8D27\uFF1Aoasis design status ${artifactId2} --pull\uFF08\u62C9\u5230\u672C\u5730\u76EE\u5F55\u8BFB\uFF0C\u8BFB\u5B8C propose --from-dir \u4EA4\u7A3F\uFF09`);
172109
+ }
172110
+ break;
172111
+ }
172112
+ if (sub !== "run") throw new Error("\u7528\u6CD5\uFF1Aoasis design run <artifactId> \u2026\uFF1Boasis design chat <artifactId> --message <t> [--turn first|continue] [--async]\uFF1Boasis design status <artifactId> [--pull]");
171614
172113
  const artifactId = needPos(positional, 1, "oasis design run <artifactId>");
171615
172114
  const odBase = (flags.get("od-url") ?? process.env["OASIS_OPEN_DESIGN_URL"] ?? "http://127.0.0.1:7457").replace(/\/$/, "");
171616
172115
  const odPublic = (flags.get("od-public-url") ?? process.env["OASIS_OPEN_DESIGN_PUBLIC_URL"] ?? "https://design.open-friday.com").replace(/\/$/, "");
@@ -171708,6 +172207,18 @@ ${res.warning}`);
171708
172207
  println(message);
171709
172208
  break;
171710
172209
  }
172210
+ case "record-action": {
172211
+ const artifactId = needPos(positional, 0, "oasis record-action <artifactId> --name <\u52A8\u4F5C\u540D> [--pass true] [--result <json>] [--params <json>]");
172212
+ const result = flags.get("result") !== void 0 ? JSON.parse(flags.get("result")) : { pass: flags.get("pass") === "true" };
172213
+ const { message } = await api.cmd("toolCall", {
172214
+ artifactId,
172215
+ name: need(flags, "name"),
172216
+ ...flags.get("params") !== void 0 ? { params: JSON.parse(flags.get("params")) } : {},
172217
+ result
172218
+ });
172219
+ println(message);
172220
+ break;
172221
+ }
171711
172222
  case "reply": {
171712
172223
  const { message } = await api.cmd("reply", {
171713
172224
  annotationId: needPos(positional, 0, "oasis reply <annotationId> --body <\u7B54\u590D>"),
@@ -172270,7 +172781,7 @@ ${res.warning}`);
172270
172781
  init_src6();
172271
172782
 
172272
172783
  // src/index.ts
172273
- var PKG_VERSION = true ? "0.1.61" : "dev";
172784
+ var PKG_VERSION = true ? "0.1.63" : "dev";
172274
172785
  var OASIS_DIR = path21.join(os9.homedir(), ".oasis");
172275
172786
  var CONFIG_FILE = path21.join(OASIS_DIR, "node-config.json");
172276
172787
  var PID_FILE = path21.join(OASIS_DIR, "node.pid");