@stofone/dsh-agent-roster 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +78 -4
  3. package/README.zh.md +44 -1
  4. package/bin/cli.mjs +228 -2
  5. package/docs/roster-v2.md +220 -0
  6. package/examples/dist/core-review-1.0.0.json +160 -0
  7. package/examples/dist/research-0.1.0.json +95 -0
  8. package/examples/roles/core-review-correctness/AGENT.md +20 -0
  9. package/examples/roles/core-review-lead/AGENT.md +23 -0
  10. package/examples/roles/core-review-lead-captain/AGENT.md +13 -0
  11. package/examples/roles/core-review-maintainer/AGENT.md +20 -0
  12. package/examples/roles/research-scout/AGENT.md +16 -0
  13. package/examples/roles/research-verifier/AGENT.md +16 -0
  14. package/examples/rosters/core-review/roster.yaml +39 -0
  15. package/examples/rosters/research/roster.yaml +20 -0
  16. package/examples-orchestration/dist/core-review-1.0.0.json +185 -0
  17. package/examples-orchestration/dist/core-review-1.0.0.tasks.json +76 -0
  18. package/examples-orchestration/roles/fixer/AGENT.md +18 -0
  19. package/examples-orchestration/roles/lead/AGENT.md +18 -0
  20. package/examples-orchestration/roles/reviewer/AGENT.md +23 -0
  21. package/examples-orchestration/roles/reviewer/parts/checklist.md +6 -0
  22. package/examples-orchestration/rosters/core-review/overrides/fixer.md +1 -0
  23. package/examples-orchestration/rosters/core-review/roster.yaml +43 -0
  24. package/index.js +8 -1
  25. package/lib/compile2.js +522 -0
  26. package/lib/format-v2.js +196 -0
  27. package/lib/frontmatter.js +113 -0
  28. package/lib/migrate.js +326 -0
  29. package/lib/plan.js +167 -0
  30. package/lib/registry.js +65 -11
  31. package/lib/role.js +498 -0
  32. package/lib/roster2.js +591 -0
  33. package/lib/run.js +188 -0
  34. package/lib/write.js +14 -8
  35. package/orgs/agent-roster-collective/dist/api-design-1.0.0.json +130 -0
  36. package/orgs/agent-roster-collective/dist/content-ops-1.0.0.json +130 -0
  37. package/orgs/agent-roster-collective/dist/contract-review-1.0.0.json +130 -0
  38. package/orgs/agent-roster-collective/dist/data-quality-1.0.0.json +130 -0
  39. package/orgs/agent-roster-collective/dist/db-migration-1.0.0.json +130 -0
  40. package/orgs/agent-roster-collective/dist/docs-writer-1.0.0.json +130 -0
  41. package/orgs/agent-roster-collective/dist/finance-recon-1.0.0.json +130 -0
  42. package/orgs/agent-roster-collective/dist/frontend-ui-1.0.0.json +130 -0
  43. package/orgs/agent-roster-collective/dist/i18n-l10n-1.0.0.json +130 -0
  44. package/orgs/agent-roster-collective/dist/incident-response-1.0.0.json +130 -0
  45. package/orgs/agent-roster-collective/dist/literature-review-1.0.0.json +130 -0
  46. package/orgs/agent-roster-collective/dist/market-research-1.0.0.json +130 -0
  47. package/orgs/agent-roster-collective/dist/ml-eval-1.0.0.json +130 -0
  48. package/orgs/agent-roster-collective/dist/performance-1.0.0.json +130 -0
  49. package/orgs/agent-roster-collective/dist/product-discovery-1.0.0.json +130 -0
  50. package/orgs/agent-roster-collective/dist/qa-regression-1.0.0.json +130 -0
  51. package/orgs/agent-roster-collective/dist/recruiting-1.0.0.json +130 -0
  52. package/orgs/agent-roster-collective/dist/release-manager-1.0.0.json +130 -0
  53. package/orgs/agent-roster-collective/dist/security-audit-1.0.0.json +132 -0
  54. package/orgs/agent-roster-collective/dist/sre-oncall-1.0.0.json +130 -0
  55. package/orgs/agent-roster-collective/dist/support-triage-1.0.0.json +130 -0
  56. package/orgs/agent-roster-collective/roles/api-design-failure-mode/AGENT.md +19 -0
  57. package/orgs/agent-roster-collective/roles/api-design-lead/AGENT.md +19 -0
  58. package/orgs/agent-roster-collective/roles/api-design-schema/AGENT.md +20 -0
  59. package/orgs/agent-roster-collective/roles/content-ops-distributor/AGENT.md +19 -0
  60. package/orgs/agent-roster-collective/roles/content-ops-editor/AGENT.md +19 -0
  61. package/orgs/agent-roster-collective/roles/content-ops-lead/AGENT.md +19 -0
  62. package/orgs/agent-roster-collective/roles/contract-review-clause-reader/AGENT.md +19 -0
  63. package/orgs/agent-roster-collective/roles/contract-review-lead/AGENT.md +19 -0
  64. package/orgs/agent-roster-collective/roles/contract-review-risk-note/AGENT.md +19 -0
  65. package/orgs/agent-roster-collective/roles/data-quality-lead/AGENT.md +19 -0
  66. package/orgs/agent-roster-collective/roles/data-quality-profiler/AGENT.md +19 -0
  67. package/orgs/agent-roster-collective/roles/data-quality-rule-author/AGENT.md +19 -0
  68. package/orgs/agent-roster-collective/roles/db-migration-lead/AGENT.md +19 -0
  69. package/orgs/agent-roster-collective/roles/db-migration-migrator/AGENT.md +19 -0
  70. package/orgs/agent-roster-collective/roles/db-migration-verifier/AGENT.md +19 -0
  71. package/orgs/agent-roster-collective/roles/docs-writer-example-runner/AGENT.md +19 -0
  72. package/orgs/agent-roster-collective/roles/docs-writer-lead/AGENT.md +19 -0
  73. package/orgs/agent-roster-collective/roles/docs-writer-writer/AGENT.md +19 -0
  74. package/orgs/agent-roster-collective/roles/finance-recon-exception/AGENT.md +19 -0
  75. package/orgs/agent-roster-collective/roles/finance-recon-lead/AGENT.md +19 -0
  76. package/orgs/agent-roster-collective/roles/finance-recon-matcher/AGENT.md +19 -0
  77. package/orgs/agent-roster-collective/roles/frontend-ui-a11y-reviewer/AGENT.md +19 -0
  78. package/orgs/agent-roster-collective/roles/frontend-ui-implementer/AGENT.md +19 -0
  79. package/orgs/agent-roster-collective/roles/frontend-ui-lead/AGENT.md +19 -0
  80. package/orgs/agent-roster-collective/roles/i18n-l10n-lead/AGENT.md +19 -0
  81. package/orgs/agent-roster-collective/roles/i18n-l10n-reviewer/AGENT.md +19 -0
  82. package/orgs/agent-roster-collective/roles/i18n-l10n-translator/AGENT.md +19 -0
  83. package/orgs/agent-roster-collective/roles/incident-response-commander/AGENT.md +19 -0
  84. package/orgs/agent-roster-collective/roles/incident-response-scribe/AGENT.md +19 -0
  85. package/orgs/agent-roster-collective/roles/incident-response-triage/AGENT.md +19 -0
  86. package/orgs/agent-roster-collective/roles/literature-review-critic/AGENT.md +19 -0
  87. package/orgs/agent-roster-collective/roles/literature-review-extractor/AGENT.md +19 -0
  88. package/orgs/agent-roster-collective/roles/literature-review-lead/AGENT.md +19 -0
  89. package/orgs/agent-roster-collective/roles/market-research-lead/AGENT.md +19 -0
  90. package/orgs/agent-roster-collective/roles/market-research-researcher/AGENT.md +19 -0
  91. package/orgs/agent-roster-collective/roles/market-research-synthesizer/AGENT.md +19 -0
  92. package/orgs/agent-roster-collective/roles/ml-eval-dataset/AGENT.md +19 -0
  93. package/orgs/agent-roster-collective/roles/ml-eval-lead/AGENT.md +19 -0
  94. package/orgs/agent-roster-collective/roles/ml-eval-metric/AGENT.md +19 -0
  95. package/orgs/agent-roster-collective/roles/performance-lead/AGENT.md +19 -0
  96. package/orgs/agent-roster-collective/roles/performance-load-tester/AGENT.md +19 -0
  97. package/orgs/agent-roster-collective/roles/performance-profiler/AGENT.md +19 -0
  98. package/orgs/agent-roster-collective/roles/product-discovery-interviewer/AGENT.md +19 -0
  99. package/orgs/agent-roster-collective/roles/product-discovery-lead/AGENT.md +19 -0
  100. package/orgs/agent-roster-collective/roles/product-discovery-skeptic/AGENT.md +19 -0
  101. package/orgs/agent-roster-collective/roles/qa-regression-case-writer/AGENT.md +19 -0
  102. package/orgs/agent-roster-collective/roles/qa-regression-flake-hunter/AGENT.md +19 -0
  103. package/orgs/agent-roster-collective/roles/qa-regression-lead/AGENT.md +19 -0
  104. package/orgs/agent-roster-collective/roles/recruiting-lead/AGENT.md +19 -0
  105. package/orgs/agent-roster-collective/roles/recruiting-scheduler/AGENT.md +19 -0
  106. package/orgs/agent-roster-collective/roles/recruiting-screener/AGENT.md +19 -0
  107. package/orgs/agent-roster-collective/roles/release-manager-changelog/AGENT.md +19 -0
  108. package/orgs/agent-roster-collective/roles/release-manager-lead/AGENT.md +19 -0
  109. package/orgs/agent-roster-collective/roles/release-manager-rollback/AGENT.md +19 -0
  110. package/orgs/agent-roster-collective/roles/security-audit-code-auditor/AGENT.md +19 -0
  111. package/orgs/agent-roster-collective/roles/security-audit-lead/AGENT.md +19 -0
  112. package/orgs/agent-roster-collective/roles/security-audit-threat-modeler/AGENT.md +19 -0
  113. package/orgs/agent-roster-collective/roles/sre-oncall-capacity/AGENT.md +19 -0
  114. package/orgs/agent-roster-collective/roles/sre-oncall-lead/AGENT.md +19 -0
  115. package/orgs/agent-roster-collective/roles/sre-oncall-runbook/AGENT.md +19 -0
  116. package/orgs/agent-roster-collective/roles/support-triage-classifier/AGENT.md +19 -0
  117. package/orgs/agent-roster-collective/roles/support-triage-lead/AGENT.md +19 -0
  118. package/orgs/agent-roster-collective/roles/support-triage-responder/AGENT.md +19 -0
  119. package/orgs/agent-roster-collective/rosters/api-design/roster.yaml +26 -0
  120. package/orgs/agent-roster-collective/rosters/content-ops/roster.yaml +25 -0
  121. package/orgs/agent-roster-collective/rosters/contract-review/roster.yaml +25 -0
  122. package/orgs/agent-roster-collective/rosters/data-quality/roster.yaml +26 -0
  123. package/orgs/agent-roster-collective/rosters/db-migration/roster.yaml +26 -0
  124. package/orgs/agent-roster-collective/rosters/docs-writer/roster.yaml +25 -0
  125. package/orgs/agent-roster-collective/rosters/finance-recon/roster.yaml +25 -0
  126. package/orgs/agent-roster-collective/rosters/frontend-ui/roster.yaml +26 -0
  127. package/orgs/agent-roster-collective/rosters/i18n-l10n/roster.yaml +26 -0
  128. package/orgs/agent-roster-collective/rosters/incident-response/roster.yaml +26 -0
  129. package/orgs/agent-roster-collective/rosters/literature-review/roster.yaml +25 -0
  130. package/orgs/agent-roster-collective/rosters/market-research/roster.yaml +25 -0
  131. package/orgs/agent-roster-collective/rosters/ml-eval/roster.yaml +25 -0
  132. package/orgs/agent-roster-collective/rosters/performance/roster.yaml +25 -0
  133. package/orgs/agent-roster-collective/rosters/product-discovery/roster.yaml +25 -0
  134. package/orgs/agent-roster-collective/rosters/qa-regression/roster.yaml +26 -0
  135. package/orgs/agent-roster-collective/rosters/recruiting/roster.yaml +25 -0
  136. package/orgs/agent-roster-collective/rosters/release-manager/roster.yaml +25 -0
  137. package/orgs/agent-roster-collective/rosters/security-audit/roster.yaml +30 -0
  138. package/orgs/agent-roster-collective/rosters/sre-oncall/roster.yaml +26 -0
  139. package/orgs/agent-roster-collective/rosters/support-triage/roster.yaml +25 -0
  140. package/package.json +3 -1
  141. package/schema/compiled.v1.schema.json +391 -0
  142. package/schema/role.v1.schema.json +240 -0
  143. package/schema/roster.v2.schema.json +437 -0
  144. package/examples/core-review/roster.yaml +0 -82
  145. package/examples/research/roster.yaml +0 -42
  146. package/orgs/agent-roster-collective/api-design/roster.yaml +0 -67
  147. package/orgs/agent-roster-collective/content-ops/roster.yaml +0 -55
  148. package/orgs/agent-roster-collective/contract-review/roster.yaml +0 -58
  149. package/orgs/agent-roster-collective/data-quality/roster.yaml +0 -64
  150. package/orgs/agent-roster-collective/db-migration/roster.yaml +0 -64
  151. package/orgs/agent-roster-collective/docs-writer/roster.yaml +0 -59
  152. package/orgs/agent-roster-collective/finance-recon/roster.yaml +0 -63
  153. package/orgs/agent-roster-collective/frontend-ui/roster.yaml +0 -60
  154. package/orgs/agent-roster-collective/i18n-l10n/roster.yaml +0 -60
  155. package/orgs/agent-roster-collective/incident-response/roster.yaml +0 -65
  156. package/orgs/agent-roster-collective/literature-review/roster.yaml +0 -61
  157. package/orgs/agent-roster-collective/market-research/roster.yaml +0 -60
  158. package/orgs/agent-roster-collective/ml-eval/roster.yaml +0 -63
  159. package/orgs/agent-roster-collective/performance/roster.yaml +0 -63
  160. package/orgs/agent-roster-collective/product-discovery/roster.yaml +0 -55
  161. package/orgs/agent-roster-collective/qa-regression/roster.yaml +0 -64
  162. package/orgs/agent-roster-collective/recruiting/roster.yaml +0 -58
  163. package/orgs/agent-roster-collective/release-manager/roster.yaml +0 -59
  164. package/orgs/agent-roster-collective/security-audit/roster.yaml +0 -65
  165. package/orgs/agent-roster-collective/sre-oncall/roster.yaml +0 -60
  166. package/orgs/agent-roster-collective/support-triage/roster.yaml +0 -58
  167. /package/orgs/agent-roster-collective/{security-audit → rosters/security-audit}/capabilities.yaml +0 -0
package/lib/run.js ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * 施工图的执行回执(`dist/<id>-<version>.run.json`)。
3
+ *
4
+ * 施工图(`plan@1`)规定了「谁在什么时候做什么」,但执行发生在队长会话里。没有回执时,
5
+ * 一次失败只会停在某张红卡上;有了回执,下一次编译能把「上一轮哪张卡没过、为什么」带进
6
+ * 重试卡的 description——失败因此能回流到编译,而不是靠人记。
7
+ *
8
+ * 回执只记录可复核的东西:卡片 id、状态、门跑过的命令与退出码、失败意见的原文。
9
+ * 它不记录会话 id 或模型路由——那些是运行时事实,写进来就过期。
10
+ *
11
+ * @module dsh-agent-roster/run
12
+ */
13
+ import { diagnostic } from "./parse.js";
14
+
15
+ export const RUN_KIND = "dsh-agent-roster/run@1";
16
+ export const RUN_STATUSES = ["passed", "failed", "skipped"];
17
+ const RUN_FIELDS = ["kind", "roster", "rounds", "notes"];
18
+ const ROUND_FIELDS = ["round", "at", "tasks"];
19
+ const TASK_FIELDS = ["id", "status", "findings", "commands", "note"];
20
+ const FINDING_FIELDS = ["severity", "problem", "requiredFix", "file", "line"];
21
+ const COMMAND_FIELDS = ["command", "exitCode", "outputTail"];
22
+ const SEVERITIES = ["low", "medium", "high", "blocker"];
23
+ const LIMITS = { rounds: 20, tasks: 64, findings: 32, commands: 16, text: 2000 };
24
+
25
+ function unknownKeys(value, allowed, path, file, report) {
26
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
27
+ report.push(diagnostic("error", "run-shape", path, `${path} 必须是对象`, file));
28
+ return false;
29
+ }
30
+ for (const key of Object.keys(value)) {
31
+ if (!allowed.includes(key)) {
32
+ report.push(diagnostic("error", "unknown-field", `${path}.${key}`, `未知字段 "${key}";可用字段:${allowed.join(" / ")}`, file));
33
+ }
34
+ }
35
+ return true;
36
+ }
37
+
38
+ function textField(value, path, file, report, { max = LIMITS.text, required = false } = {}) {
39
+ if (value === undefined || value === null) {
40
+ if (required) report.push(diagnostic("error", "run-field", path, `${path} 不能为空`, file));
41
+ return null;
42
+ }
43
+ if (typeof value !== "string") {
44
+ report.push(diagnostic("error", "run-field", path, `${path} 必须是字符串`, file));
45
+ return null;
46
+ }
47
+ if (value.length > max) {
48
+ report.push(diagnostic("error", "run-field", path, `${path} 超过 ${max} 字符`, file));
49
+ return null;
50
+ }
51
+ return value;
52
+ }
53
+
54
+ /** 校验一份执行回执。不合法时返回 `run: null`,并把每条原因写成诊断。 */
55
+ export function validateRun(value, { file = "run.json" } = {}) {
56
+ const report = [];
57
+ if (!unknownKeys(value, RUN_FIELDS, "run", file, report)) return { ok: false, run: null, diagnostics: report };
58
+ if (value.kind !== RUN_KIND) {
59
+ report.push(diagnostic("error", "run-kind", "run.kind", `kind 必须是 "${RUN_KIND}"`, file));
60
+ }
61
+ const roster = value.roster;
62
+ if (roster === null || typeof roster !== "object" || Array.isArray(roster)) {
63
+ report.push(diagnostic("error", "run-field", "run.roster", "roster 必须是 {id, version}", file));
64
+ } else {
65
+ unknownKeys(roster, ["id", "version"], "run.roster", file, report);
66
+ textField(roster.id, "run.roster.id", file, report, { required: true, max: 64 });
67
+ textField(roster.version, "run.roster.version", file, report, { required: true, max: 64 });
68
+ }
69
+ const rounds = value.rounds;
70
+ if (!Array.isArray(rounds) || rounds.length === 0) {
71
+ report.push(diagnostic("error", "run-rounds", "run.rounds", "rounds 必须是非空数组", file));
72
+ } else if (rounds.length > LIMITS.rounds) {
73
+ report.push(diagnostic("error", "run-rounds", "run.rounds", `rounds 最多 ${LIMITS.rounds} 轮`, file));
74
+ } else {
75
+ rounds.forEach((round, index) => verifyRound(round, index, file, report));
76
+ }
77
+ if (report.some((item) => item.severity === "error")) return { ok: false, run: null, diagnostics: report };
78
+ return { ok: true, run: value, diagnostics: report };
79
+ }
80
+
81
+ function verifyRound(round, index, file, report) {
82
+ const path = `run.rounds[${index}]`;
83
+ if (!unknownKeys(round, ROUND_FIELDS, path, file, report)) return;
84
+ if (!Number.isInteger(round.round) || round.round < 1) {
85
+ report.push(diagnostic("error", "run-round", `${path}.round`, "round 必须是从 1 开始的整数", file));
86
+ }
87
+ textField(round.at, `${path}.at`, file, report, { max: 40 });
88
+ if (!Array.isArray(round.tasks) || round.tasks.length === 0) {
89
+ report.push(diagnostic("error", "run-tasks", `${path}.tasks`, "tasks 必须是非空数组", file));
90
+ return;
91
+ }
92
+ if (round.tasks.length > LIMITS.tasks) {
93
+ report.push(diagnostic("error", "run-tasks", `${path}.tasks`, `tasks 最多 ${LIMITS.tasks} 张卡`, file));
94
+ }
95
+ round.tasks.forEach((task, taskIndex) => verifyTask(task, `${path}.tasks[${taskIndex}]`, file, report));
96
+ }
97
+
98
+ function verifyTask(task, path, file, report) {
99
+ if (!unknownKeys(task, TASK_FIELDS, path, file, report)) return;
100
+ textField(task.id, `${path}.id`, file, report, { required: true, max: 120 });
101
+ if (!RUN_STATUSES.includes(task.status)) {
102
+ report.push(diagnostic("error", "run-status", `${path}.status`, `status 只能是 ${RUN_STATUSES.join(" / ")}`, file));
103
+ }
104
+ if (task.findings !== undefined) {
105
+ if (!Array.isArray(task.findings)) {
106
+ report.push(diagnostic("error", "run-findings", `${path}.findings`, "findings 必须是数组", file));
107
+ } else if (task.findings.length > LIMITS.findings) {
108
+ report.push(diagnostic("error", "run-findings", `${path}.findings`, `findings 最多 ${LIMITS.findings} 条`, file));
109
+ } else {
110
+ task.findings.forEach((finding, index) => verifyFinding(finding, `${path}.findings[${index}]`, file, report));
111
+ }
112
+ }
113
+ if (task.commands !== undefined) {
114
+ if (!Array.isArray(task.commands)) {
115
+ report.push(diagnostic("error", "run-commands", `${path}.commands`, "commands 必须是数组", file));
116
+ } else if (task.commands.length > LIMITS.commands) {
117
+ report.push(diagnostic("error", "run-commands", `${path}.commands`, `commands 最多 ${LIMITS.commands} 条`, file));
118
+ } else {
119
+ task.commands.forEach((command, index) => verifyCommand(command, `${path}.commands[${index}]`, file, report));
120
+ }
121
+ }
122
+ textField(task.note, `${path}.note`, file, report, { max: 500 });
123
+ }
124
+
125
+ function verifyFinding(finding, path, file, report) {
126
+ if (!unknownKeys(finding, FINDING_FIELDS, path, file, report)) return;
127
+ if (!SEVERITIES.includes(finding.severity)) {
128
+ report.push(diagnostic("error", "run-severity", `${path}.severity`, `severity 只能是 ${SEVERITIES.join(" / ")}`, file));
129
+ }
130
+ textField(finding.problem, `${path}.problem`, file, report, { required: true });
131
+ textField(finding.requiredFix, `${path}.requiredFix`, file, report, { required: true });
132
+ textField(finding.file, `${path}.file`, file, report, { max: 300 });
133
+ if (finding.line !== undefined && (!Number.isInteger(finding.line) || finding.line < 1)) {
134
+ report.push(diagnostic("error", "run-line", `${path}.line`, "line 必须是正整数", file));
135
+ }
136
+ }
137
+
138
+ function verifyCommand(command, path, file, report) {
139
+ if (!unknownKeys(command, COMMAND_FIELDS, path, file, report)) return;
140
+ textField(command.command, `${path}.command`, file, report, { required: true, max: 500 });
141
+ if (!Number.isInteger(command.exitCode)) {
142
+ report.push(diagnostic("error", "run-exit", `${path}.exitCode`, "exitCode 必须是整数", file));
143
+ }
144
+ textField(command.outputTail, `${path}.outputTail`, file, report, { max: LIMITS.text });
145
+ }
146
+
147
+ /** 取最新一轮里失败的卡片。 */
148
+ export function failedTasksOf(run) {
149
+ const latest = run?.rounds?.at(-1);
150
+ return (latest?.tasks ?? []).filter((task) => task.status === "failed");
151
+ }
152
+
153
+ /**
154
+ * 把最新一轮的失败带进施工图:重试卡的 description 追加一段「上一轮为什么没过」。
155
+ *
156
+ * 只改重试目标那一张卡,其余卡片原样保留——施工图仍然是编译产物,不是运行态。
157
+ */
158
+ export function mergeRunIntoPlan(plan, run) {
159
+ if (plan === null || run === null) return { plan, feedback: [] };
160
+ const failed = failedTasksOf(run);
161
+ const feedback = failed.map((task) => summarizeTask(task));
162
+ const retryId = plan.loop?.onGateFailure?.retry ?? null;
163
+ if (retryId === null || feedback.length === 0) return { plan, feedback };
164
+ const round = (run.rounds?.at(-1)?.round ?? 0) + 1;
165
+ const text = [
166
+ "",
167
+ `上一轮(第 ${round - 1} 轮)失败,重做时先修这些:`,
168
+ ...feedback.map((line, index) => `${index + 1}. ${line}`),
169
+ ].join("\n");
170
+ const tasks = plan.tasks.map((task) =>
171
+ task.id === retryId ? { ...task, description: `${task.description}${text}`, feedback: feedback } : task,
172
+ );
173
+ return { plan: { ...plan, tasks, round, feedback }, feedback };
174
+ }
175
+
176
+ function summarizeTask(task) {
177
+ const parts = [`卡 ${task.id} 未通过`];
178
+ for (const command of task.commands ?? []) {
179
+ if (command.exitCode === 0) continue;
180
+ const tail = String(command.outputTail ?? "").trim().split("\n").slice(-3).join(" / ");
181
+ parts.push(`命令 \`${command.command}\` 退出 ${command.exitCode}${tail ? `(${tail})` : ""}`);
182
+ }
183
+ for (const finding of task.findings ?? []) {
184
+ const where = finding.file ? ` [${finding.file}${finding.line ? `:${finding.line}` : ""}]` : "";
185
+ parts.push(`${finding.severity}: ${finding.problem}${where} → ${finding.requiredFix}`);
186
+ }
187
+ return parts.join(";");
188
+ }
package/lib/write.js CHANGED
@@ -115,14 +115,20 @@ export function readRosterDocument(input) {
115
115
  const dir = resolveRosterDir(root.rootDir, input.id);
116
116
  if (dir === null) return { ok: false, code: "invalid-id" };
117
117
 
118
- for (const name of ROSTER_FILE_NAMES) {
119
- const file = nodePath.join(dir, name);
120
- if (!fs.existsSync(file)) continue;
121
- try {
122
- const text = fs.readFileSync(file, "utf8");
123
- return { ok: true, file: `${input.id}/${name}`, format: formatOf(name), text };
124
- } catch {
125
- return { ok: false, code: "unreadable-file" };
118
+ // 两种布局都读:v1 是 `<root>/<id>/roster.*`,v2 是 `<root>/rosters/<id>/roster.*`。
119
+ // 只看 id 不看布局,调用方不需要知道这座库是哪一代格式。
120
+ const candidates = [dir, nodePath.join(root.rootDir, "rosters", input.id)];
121
+ for (const candidate of candidates) {
122
+ for (const name of ROSTER_FILE_NAMES) {
123
+ const file = nodePath.join(candidate, name);
124
+ if (!fs.existsSync(file)) continue;
125
+ const relative = nodePath.relative(root.rootDir, file).split(nodePath.sep).join("/");
126
+ try {
127
+ const text = fs.readFileSync(file, "utf8");
128
+ return { ok: true, file: relative, format: formatOf(name), text };
129
+ } catch {
130
+ return { ok: false, code: "unreadable-file" };
131
+ }
126
132
  }
127
133
  }
128
134
  return { ok: false, code: "not-found" };
@@ -0,0 +1,130 @@
1
+ {
2
+ "kind": "dsh-agent-roster/compiled@1",
3
+ "source": {
4
+ "roster": "api-design",
5
+ "version": "1.0.0",
6
+ "title": "API 设计评审组",
7
+ "repo": "dsh-agent-roster/dsh-agent-roster",
8
+ "ref": "main",
9
+ "lock": null,
10
+ "file": "roster.yaml"
11
+ },
12
+ "targets": [
13
+ "agent-teams/members"
14
+ ],
15
+ "captain": null,
16
+ "members": [
17
+ {
18
+ "name": "lead",
19
+ "role": "接口负责人",
20
+ "roleRef": "api-design-lead@1.0.0",
21
+ "roleName": "api-design-lead",
22
+ "roleVersion": "1.0.0",
23
+ "provider": "deepseek",
24
+ "model": "deepseek-v4.1-flash",
25
+ "reasoningEffort": "high",
26
+ "toolPolicy": null,
27
+ "executionPrompt": "你负责这次接口评审的编排:先把资源模型、边界与调用方列清楚,再分给两名评审者,最后合并成一份结论。\n不要自己写实现,也不要替调用方假设需求;需求不明就明确写“需要调用方确认”,不要补全。\n结论必须能直接贴进设计文档:决定在前、理由在后,每条都注明是哪位评审者的证据。\n证据不足时给出“暂不定”的选项,并写清还差哪一条信息才能定。",
28
+ "promptStats": {
29
+ "chars": 164,
30
+ "parts": [
31
+ {
32
+ "path": "roles/api-design-lead/AGENT.md",
33
+ "kind": "body+parts"
34
+ },
35
+ {
36
+ "path": "body",
37
+ "kind": "part"
38
+ }
39
+ ],
40
+ "sha256": "dd530ec48e1de4abbcd0979d51c8b7f64273be9dbcf8e7f00dbba5c946aa5aee"
41
+ },
42
+ "provenance": {
43
+ "role": "api-design-lead@1.0.0",
44
+ "sha256": "87f4c33b"
45
+ }
46
+ },
47
+ {
48
+ "name": "schema",
49
+ "role": "结构与演进评审",
50
+ "roleRef": "api-design-schema@1.0.0",
51
+ "roleName": "api-design-schema",
52
+ "roleVersion": "1.0.0",
53
+ "provider": "deepseek",
54
+ "model": "deepseek-v4.1-flash",
55
+ "reasoningEffort": "high",
56
+ "toolPolicy": null,
57
+ "executionPrompt": "你只看结构层面:字段命名是否自解释、分页与过滤是否可组合、写操作是否幂等、错误码能否让调用方分辨“重试有用/没用”。\n不评论实现细节与性能数据,那是另一个人的事;也不要提议重写整个资源模型。\n每条问题给出最小修改方向,并标注它是“现在改”还是“下个版本改”。\n对兼容性拿不准时,写成“破坏性:是/不确定”,不要默认无害。",
58
+ "promptStats": {
59
+ "chars": 160,
60
+ "parts": [
61
+ {
62
+ "path": "roles/api-design-schema/AGENT.md",
63
+ "kind": "body+parts"
64
+ },
65
+ {
66
+ "path": "body",
67
+ "kind": "part"
68
+ }
69
+ ],
70
+ "sha256": "673411e6e249cd0f32c15bff495d572a7345a879388d2d8b0446b309785baec9"
71
+ },
72
+ "provenance": {
73
+ "role": "api-design-schema@1.0.0",
74
+ "sha256": "8cdfbf46"
75
+ }
76
+ },
77
+ {
78
+ "name": "failure-mode",
79
+ "role": "失败语义评审",
80
+ "roleRef": "api-design-failure-mode@1.0.0",
81
+ "roleName": "api-design-failure-mode",
82
+ "roleVersion": "1.0.0",
83
+ "provider": "deepseek",
84
+ "model": "deepseek-v4.1-flash",
85
+ "reasoningEffort": "high",
86
+ "toolPolicy": null,
87
+ "executionPrompt": "你只问一件事:这个接口失败时调用方看到什么、能不能安全重试、会不会把一次失败放大成多次副作用。\n不讨论字段命名与版本策略;只输出失败场景、可观测信号与契约要求。\n每条场景写成“触发条件 → 调用方看到的状态 → 要求实现方保证什么”。\n无法判定是否幂等的接口,直接要求补幂等键,不要用“一般来说”糊过去。",
88
+ "promptStats": {
89
+ "chars": 152,
90
+ "parts": [
91
+ {
92
+ "path": "roles/api-design-failure-mode/AGENT.md",
93
+ "kind": "body+parts"
94
+ },
95
+ {
96
+ "path": "body",
97
+ "kind": "part"
98
+ }
99
+ ],
100
+ "sha256": "5259649be58f339e703ed691df24246479b2fab73520526b2f4a2b0fb1c4c3db"
101
+ },
102
+ "provenance": {
103
+ "role": "api-design-failure-mode@1.0.0",
104
+ "sha256": "2633b9d8"
105
+ }
106
+ }
107
+ ],
108
+ "orchestration": null,
109
+ "declarations": [
110
+ {
111
+ "scope": "roster",
112
+ "field": "defaults.route",
113
+ "note": "materialized:已展开成每个成员的显式值"
114
+ },
115
+ {
116
+ "scope": "roster",
117
+ "field": "defaults.route",
118
+ "note": "materialized:已展开成每个成员的显式值"
119
+ },
120
+ {
121
+ "scope": "roster",
122
+ "field": "defaults.route",
123
+ "note": "materialized:已展开成每个成员的显式值"
124
+ }
125
+ ],
126
+ "requires": {
127
+ "skills": [],
128
+ "toolPolicies": []
129
+ }
130
+ }
@@ -0,0 +1,130 @@
1
+ {
2
+ "kind": "dsh-agent-roster/compiled@1",
3
+ "source": {
4
+ "roster": "content-ops",
5
+ "version": "1.0.0",
6
+ "title": "内容运营组",
7
+ "repo": "dsh-agent-roster/dsh-agent-roster",
8
+ "ref": "main",
9
+ "lock": null,
10
+ "file": "roster.yaml"
11
+ },
12
+ "targets": [
13
+ "agent-teams/members"
14
+ ],
15
+ "captain": null,
16
+ "members": [
17
+ {
18
+ "name": "lead",
19
+ "role": "内容负责人",
20
+ "roleRef": "content-ops-lead@1.0.0",
21
+ "roleName": "content-ops-lead",
22
+ "roleVersion": "1.0.0",
23
+ "provider": "deepseek",
24
+ "model": "deepseek-v4.1-flash",
25
+ "reasoningEffort": "high",
26
+ "toolPolicy": null,
27
+ "executionPrompt": "你定选题:读者是谁、读完能带走什么、发在哪个渠道,以及每项的验收。\n不写正文,也不做排版;你负责取舍与排期。\n产出是一页 brief:读者 · 主张 · 证据要求 · 渠道 · 截止。\n选题无法说清“读者为什么要读”时,换题而不是硬写。",
28
+ "promptStats": {
29
+ "chars": 118,
30
+ "parts": [
31
+ {
32
+ "path": "roles/content-ops-lead/AGENT.md",
33
+ "kind": "body+parts"
34
+ },
35
+ {
36
+ "path": "body",
37
+ "kind": "part"
38
+ }
39
+ ],
40
+ "sha256": "1e7bbbff7b441f2acd3c46e1ea3ba848e2fd5ffbf9f140c9437b8cfd76ccaede"
41
+ },
42
+ "provenance": {
43
+ "role": "content-ops-lead@1.0.0",
44
+ "sha256": "b8172fa1"
45
+ }
46
+ },
47
+ {
48
+ "name": "editor",
49
+ "role": "成稿编辑",
50
+ "roleRef": "content-ops-editor@1.0.0",
51
+ "roleName": "content-ops-editor",
52
+ "roleVersion": "1.0.0",
53
+ "provider": "deepseek",
54
+ "model": "deepseek-v4.1-flash",
55
+ "reasoningEffort": "high",
56
+ "toolPolicy": null,
57
+ "executionPrompt": "你编辑成稿:每段一个意思,删掉无法验证的形容词,给每个主张补证据或删除。\n不改事实,也不加未经验证的数据;发现缺证据就标出来让对方补。\n交付改后稿 + 一段“我删了什么、为什么”。\n拿不准的事实一律降级为“据说/待确认”,不要保留确定性语气。",
58
+ "promptStats": {
59
+ "chars": 121,
60
+ "parts": [
61
+ {
62
+ "path": "roles/content-ops-editor/AGENT.md",
63
+ "kind": "body+parts"
64
+ },
65
+ {
66
+ "path": "body",
67
+ "kind": "part"
68
+ }
69
+ ],
70
+ "sha256": "a44b7f0dbb313283234bb933c3cb8d8e58a4e746279bd623b9418b155c6f6b9d"
71
+ },
72
+ "provenance": {
73
+ "role": "content-ops-editor@1.0.0",
74
+ "sha256": "4414cd52"
75
+ }
76
+ },
77
+ {
78
+ "name": "distributor",
79
+ "role": "分发与回读",
80
+ "roleRef": "content-ops-distributor@1.0.0",
81
+ "roleName": "content-ops-distributor",
82
+ "roleVersion": "1.0.0",
83
+ "provider": "deepseek",
84
+ "model": "deepseek-v4.1-flash",
85
+ "reasoningEffort": "high",
86
+ "toolPolicy": null,
87
+ "executionPrompt": "你按渠道改写标题与首段(同一主张不同入口),并规划发布时间与配套动作。\n不改正文主张;只调整入口表达与分发节奏。\n发布后回收读数:曝光、点击、读完率,并说明哪些指标不可得。\n读数缺失时如实写“无法获取”,不要用估算值冒充实测。",
88
+ "promptStats": {
89
+ "chars": 113,
90
+ "parts": [
91
+ {
92
+ "path": "roles/content-ops-distributor/AGENT.md",
93
+ "kind": "body+parts"
94
+ },
95
+ {
96
+ "path": "body",
97
+ "kind": "part"
98
+ }
99
+ ],
100
+ "sha256": "a310ca5dc37ea2051e3c4a996aa5f8c12fa225d95964d0e1bf074194975c1c20"
101
+ },
102
+ "provenance": {
103
+ "role": "content-ops-distributor@1.0.0",
104
+ "sha256": "330a108f"
105
+ }
106
+ }
107
+ ],
108
+ "orchestration": null,
109
+ "declarations": [
110
+ {
111
+ "scope": "roster",
112
+ "field": "defaults.route",
113
+ "note": "materialized:已展开成每个成员的显式值"
114
+ },
115
+ {
116
+ "scope": "roster",
117
+ "field": "defaults.route",
118
+ "note": "materialized:已展开成每个成员的显式值"
119
+ },
120
+ {
121
+ "scope": "roster",
122
+ "field": "defaults.route",
123
+ "note": "materialized:已展开成每个成员的显式值"
124
+ }
125
+ ],
126
+ "requires": {
127
+ "skills": [],
128
+ "toolPolicies": []
129
+ }
130
+ }
@@ -0,0 +1,130 @@
1
+ {
2
+ "kind": "dsh-agent-roster/compiled@1",
3
+ "source": {
4
+ "roster": "contract-review",
5
+ "version": "1.0.0",
6
+ "title": "合同审阅组",
7
+ "repo": "dsh-agent-roster/dsh-agent-roster",
8
+ "ref": "main",
9
+ "lock": null,
10
+ "file": "roster.yaml"
11
+ },
12
+ "targets": [
13
+ "agent-teams/members"
14
+ ],
15
+ "captain": null,
16
+ "members": [
17
+ {
18
+ "name": "lead",
19
+ "role": "审阅负责人",
20
+ "roleRef": "contract-review-lead@1.0.0",
21
+ "roleName": "contract-review-lead",
22
+ "roleVersion": "1.0.0",
23
+ "provider": "deepseek",
24
+ "model": "deepseek-v4.1-flash",
25
+ "reasoningEffort": "high",
26
+ "toolPolicy": null,
27
+ "executionPrompt": "你先确认我方角色与商业目标,再定红线:哪些条款出现就不签。\n不逐条解释条款;你负责范围与最终建议(签 / 改 / 不签)。\n产出是红线清单 + 建议的谈判顺序。\n立场不明时先问“最坏情况下我们能承受什么”,不要默认接受。",
28
+ "promptStats": {
29
+ "chars": 110,
30
+ "parts": [
31
+ {
32
+ "path": "roles/contract-review-lead/AGENT.md",
33
+ "kind": "body+parts"
34
+ },
35
+ {
36
+ "path": "body",
37
+ "kind": "part"
38
+ }
39
+ ],
40
+ "sha256": "a2b619fee43aa71724fdc4d95908e1e73c050a5edb484b0284f2d39bc5afd83f"
41
+ },
42
+ "provenance": {
43
+ "role": "contract-review-lead@1.0.0",
44
+ "sha256": "b086fec1"
45
+ }
46
+ },
47
+ {
48
+ "name": "clause-reader",
49
+ "role": "条款识别",
50
+ "roleRef": "contract-review-clause-reader@1.0.0",
51
+ "roleName": "contract-review-clause-reader",
52
+ "roleVersion": "1.0.0",
53
+ "provider": "deepseek",
54
+ "model": "deepseek-v4.1-flash",
55
+ "reasoningEffort": "high",
56
+ "toolPolicy": null,
57
+ "executionPrompt": "你摘出关键条款:责任上限、赔偿、终止与续约、知识产权归属、保密、争议解决。\n不做商业判断;每条必须给条款编号与原文摘录。\n输出“条款 · 原文 · 位置 · 对我方的影响(有利/中性/不利)”。\n条款缺失同样重要:明确写“未见某类条款”,不要当作默认有利。",
58
+ "promptStats": {
59
+ "chars": 128,
60
+ "parts": [
61
+ {
62
+ "path": "roles/contract-review-clause-reader/AGENT.md",
63
+ "kind": "body+parts"
64
+ },
65
+ {
66
+ "path": "body",
67
+ "kind": "part"
68
+ }
69
+ ],
70
+ "sha256": "4a21ae8ee5c4ad9874d35ab82abbe0c0b5a0469627d56c28677b9e83a55cfbeb"
71
+ },
72
+ "provenance": {
73
+ "role": "contract-review-clause-reader@1.0.0",
74
+ "sha256": "cfd70c98"
75
+ }
76
+ },
77
+ {
78
+ "name": "risk-note",
79
+ "role": "风险提示",
80
+ "roleRef": "contract-review-risk-note@1.0.0",
81
+ "roleName": "contract-review-risk-note",
82
+ "roleVersion": "1.0.0",
83
+ "provider": "deepseek",
84
+ "model": "deepseek-v4.1-flash",
85
+ "reasoningEffort": "high",
86
+ "toolPolicy": null,
87
+ "executionPrompt": "你把条款翻译成后果:最坏情形下赔多少、被锁定多久、退出成本多少。\n不写法律意见书,也不替代律师;你给的是量化提示与修改方向。\n输出“风险 · 触发条件 · 最坏敞口 · 建议改法(一句话)”。\n无法量化时给区间并说明假设,不要给“风险较低”这种结论。",
88
+ "promptStats": {
89
+ "chars": 125,
90
+ "parts": [
91
+ {
92
+ "path": "roles/contract-review-risk-note/AGENT.md",
93
+ "kind": "body+parts"
94
+ },
95
+ {
96
+ "path": "body",
97
+ "kind": "part"
98
+ }
99
+ ],
100
+ "sha256": "fba0572e317e8a2d787254814fb970282dbeb01803495be09e0b9c9f88162b45"
101
+ },
102
+ "provenance": {
103
+ "role": "contract-review-risk-note@1.0.0",
104
+ "sha256": "b589aa0d"
105
+ }
106
+ }
107
+ ],
108
+ "orchestration": null,
109
+ "declarations": [
110
+ {
111
+ "scope": "roster",
112
+ "field": "defaults.route",
113
+ "note": "materialized:已展开成每个成员的显式值"
114
+ },
115
+ {
116
+ "scope": "roster",
117
+ "field": "defaults.route",
118
+ "note": "materialized:已展开成每个成员的显式值"
119
+ },
120
+ {
121
+ "scope": "roster",
122
+ "field": "defaults.route",
123
+ "note": "materialized:已展开成每个成员的显式值"
124
+ }
125
+ ],
126
+ "requires": {
127
+ "skills": [],
128
+ "toolPolicies": []
129
+ }
130
+ }