oasis_test 0.1.62 → 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 +674 -296
  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,28 +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\u3002`,
3871
- ``,
3872
- `**\u7B2C 0 \u6B65\uFF08\u6BCF\u6B21\u88AB\u6D3E\u53D1\u90FD\u5148\u505A\uFF0C\u518D\u51B3\u5B9A\u5F00\u4E0D\u5F00\u65B0\u8F6E\uFF09\u2014\u2014\u5148\u53D6\u8D27\uFF1A**`,
3873
- ``,
3874
- ` oasis design status ${artifactId}`,
3875
- ``,
3876
- `- \u677F\u4E0A**\u5DF2\u6709\u8DD1\u5B8C\u7684\u4EA4\u4ED8\u7269**\uFF08\u4E0A\u4E00\u4E2A\u4F1A\u8BDD\u59D4\u6258\u7684\u5F15\u64CE\u8DD1\u5B8C\u4E86\uFF0C\u8D27\u6CA1\u4EBA\u6536\uFF09\u2192 \`--pull\` \u62C9\u5230\u672C\u5730**\u76F4\u63A5\u8FDB\u300C\u8BFB\u4EA7\u7269\u300D**\uFF0C\u522B\u91CD\u8DD1\u5F15\u64CE\u767D\u70E7 20-40 \u5206\u949F\uFF1B`,
3877
- `- **\u5F15\u64CE\u8FD8\u5728\u8DD1** \u2192 \u522B\u518D\u8D77\u65B0\u8F6E\uFF1B\u8FC7\u51E0\u5206\u949F\u518D status\uFF0C\u6216\u672C\u8F6E\u6536\u5DE5\uFF08\u5F15\u64CE\u5728\u670D\u52A1\u7AEF\u81EA\u5DF1\u8DD1\u5B8C\uFF0C\u4E0B\u6B21\u6D3E\u53D1\u6765\u53D6\uFF09\uFF1B`,
3878
- `- \u4EC0\u4E48\u90FD\u6CA1\u6709 \u2192 \u8D77\u9996\u8F6E\u3002`,
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`,
3879
3988
  ``,
3880
- `**\u8D77\u4E00\u8F6E\uFF08\u4E0D\u963B\u585E\uFF0C\u79D2\u56DE\uFF09\uFF1A**`,
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`,
3881
3990
  ``,
3882
- ` oasis design chat ${artifactId} --message "<\u8FD9\u4E00\u8F6E\u8981\u5F15\u64CE\u505A\u4EC0\u4E48\uFF1A\u8BBE\u8BA1\u9700\u6C42 + \u771F\u5B9E\u6570\u636E>" --async`,
3883
- ` oasis design chat ${artifactId} --turn continue --message "<\u8BFB\u5B8C\u4E0A\u4E00\u7248\u4EA7\u7269\u540E\uFF0C\u8981\u6539\u54EA\u91CC>" --async`,
3884
- ``,
3885
- `\u5F15\u64CE\u4E00\u8F6E\u8981 13\u201344 \u5206\u949F\uFF0C\u5728 od \u670D\u52A1\u7AEF\u81EA\u5DF1\u8DD1\u3001**\u4E0E\u4F60\u7684\u4F1A\u8BDD\u751F\u6B7B\u65E0\u5173**\u2014\u2014\`--async\` \u4E0B\u5355\u5373\u8FD4\uFF0C**\u4E0D\u8981\u81EA\u5DF1\u53D1\u660E\u963B\u585E\u7B49\u5F85\u3001\u66F4\u522B\u7528 \`timeout\` \u5305\u547D\u4EE4**\u3002\u4E0B\u5355\u540E\u6BCF\u9694\u51E0\u5206\u949F \`oasis design status\` \u770B\u4E00\u773C\uFF1B\u4E00\u76F4\u6CA1\u5B8C\u4E5F\u53EF\u4EE5\u6536\u5DE5\uFF0C\u4E0B\u6B21\u6D3E\u53D1\u5148 status \u53D6\u8D27\u3002`,
3886
- ``,
3887
- `**\u6BCF\u4E00\u8F6E\u7684\u95ED\u73AF\uFF08\u522B\u8DF3\u6B65\uFF09**\uFF1A`,
3888
- `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`,
3889
- `2. **\u8BFB\u4EA7\u7269**\uFF1A\`oasis design status ${artifactId} --pull\` \u628A\u4EA4\u4ED8\u7269\u62C9\u5230\u672C\u5730\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`,
3890
- `3. **\u51B3\u5B9A**\uFF1A\u591F\u597D \u2192 \`oasis propose ${artifactId} --from-dir <\u62C9\u4E0B\u6765\u7684\u76EE\u5F55> --reason "<\u8FD9\u7248\u505A\u4E86\u4EC0\u4E48>"\` \u4EA4\u7A3F\uFF1B\u8FD8\u5DEE \u2192 \`--turn continue\` **\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`,
3891
- `- **\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`,
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`,
3892
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`
3893
3994
  ] : (() => {
3894
3995
  const codeRepo = args.codeRepo ?? null;
@@ -3944,7 +4045,7 @@ async function assembleContext(args) {
3944
4045
  ``,
3945
4046
  ...canvasMode ? [
3946
4047
  `## \u5173\u4E8E\u5DE5\u5177`,
3947
- `oasis CLI \u5DF2\u6CE8\u5165\uFF08\`OASIS_SERVER\` / \`OASIS_TOKEN\` \u5C31\u7EEA\uFF0C\u65E0\u9700\u914D\u7F6E\uFF09\u2014\u2014\u5148 \`oasis design status\` \u53D6\u8D27\uFF0C\u518D\u7528 \`oasis design chat --async\` \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`
3948
4049
  ] : commandReference({
3949
4050
  artifactId,
3950
4051
  action: args.action ?? "produce",
@@ -3972,14 +4073,14 @@ async function assembleContext(args) {
3972
4073
  (s2) => `- ${s2.upstream}: pin=${s2.pinned ?? "\u2205"} \u2192 \u6700\u65B0 ${s2.latest}${s2.upstreamCancelled ? "\uFF08\u26A0 \u4E0A\u6E38\u5DF2 cancelled\uFF09" : ""}`
3973
4074
  )
3974
4075
  ] : [],
3975
- // 画板模式:你在通用 runtime 上、CLI 齐全(design chat/propose/gap 都能用);引擎里的内层 agent 才没 CLI,那层不用你管
4076
+ // 画板模式:守则正文(甲方多轮/读产物再交/喂真数据/别本地手改板…)在人设 identity/AGENTS.md,此处不重复。
4077
+ // 只留内核锚点:你产哪一个、不归你定的别硬编。你在通用 runtime、CLI 齐全(design chat/propose/gap 都能用)。
3976
4078
  ...canvasMode ? [
3977
4079
  ``,
3978
4080
  `## \u751F\u4EA7\u8005\u5B88\u5219\uFF08\u753B\u677F\u7248\uFF09`,
3979
- `- \u4F60\u662F\u7532\u65B9\uFF1A\u5148 \`oasis design status\` \u53D6\u8D27\uFF08\u5F15\u64CE\u7684\u6D3B\u4E0D\u767D\u5E72\uFF09\uFF0C\u518D\u591A\u8F6E \`oasis design chat --async\` \u9A71\u52A8\u5F15\u64CE\uFF1B\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`,
3980
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`,
3981
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`,
3982
- `- \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`,
3983
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`
3984
4085
  ] : [],
3985
4086
  // review 会话不产出、不该给生产者守则(它只投票,守则的 propose/conclude/gap/add-revision 全不适用)
@@ -135290,6 +135391,100 @@ var init_workorder_drafts = __esm({
135290
135391
  });
135291
135392
 
135292
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
+ }
135293
135488
  function resolveRef(ref2, draft) {
135294
135489
  const res = resolveNodeRef(ref2, candidatesOf(draft));
135295
135490
  if (res.ok) return res.id;
@@ -135300,6 +135495,9 @@ function resolveRef(ref2, draft) {
135300
135495
  function editDraftPlan(draft, op) {
135301
135496
  switch (op.action) {
135302
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
+ }
135303
135501
  const title = op.title?.trim();
135304
135502
  if (!title) throw new Error("\u7ED9\u8349\u6848\u52A0\u8282\u70B9\u8981 --title\uFF08\u636E (type,title) \u6D3E\u751F id\uFF09");
135305
135503
  if (candidatesOf(draft).some((c) => c.type === op.type && (c.title ?? "").toLowerCase() === title.toLowerCase()))
@@ -135321,6 +135519,7 @@ function editDraftPlan(draft, op) {
135321
135519
  }
135322
135520
  case "edit": {
135323
135521
  const id = resolveRef(op.artifactId, draft);
135522
+ assertNotDeliverySummary(draft, [id], "\u7F16\u8F91\u8282\u70B9\u5B9A\u4E49");
135324
135523
  const target = spawnOps(draft).find((o) => o.id === id);
135325
135524
  if (!target) throw new Error(`\u8349\u6848\u91CC\u6CA1\u6709\u8282\u70B9 ${id} \u7684 spawn`);
135326
135525
  if (op.title !== void 0) target.title = op.title;
@@ -135331,12 +135530,14 @@ function editDraftPlan(draft, op) {
135331
135530
  case "link": {
135332
135531
  const consumer = resolveRef(op.artifactId, draft);
135333
135532
  const upstream = resolveRef(op.to, draft);
135533
+ assertNotDeliverySummary(draft, [consumer, upstream], "\u4FEE\u6539\u8FDE\u7EBF");
135334
135534
  draft.plan.ops.push({ action: "link", artifactId: consumer, to: upstream, ...op.required !== void 0 ? { required: op.required } : {} });
135335
135535
  break;
135336
135536
  }
135337
135537
  case "unlink": {
135338
135538
  const consumer = resolveRef(op.artifactId, draft);
135339
135539
  const upstream = resolveRef(op.to, draft);
135540
+ assertNotDeliverySummary(draft, [consumer, upstream], "\u4FEE\u6539\u8FDE\u7EBF");
135340
135541
  draft.plan.ops = draft.plan.ops.filter((o) => !(o.action === "link" && o.artifactId === consumer && o.to === upstream));
135341
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);
135342
135543
  break;
@@ -135344,6 +135545,7 @@ function editDraftPlan(draft, op) {
135344
135545
  case "seal": {
135345
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`);
135346
135547
  const id = resolveRef(op.artifactId, draft);
135548
+ assertNotDeliverySummary(draft, [id], "\u5220\u9664\u8282\u70B9");
135347
135549
  draft.plan.ops = draft.plan.ops.filter((o) => {
135348
135550
  if (o.action === "spawn" && o.id === id) return false;
135349
135551
  if (o.action === "annotate" && o.artifactId === id) return false;
@@ -135356,14 +135558,24 @@ function editDraftPlan(draft, op) {
135356
135558
  default:
135357
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`);
135358
135560
  }
135561
+ reconcileDeliverySummaryPlan(draft);
135359
135562
  }
135360
- var spawnOps, candidatesOf;
135563
+ var spawnOps, candidatesOf, spawnById, SUMMARY_TASK_BOOK;
135361
135564
  var init_draft_edit = __esm({
135362
135565
  "../server/src/governance/draft-edit.ts"() {
135363
135566
  "use strict";
135567
+ init_src();
135364
135568
  init_src2();
135365
135569
  spawnOps = (draft) => draft.plan.ops.filter((o) => o.action === "spawn" && !!o.id);
135366
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");
135367
135579
  }
135368
135580
  });
135369
135581
 
@@ -135630,7 +135842,7 @@ async function plannerAvailableActors(registry2) {
135630
135842
  }
135631
135843
  async function plannerArtifactTypes(schema, registry2) {
135632
135844
  if (!registry2) {
135633
- 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) => ({
135634
135846
  name: def.name,
135635
135847
  ...def.ownerRole ? { ownerRole: def.ownerRole } : {},
135636
135848
  contentType: def.contentType,
@@ -135639,7 +135851,7 @@ async function plannerArtifactTypes(schema, registry2) {
135639
135851
  }
135640
135852
  const actors = await plannerAvailableActors(registry2);
135641
135853
  const staffableRoles = new Set(actors.flatMap((actor) => actor.roles));
135642
- 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) => ({
135643
135855
  name: def.name,
135644
135856
  ...def.ownerRole ? { ownerRole: def.ownerRole } : {},
135645
135857
  contentType: def.contentType
@@ -135734,7 +135946,8 @@ async function buildDynamicWorkorderPlan(input) {
135734
135946
  ...def.ownerRole ? { role: def.ownerRole } : {},
135735
135947
  owner: ownerActor,
135736
135948
  dependsOn: node.dependsOn,
135737
- ...node.brief ? { brief: node.brief } : {}
135949
+ ...node.brief ? { brief: node.brief } : {},
135950
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135738
135951
  });
135739
135952
  }
135740
135953
  } else {
@@ -135749,11 +135962,12 @@ async function buildDynamicWorkorderPlan(input) {
135749
135962
  const ownerActor = owner?.actor ?? (!def.ownerRole ? PENDING_UNASSIGNED : type === primaryType ? input.actor : `pending:role:${def.ownerRole}`);
135750
135963
  if (enforceStaffing && def.ownerRole && !owner?.actor && type !== primaryType) continue;
135751
135964
  const id = type === primaryType ? primaryId : `artifact:${type}:${slug5}`;
135752
- 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 } : {} });
135753
135966
  }
135754
135967
  }
135755
135968
  for (const node of availableNodes) {
135756
135969
  const inputs = agentGraph ? agentEdgesFor(node.dependsOn ?? [], availableNodes, briefId, issues) : ruleEdgesFor(node.type, availableNodes, briefId);
135970
+ node.inputs = inputs;
135757
135971
  const shortTitle = node.title ?? (!agentGraph && node.type === "prd" && issues.length > 0 ? "\u964D\u7EA7\u65B9\u6848\u6587\u6863" : labelForType(node.type));
135758
135972
  const briefBody = node.brief ?? taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes, shortTitle);
135759
135973
  ops.push({
@@ -135767,10 +135981,59 @@ async function buildDynamicWorkorderPlan(input) {
135767
135981
  // 所有类型(含 document)统一:description = 完整任务书,直接进 owner 的 TASK.md,**不再造 open 人类 annotation**
135768
135982
  //(那条 annotation 默认 kind=change-request、触发 hasOpenHumanChangeRequest 焊死自动合并 + 假审核,是迁移遗留)。
135769
135983
  // document 的 docType/title 走独立字段;老文档缺独立字段的仍由 decodeDocumentDescription 兜底解析 description(保留)。
135770
- description: briefBody
135984
+ description: briefBody,
135985
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135771
135986
  });
135772
135987
  spawned.push({ id: node.id, type: node.type, role: node.role, owner: node.owner });
135773
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
+ }
135774
136037
  return {
135775
136038
  plan: {
135776
136039
  reason: `\u52A8\u6001\u89C4\u5212\u5DE5\u5355\uFF1A${displayTitle}`,
@@ -135815,15 +136078,19 @@ function buildPmAgentPlannerPrompt(input) {
135815
136078
  "",
135816
136079
  JSON.stringify({
135817
136080
  nodes: [
135818
- { 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" },
135819
- { type: "research", title: "\u7ADE\u54C1\u8C03\u7814", dependsOn: ["brief"], brief: "\u2026" },
135820
- { 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 }
135821
136085
  ],
135822
136086
  notes: ["\u53EF\u9009\uFF1A\u89C4\u5212\u7406\u7531\u6216\u98CE\u9669\u63D0\u793A"]
135823
136087
  }, null, 2),
135824
136088
  "",
135825
136089
  "\u89C4\u5219\uFF1A",
135826
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",
135827
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",
135828
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",
135829
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',
@@ -135850,7 +136117,8 @@ function parseAgentWorkorderPlan(text) {
135850
136117
  type: n.type.trim().toLowerCase(),
135851
136118
  ...Array.isArray(n.dependsOn) ? { dependsOn: n.dependsOn.filter((x2) => typeof x2 === "string").map((x2) => x2.trim().toLowerCase()).filter(Boolean) } : {},
135852
136119
  ...typeof n.title === "string" && n.title.trim() ? { title: n.title.trim() } : {},
135853
- ...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 } : {}
135854
136122
  });
135855
136123
  }
135856
136124
  if (nodes.length === 0) return null;
@@ -135926,13 +136194,14 @@ function desiredArtifacts(primaryType, description) {
135926
136194
  return ["prd", "research", "design", "adr", "dev", "qa"].filter((type) => wanted.has(type));
135927
136195
  }
135928
136196
  function normalizeAgentPlan(plan, validTypes) {
135929
- 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);
135930
136198
  const nodes = [];
135931
136199
  const seen = /* @__PURE__ */ new Set();
135932
136200
  const dupTitles = [];
135933
136201
  for (const node of plan.nodes) {
135934
136202
  const type = node.type.trim().toLowerCase();
135935
136203
  if (type === "brief") continue;
136204
+ if (type === DELIVERY_SUMMARY_ARTIFACT_TYPE) continue;
135936
136205
  if (!allowed.includes(type)) continue;
135937
136206
  const title = (node.title?.trim() || labelForType(type)).slice(0, 40);
135938
136207
  const key = `${type}:${title.toLowerCase()}`;
@@ -135945,7 +136214,8 @@ function normalizeAgentPlan(plan, validTypes) {
135945
136214
  type,
135946
136215
  title,
135947
136216
  dependsOn: node.dependsOn?.map((d) => d.trim()).filter(Boolean) ?? [],
135948
- ...node.brief ? { brief: node.brief } : {}
136217
+ ...node.brief ? { brief: node.brief } : {},
136218
+ ...node.isFinalOutput ? { isFinalOutput: true } : {}
135949
136219
  });
135950
136220
  }
135951
136221
  return nodes.length > 0 ? { nodes, dupTitles, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
@@ -136023,6 +136293,29 @@ ${plannerNotes.map((note) => `- ${note}`).join("\n")}` : "";
136023
136293
  notes
136024
136294
  ].join("\n");
136025
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
+ }
136026
136319
  function dedupeIssues(issues) {
136027
136320
  const seen = /* @__PURE__ */ new Set();
136028
136321
  const out = [];
@@ -136042,7 +136335,8 @@ function labelForType(type) {
136042
136335
  design: "\u8BBE\u8BA1",
136043
136336
  adr: "\u6280\u672F\u65B9\u6848",
136044
136337
  dev: "\u5B9E\u73B0",
136045
- qa: "\u6D4B\u8BD5\u9A8C\u6536"
136338
+ qa: "\u6D4B\u8BD5\u9A8C\u6536",
136339
+ [DELIVERY_SUMMARY_ARTIFACT_TYPE]: "\u4EA4\u4ED8\u6C47\u603B"
136046
136340
  };
136047
136341
  return labels[type] ?? type;
136048
136342
  }
@@ -136054,16 +136348,26 @@ var DEFAULT_TYPES, WORKORDER_SPEC_PREFIX;
136054
136348
  var init_planner = __esm({
136055
136349
  "../server/src/domains/collab/planner.ts"() {
136056
136350
  "use strict";
136351
+ init_src();
136057
136352
  init_src2();
136058
136353
  init_identity();
136059
136354
  DEFAULT_TYPES = [
136060
- // brief 的 goal/acceptanceCriteria 现在是【自定义字段】(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)
136355
+ // brief 的 goal/acceptanceCriteria 现在是【自定义字段】
136356
+ //(Phase 3,替代旧 OASIS_WORKORDER_SPEC 批注,铲 #1 死锁根)。
136061
136357
  // brief 不绑岗位:内置产物,owner 恒 = 发起人本人(决策 0026)——ownerRole 从不被解析。
136062
136358
  { name: "brief", contentType: "text", fieldDefs: [
136063
136359
  { name: "goal", type: "string", description: "\u4E00\u53E5\u8BDD\u76EE\u6807\uFF1A\u8FD9\u4E2A\u5DE5\u5355\u8981\u8FBE\u6210\u4EC0\u4E48" },
136064
136360
  { name: "acceptanceCriteria", type: "string[]", description: "\u9A8C\u6536\u6761\u76EE\uFF08\u6EE1\u8DB3\u5373\u7B97\u5B8C\u6210\uFF09" },
136065
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" }
136066
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
+ },
136067
136371
  { name: "prd", contentType: "text", ownerRole: "pm" },
136068
136372
  { name: "research", contentType: "text", ownerRole: "research" },
136069
136373
  { name: "design", contentType: "text", ownerRole: "designer" },
@@ -136214,6 +136518,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
136214
136518
  };
136215
136519
  };
136216
136520
  const runtimeByArtifact = buildRuntimeByArtifact(dispatchJournal, ref2);
136521
+ const hasDeliverySummary = arts.some((a) => a.type === DELIVERY_SUMMARY_ARTIFACT_TYPE);
136217
136522
  const nodes = arts.map((a) => {
136218
136523
  const info = nodeInfo(model, a);
136219
136524
  const observedRuntime = runtimeByArtifact.get(a.id);
@@ -136242,6 +136547,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
136242
136547
  queue: info.queueLen,
136243
136548
  blocked: blockedOf(model, a.id).blocked,
136244
136549
  owner: ref2(a.owner),
136550
+ ...(hasDeliverySummary ? a.type === DELIVERY_SUMMARY_ARTIFACT_TYPE : a.isFinalOutput === true) ? { isFinalOutput: true } : {},
136245
136551
  // 封存原因透出:stage 只到 "sealed" 粒度,原因(accepted/cancelled/frozen)另给,供前端区分展示。
136246
136552
  ...lifecycle !== "active" ? { sealedReason: lifecycle.slice("sealed:".length) } : {},
136247
136553
  ...runtime ? { runtime } : {},
@@ -136315,6 +136621,7 @@ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
136315
136621
  return { id, ...extra?.name ? { name: extra.name } : {}, ...extra?.role ? { role: extra.role } : {}, ...extra?.avatar ? { avatar: extra.avatar } : {} };
136316
136622
  };
136317
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);
136318
136625
  const nodes = spawns.map((op) => {
136319
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) ?? [];
136320
136627
  const typePv = typeReviewerPreview?.(op.type) ?? null;
@@ -136328,6 +136635,7 @@ function buildWorkorderDraftDetail(draft, resolveActor, typeReviewerPreview) {
136328
136635
  queue: 0,
136329
136636
  blocked: false,
136330
136637
  owner: ref2(op.owner ?? draft.draftedBy),
136638
+ ...(hasDeliverySummary ? op.type === DELIVERY_SUMMARY_ARTIFACT_TYPE : op.isFinalOutput === true) ? { isFinalOutput: true } : {},
136331
136639
  ...pv ? { reviewers: pv.reviewers.map((r) => ({ actor: ref2(r.actor), source: r.source })), quorum: pv.quorum, typeName: pv.typeName } : {}
136332
136640
  };
136333
136641
  });
@@ -136606,6 +136914,7 @@ var init_workorder_detail = __esm({
136606
136914
  "../server/src/domains/collab/workorder-detail.ts"() {
136607
136915
  "use strict";
136608
136916
  init_src2();
136917
+ init_src();
136609
136918
  init_node_state();
136610
136919
  init_stall();
136611
136920
  init_workorders();
@@ -137503,10 +137812,11 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
137503
137812
  const goal = optStr(args, "goal");
137504
137813
  const rawCriteria = args["acceptance_criteria"] ?? args["acceptanceCriteria"];
137505
137814
  const acceptanceCriteria = Array.isArray(rawCriteria) ? rawCriteria.filter((x2) => typeof x2 === "string" && x2.trim().length > 0).map((x2) => x2.trim()) : void 0;
137506
- const briefForPlanner = acceptanceCriteria && acceptanceCriteria.length > 0 ? `${brief}
137507
-
137508
- \u9A8C\u6536\u6807\u51C6\uFF1A
137509
- ${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");
137510
137820
  const agentIssues = [];
137511
137821
  let agentPlan = null;
137512
137822
  const inlinePlan = args["plan"];
@@ -137654,7 +137964,6 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137654
137964
  return annotation !== void 0 && isHumanChangeRequest(annotation);
137655
137965
  }) : hasOpenHumanChangeRequest2(kernel, artifactId));
137656
137966
  const changeRequestParent = resolves !== void 0 ? latestDeclaredRevisionAnchor(kernel, resolves) : isAgent(actor) ? latestOpenHumanChangeRequestParent(kernel, artifactId) : void 0;
137657
- const holdForReview = args["holdForReview"] === true || args["review"] === true;
137658
137967
  const continuation = args["continuation"];
137659
137968
  if (continuation !== void 0 && (continuation === null || typeof continuation !== "object" || continuation.checkpoint !== true || continuation.continue !== true)) {
137660
137969
  throw new Error("continuation \u5FC5\u987B\u662F { checkpoint: true, continue: true }");
@@ -137678,7 +137987,7 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137678
137987
  const typeDefForLand = artForLand ? ctx.schema?.find((t) => t.name === artForLand.type) : void 0;
137679
137988
  const isExternalFF = handlerForType(typeDefForLand?.contentType)?.mergeMode === "external-ff";
137680
137989
  let landed = false;
137681
- 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)) {
137682
137991
  try {
137683
137992
  const r = await kernel.advanceQueue(artifactId, actor);
137684
137993
  landed = r.landed.some((x2) => x2.id === revision.id);
@@ -137686,7 +137995,8 @@ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
137686
137995
  } catch {
137687
137996
  }
137688
137997
  }
137689
- 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 };
137690
138000
  }
137691
138001
  case "merge": {
137692
138002
  const revisionId = optStr(args, "revisionId");
@@ -138530,6 +138840,8 @@ async function startOasisServer(opts) {
138530
138840
  );
138531
138841
  return;
138532
138842
  }
138843
+ const trustedDraft = targetWs && opts.workorderDrafts ? await opts.workorderDrafts.forWorkspace(targetWs) : void 0;
138844
+ assertDeliverySummaryPlanIntegrity(body.plan, trustedDraft?.plan);
138533
138845
  const principalRegistry = engine.registry ?? opts.registry;
138534
138846
  if (principalRegistry) {
138535
138847
  const principalErrors = await validateWorkorderPrincipals(body.plan, body.briefArtifactId, (id) => principalRegistry.getActor(id));
@@ -138685,71 +138997,26 @@ async function startOasisServer(opts) {
138685
138997
  try {
138686
138998
  const body = JSON.parse(rawBody || "{}");
138687
138999
  const ctx = resolveDiscussContext(engine.kernel.model, body, actor ?? void 0);
138688
- let retainedRt = opts.retainedSession?.(
138689
- body.annotationId ? { annotationId: body.annotationId, ...ctx.anchorArtifactId ? { artifactId: ctx.anchorArtifactId } : {} } : body
138690
- );
138691
- if (!retainedRt && opts.escalateDiscussRetain && body.artifactId) {
138692
- const raw = opts.escalateDiscussRetain.get(`esc:${body.artifactId}`);
138693
- if (raw) {
138694
- try {
138695
- const rec = JSON.parse(raw);
138696
- if (rec.runtimeSessionId && rec.human === ctx.ownerId) retainedRt = rec.runtimeSessionId;
138697
- } catch {
138698
- }
138699
- }
138700
- }
138701
- if (retainedRt && body.draftId && opts.escalateDiscussRetain) {
138702
- const claimKey = `draftclaim:${body.draftId}`;
138703
- const claimer = opts.escalateDiscussRetain.get(claimKey);
138704
- if (claimer && claimer !== ctx.ownerId) retainedRt = void 0;
138705
- else if (!claimer) opts.escalateDiscussRetain.set(claimKey, ctx.ownerId);
138706
- }
138707
139000
  const chatStore = opts.chatSession;
138708
139001
  const baseItemKey = body.draftId ? `draft:${body.draftId}` : body.annotationId ? `ann:${body.annotationId}` : `esc:${body.artifactId}`;
138709
139002
  const itemKey = `${baseItemKey}:${ctx.ownerId}`;
138710
139003
  let chatSessionId;
138711
- let createdThisCall = false;
138712
139004
  if (chatStore) {
138713
139005
  const { randomUUID: randomUUID22 } = await import("node:crypto");
138714
139006
  const known = discussSessions.get(itemKey);
138715
139007
  if (known && await chatStore.getSession(known).catch(() => null)) chatSessionId = known;
138716
139008
  if (!chatSessionId) {
138717
139009
  chatSessionId = randomUUID22();
138718
- createdThisCall = true;
138719
139010
  const now = (/* @__PURE__ */ new Date()).toISOString();
138720
139011
  const title = `\u89E3\u51B3\uFF1A${ctx.seed.slice(ctx.seed.indexOf("\uFF1A") + 1, ctx.seed.indexOf("\uFF1A") + 25) || "\u5DE5\u5355\u5361\u70B9"}`;
138721
139012
  const reg0 = (await resolveEngine(currentCompanyId).catch(() => null))?.registry ?? opts.registry;
138722
139013
  const mgrBinding = reg0 ? await reg0.getBinding(ctx.managerId).catch(() => null) : null;
138723
139014
  const managerNodeId = mgrBinding?.status === "active" ? mgrBinding.nodeId : "";
138724
139015
  const managerRuntimeKind = mgrBinding?.status === "active" ? mgrBinding.runtimeKind : void 0;
138725
- 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);
138726
139017
  await chatStore.linkWorkOrder(chatSessionId, ctx.workspace).catch(() => void 0);
138727
139018
  }
138728
139019
  discussSessions.set(itemKey, chatSessionId);
138729
- await chatStore.appendMessage({ id: randomUUID22(), sessionId: chatSessionId, role: "user", content: ctx.seed, createdAt: (/* @__PURE__ */ new Date()).toISOString() }).catch(() => void 0);
138730
- }
138731
- let session;
138732
- try {
138733
- session = await opts.dispatchChat({ actorId: ctx.managerId, message: ctx.seed, workspace: ctx.workspace, ...retainedRt ? { sessionId: retainedRt } : {}, ...chatSessionId ? { chatSessionId } : {} });
138734
- } catch (err) {
138735
- if (createdThisCall && chatStore && chatSessionId) {
138736
- await chatStore.deleteSession(chatSessionId).catch(() => void 0);
138737
- discussSessions.delete(itemKey);
138738
- }
138739
- throw err;
138740
- }
138741
- if (chatStore && chatSessionId) {
138742
- const sid = chatSessionId;
138743
- let reply = "";
138744
- session.onOutput((chunk) => {
138745
- reply += chunk;
138746
- });
138747
- void session.done.then(async () => {
138748
- const { randomUUID: randomUUID22 } = await import("node:crypto");
138749
- if (reply || session.runId) {
138750
- await chatStore.appendMessage({ id: randomUUID22(), sessionId: sid, role: "assistant", content: reply, createdAt: (/* @__PURE__ */ new Date()).toISOString(), ...session.runId ? { runId: session.runId } : {} }).catch(() => void 0);
138751
- }
138752
- }).catch(() => void 0);
138753
139020
  }
138754
139021
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ sessionId: chatSessionId ?? null, managerId: ctx.managerId, workspace: ctx.workspace }));
138755
139022
  } catch (err) {
@@ -147373,12 +147640,12 @@ function scrubLeakyEnv(env) {
147373
147640
  }
147374
147641
  function classifyExit(result) {
147375
147642
  if (!result) return void 0;
147376
- const text = `${String(result.subtype ?? "")} ${String(result.result ?? "")}`.toLowerCase();
147377
- if (text.includes("output token") || text.includes("max_tokens") || text.includes("output_token")) return "output-limit";
147378
- if (text.includes("rate") || text.includes("overloaded") || text.includes("429") || text.includes("529")) return "rate-limit";
147379
- if (result.subtype === "success" && result.is_error !== true) return "clean";
147380
- if (result.is_error === true || String(result.subtype ?? "").startsWith("error")) return "error";
147381
- 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";
147382
147649
  }
147383
147650
  var import_node_child_process7, import_node_crypto13, fs10, path9, readline, ONE_SHOT_UNSAFE_TOOLS, ClaudeCodeAdapter;
147384
147651
  var init_claude_code = __esm({
@@ -157408,7 +157675,7 @@ ${body || "\uFF08\u672A\u586B\u5199\uFF09"}`;
157408
157675
  "\u7531\u300C\u5267\u672C \xB7 Bug \u4FEE\u590D\u300D\u53D1\u8D77\u3002"
157409
157676
  ].join("\n");
157410
157677
  const plan = {
157411
- 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 } : {} }))
157412
157679
  };
157413
157680
  return {
157414
157681
  produces: "workorder",
@@ -157470,7 +157737,8 @@ var init_registry2 = __esm({
157470
157737
  title: "\u590D\u73B0\u9A8C\u8BC1\u4E0E\u56DE\u5F52",
157471
157738
  dependsOn: ["dev:\u4FEE\u590D\u5B9E\u73B0"],
157472
157739
  role: "\u6D4B\u8BD5",
157473
- 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
157474
157742
  }
157475
157743
  ];
157476
157744
  BUILTIN2 = {
@@ -159072,6 +159340,94 @@ var init_builtin_skills = __esm({
159072
159340
  }
159073
159341
  });
159074
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
+
159075
159431
  // ../server/src/index.ts
159076
159432
  var init_src7 = __esm({
159077
159433
  "../server/src/index.ts"() {
@@ -159128,6 +159484,7 @@ var init_src7 = __esm({
159128
159484
  init_workorder_drafts();
159129
159485
  init_builtin_skills();
159130
159486
  init_connector_skills();
159487
+ init_board_watch();
159131
159488
  }
159132
159489
  });
159133
159490
 
@@ -169845,6 +170202,14 @@ async function startServe(opts) {
169845
170202
  console.error(`[cascade] ${action.artifactId} \u5931\u8D25: ${String(err)}`);
169846
170203
  }
169847
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
+ }
169848
170213
  for (const a of kernel.model.artifacts.values()) {
169849
170214
  if (!a.owner.startsWith("actor:human:")) continue;
169850
170215
  if (lifecycleOf(kernel.model, a.id) !== "active") continue;
@@ -170083,6 +170448,18 @@ async function startServe(opts) {
170083
170448
  const automationTimer = setInterval(() => {
170084
170449
  void automationScheduler.tick().catch((err) => console.error(`[automation-scheduler] tick \u5931\u8D25: ${String(err)}`));
170085
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
+ }
170086
170463
  return {
170087
170464
  baseUrl: server.baseUrl,
170088
170465
  operatorToken,
@@ -170095,6 +170472,7 @@ async function startServe(opts) {
170095
170472
  if (livenessTimer) clearInterval(livenessTimer);
170096
170473
  if (orphanSweepTimer) clearInterval(orphanSweepTimer);
170097
170474
  clearInterval(automationTimer);
170475
+ if (boardWatchTimer) clearInterval(boardWatchTimer);
170098
170476
  clearInterval(serverHeartbeatTimer);
170099
170477
  hub?.close();
170100
170478
  await server.close();
@@ -172403,7 +172781,7 @@ ${res.warning}`);
172403
172781
  init_src6();
172404
172782
 
172405
172783
  // src/index.ts
172406
- var PKG_VERSION = true ? "0.1.62" : "dev";
172784
+ var PKG_VERSION = true ? "0.1.63" : "dev";
172407
172785
  var OASIS_DIR = path21.join(os9.homedir(), ".oasis");
172408
172786
  var CONFIG_FILE = path21.join(OASIS_DIR, "node-config.json");
172409
172787
  var PID_FILE = path21.join(OASIS_DIR, "node.pid");