@tea-agent/loop-agent 0.33.0 → 0.33.2

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 (49) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/worker/console/operator-surface-health.js +1 -0
  3. package/dist/worker/console/static/assets/index-CnUXAqxG.css +1 -0
  4. package/dist/worker/console/static/assets/{index-3R-GT3a_.js → index-PzYzcuFG.js} +17 -17
  5. package/dist/worker/console/static/index.html +3 -2
  6. package/dist/worker/console/static-src/app/useRecoveryConsole.js +3 -2
  7. package/dist/worker/observe/routes.js +3 -8
  8. package/dist/worker/observe/static/app.js +11 -0
  9. package/dist/worker/observe/static/index.html +11 -43
  10. package/dist/worker/observe/static/kpi.js +2 -24
  11. package/dist/worker/observe/static/operator-chrome.css +480 -0
  12. package/dist/worker/observe/static/operator-chrome.d.ts +82 -0
  13. package/dist/worker/observe/static/operator-chrome.js +554 -0
  14. package/dist/worker/observe/static/shell-chrome.js +1 -11
  15. package/dist/worker/observe/static/styles.css +62 -269
  16. package/dist/worker/observe/static/views/dashboard.js +52 -4
  17. package/dist/workflows/dag/retry-policy.js +5 -0
  18. package/package.json +1 -1
  19. package/skills/analyze-product-dependencies/SKILL.md +74 -33
  20. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +16 -11
  21. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +20 -10
  22. package/skills/analyze-product-dependencies/references/example.md +9 -9
  23. package/skills/analyze-product-dependencies/references/forward-test-cases.md +93 -18
  24. package/skills/analyze-product-dependencies/references/input-contract.md +27 -4
  25. package/skills/analyze-product-dependencies/references/kb-integration.md +64 -0
  26. package/skills/analyze-product-dependencies/references/scouting-rules.md +25 -10
  27. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +247 -54
  28. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +122 -29
  29. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +94 -36
  30. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +37 -23
  31. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +35 -43
  32. package/skills/analyze-product-requirements/SKILL.md +98 -43
  33. package/skills/analyze-product-requirements/references/acceptance-criteria.md +8 -12
  34. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +28 -14
  35. package/skills/analyze-product-requirements/references/example.md +24 -6
  36. package/skills/analyze-product-requirements/references/forward-test-cases.md +87 -9
  37. package/skills/analyze-product-requirements/references/kb-integration.md +56 -0
  38. package/skills/analyze-product-requirements/references/product-analysis-schema.md +19 -10
  39. package/skills/analyze-product-requirements/references/product-requirement-schema.md +21 -12
  40. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +45 -14
  41. package/skills/analyze-product-requirements/scripts/compute-source-identity.mjs +35 -0
  42. package/skills/analyze-product-requirements/scripts/test-validators.mjs +337 -29
  43. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +38 -7
  44. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +41 -29
  45. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +33 -22
  46. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +43 -24
  47. package/dist/worker/console/static/assets/index-B0EQt_yq.css +0 -1
  48. package/skills/analyze-product-dependencies/agents/openai.yaml +0 -4
  49. package/skills/analyze-product-requirements/agents/openai.yaml +0 -4
@@ -76,59 +76,6 @@ body {
76
76
  line-height: 1.5;
77
77
  }
78
78
 
79
- .embedded-operator-nav {
80
- display: none;
81
- }
82
-
83
- body[data-embedded-shell="true"] .site-header .logo {
84
- display: none;
85
- }
86
-
87
- body[data-embedded-shell="true"] .site-header {
88
- position: sticky;
89
- top: 49px;
90
- z-index: 30;
91
- grid-template-columns: 1fr auto;
92
- grid-template-areas: "nav status" "crumb crumb";
93
- min-height: 56px;
94
- margin-bottom: var(--space-5);
95
- padding: 10px 0;
96
- }
97
-
98
- body[data-embedded-shell="true"] .embedded-operator-nav {
99
- position: sticky;
100
- top: 0;
101
- z-index: 40;
102
- display: flex;
103
- gap: 6px;
104
- align-items: center;
105
- padding: 8px max(24px, calc((100vw - 1680px) / 2));
106
- border-bottom: 1px solid var(--hairline);
107
- background: color-mix(in srgb, var(--canvas) 92%, transparent);
108
- backdrop-filter: blur(10px);
109
- font-size: 13px;
110
- font-weight: 700;
111
- }
112
-
113
- .embedded-operator-nav a,
114
- .embedded-operator-nav span {
115
- padding: 6px 12px;
116
- border-radius: var(--radius-pill);
117
- text-decoration: none;
118
- }
119
-
120
- .embedded-operator-nav a:hover,
121
- .embedded-operator-nav a:focus-visible {
122
- background: var(--surface);
123
- color: var(--orange-active);
124
- outline: none;
125
- }
126
-
127
- .embedded-operator-nav span {
128
- background: var(--ink);
129
- color: #fff;
130
- }
131
-
132
79
  button,
133
80
  input,
134
81
  select,
@@ -149,7 +96,6 @@ pre,
149
96
  .dag-node-table td:nth-child(2),
150
97
  td:first-child,
151
98
  .meta-item dd,
152
- .header-refresh,
153
99
  .kpi-value,
154
100
  .risk-total,
155
101
  .process-timeline-time {
@@ -176,140 +122,13 @@ td:first-child,
176
122
  #app {
177
123
  width: min(100% - 48px, 1680px);
178
124
  margin: 0 auto;
179
- padding-bottom: 56px;
180
- }
181
-
182
- .site-header {
183
- position: sticky;
184
- top: 0;
185
- z-index: 30;
186
- display: grid;
187
- grid-template-columns: auto 1fr auto;
188
- grid-template-areas: "brand nav status" "crumb crumb crumb";
189
- column-gap: var(--space-5);
190
- align-items: center;
191
- min-height: 72px;
192
- margin-bottom: var(--space-6);
193
- padding: 14px 0 12px;
194
- background: color-mix(in srgb, var(--canvas) 82%, transparent);
195
- border-bottom: 1px solid var(--hairline);
196
- backdrop-filter: blur(12px);
197
- }
198
-
199
- .logo {
200
- grid-area: brand;
201
- display: inline-flex;
202
- align-items: center;
203
- gap: 10px;
204
- color: var(--ink);
205
- font-size: 15px;
206
- font-weight: 650;
207
- letter-spacing: -0.01em;
208
- text-decoration: none;
209
- }
210
-
211
- .logo > i {
212
- display: inline-flex;
213
- align-items: center;
214
- justify-content: center;
215
- width: 32px;
216
- height: 32px;
217
- border-radius: var(--radius-md);
218
- background: var(--orange-soft);
219
- color: var(--orange-active);
220
- font-size: 18px;
221
- font-weight: 400;
222
- }
223
-
224
- .logo:hover {
225
- color: var(--orange-active);
226
- }
227
-
228
- .top-nav {
229
- grid-area: nav;
230
- display: inline-flex;
231
- flex-wrap: wrap;
232
- align-items: center;
233
- gap: 4px;
234
- justify-self: start;
235
- padding: 5px;
236
- border: 1px solid var(--hairline);
237
- border-radius: var(--radius-pill);
238
- background: color-mix(in srgb, var(--surface) 88%, transparent);
239
- box-shadow: var(--shadow-panel);
240
- }
241
-
242
- .nav-link {
243
- display: inline-flex;
244
- align-items: center;
245
- gap: 7px;
246
- min-height: 34px;
247
- padding: 0 14px;
248
- border: 1px solid transparent;
249
- border-radius: var(--radius-pill);
250
- color: var(--body);
251
- font-size: 13px;
252
- font-weight: 600;
253
- text-decoration: none;
254
- transition:
255
- border-color var(--transition-fast),
256
- background-color var(--transition-fast),
257
- color var(--transition-fast),
258
- box-shadow var(--transition-fast);
259
- }
260
-
261
- .nav-link > i {
262
- font-size: 15px;
263
- font-weight: 400;
264
- }
265
-
266
- .nav-link:hover,
267
- .nav-link:focus-visible {
268
- border-color: transparent;
269
- background: var(--surface);
270
- color: var(--ink);
271
- outline: none;
272
- }
273
-
274
- .nav-link-active {
275
- border-color: transparent;
276
- background: var(--ink);
277
- color: #fff;
278
- box-shadow: none;
279
- }
280
-
281
- .nav-link-active:hover,
282
- .nav-link-active:focus-visible {
283
- background: var(--ink);
284
- color: #fff;
285
- }
286
-
287
- .header-meta {
288
- grid-area: status;
289
- display: inline-flex;
290
- justify-self: end;
291
- align-items: center;
292
- gap: 8px;
293
- min-height: 34px;
294
- padding: 0 12px;
295
- border: 1px solid var(--hairline);
296
- border-radius: var(--radius-pill);
297
- background: var(--surface);
298
- color: var(--muted);
299
- font-size: 12px;
300
- font-variant-numeric: tabular-nums;
301
- box-shadow: var(--shadow-panel);
302
- }
303
-
304
- .header-meta > i {
305
- color: color-mix(in srgb, var(--green) 70%, white);
306
- font-size: 14px;
125
+ padding: var(--space-5) 0 56px;
307
126
  }
308
127
 
309
128
  .breadcrumb {
310
- grid-area: crumb;
311
129
  display: flex;
312
130
  min-height: 16px;
131
+ margin-bottom: var(--space-4);
313
132
  gap: 7px;
314
133
  color: var(--muted);
315
134
  font-size: 12px;
@@ -344,6 +163,65 @@ td:first-child,
344
163
  gap: var(--space-4);
345
164
  }
346
165
 
166
+ /* 「昨夜班次」条带:夜间结果在观测总览的唯一一级呈现(管理动作在操作侧) */
167
+ .night-strip {
168
+ display: flex;
169
+ align-items: center;
170
+ gap: 14px;
171
+ padding: 13px 18px;
172
+ margin-bottom: var(--space-4);
173
+ border-left: 3px solid var(--lavender);
174
+ background: linear-gradient(
175
+ 90deg,
176
+ color-mix(in srgb, var(--lavender) 22%, var(--surface)),
177
+ var(--surface) 55%
178
+ );
179
+ }
180
+ .night-strip[hidden] {
181
+ display: none;
182
+ }
183
+ .night-strip-icon {
184
+ display: inline-flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ flex: 0 0 auto;
188
+ width: 34px;
189
+ height: 34px;
190
+ border-radius: 10px;
191
+ background: color-mix(in srgb, var(--lavender) 55%, var(--surface));
192
+ color: #6a4a9e;
193
+ font-size: 16px;
194
+ }
195
+ .night-strip-main {
196
+ min-width: 0;
197
+ }
198
+ .night-strip-title {
199
+ font-size: 13px;
200
+ font-weight: 700;
201
+ color: var(--ink);
202
+ }
203
+ .night-strip-sub {
204
+ margin-top: 1px;
205
+ font-size: 12px;
206
+ color: var(--body);
207
+ font-variant-numeric: tabular-nums;
208
+ }
209
+ .night-strip-actions {
210
+ margin-left: auto;
211
+ display: inline-flex;
212
+ align-items: center;
213
+ gap: 8px;
214
+ flex-shrink: 0;
215
+ }
216
+ .night-strip-manage {
217
+ border-color: color-mix(in srgb, var(--orange) 35%, var(--hairline));
218
+ color: var(--orange-active);
219
+ }
220
+ .night-strip-manage:hover {
221
+ border-color: var(--orange);
222
+ background: var(--orange-soft);
223
+ }
224
+
347
225
  .view-heading {
348
226
  display: grid;
349
227
  gap: 4px;
@@ -638,75 +516,6 @@ td:first-child,
638
516
  gap: var(--space-3);
639
517
  }
640
518
 
641
- .repo-banner {
642
- display: flex;
643
- align-items: flex-start;
644
- gap: 10px;
645
- padding: 6px 12px;
646
- border: 1px solid var(--hairline);
647
- border-radius: var(--radius-md);
648
- background: transparent;
649
- }
650
- .repo-banner-inline {
651
- justify-self: end;
652
- align-items: center;
653
- max-width: min(48ch, 100%);
654
- padding: 4px 10px 4px 8px;
655
- border: 1px solid var(--hairline);
656
- border-radius: 999px;
657
- background: var(--canvas-soft);
658
- }
659
- .repo-banner-icon {
660
- display: inline-flex;
661
- align-items: center;
662
- justify-content: center;
663
- flex: 0 0 auto;
664
- width: 28px;
665
- height: 28px;
666
- border-radius: 8px;
667
- background: var(--surface);
668
- border: 1px solid var(--hairline);
669
- color: var(--orange-active);
670
- }
671
- .repo-banner-icon > i {
672
- font-size: 15px;
673
- line-height: 1;
674
- }
675
- .repo-banner-inline .repo-banner-text {
676
- display: grid;
677
- justify-items: start;
678
- gap: 1px;
679
- min-width: 0;
680
- }
681
- .repo-banner-text {
682
- display: flex;
683
- flex-wrap: wrap;
684
- align-items: baseline;
685
- gap: 6px 10px;
686
- min-width: 0;
687
- }
688
- .repo-banner-name {
689
- color: var(--ink);
690
- font-size: 13px;
691
- font-weight: 700;
692
- letter-spacing: 0.01em;
693
- line-height: 1.25;
694
- overflow: hidden;
695
- text-overflow: ellipsis;
696
- white-space: nowrap;
697
- }
698
- .repo-banner-path {
699
- color: var(--muted);
700
- font-size: 11px;
701
- line-height: 1.35;
702
- font-variant-numeric: tabular-nums;
703
- letter-spacing: 0.01em;
704
- overflow: hidden;
705
- text-overflow: ellipsis;
706
- white-space: nowrap;
707
- max-width: 36ch;
708
- }
709
-
710
519
  .panel-kpi {
711
520
  display: grid;
712
521
  grid-template-columns: var(--dashboard-cols);
@@ -1315,7 +1124,7 @@ tr.clickable:focus-within {
1315
1124
  .dag-inspector {
1316
1125
  position: fixed;
1317
1126
  z-index: 40;
1318
- /* Align with site-header top edge; same inset as right/bottom. */
1127
+ /* Align near the unified chrome bottom edge; same inset as right/bottom. */
1319
1128
  top: 24px;
1320
1129
  right: 24px;
1321
1130
  bottom: 24px;
@@ -2845,11 +2654,6 @@ body.is-resizing-dag-graph {
2845
2654
  #app {
2846
2655
  width: min(100% - 32px, 1680px);
2847
2656
  }
2848
- .site-header {
2849
- grid-template-columns: auto 1fr auto;
2850
- grid-template-areas: "brand nav status" "crumb crumb crumb";
2851
- row-gap: 8px;
2852
- }
2853
2657
  .run-detail-workspace {
2854
2658
  grid-template-columns: 1fr;
2855
2659
  }
@@ -2864,14 +2668,6 @@ body.is-resizing-dag-graph {
2864
2668
  .dashboard-page-header {
2865
2669
  grid-template-columns: 1fr;
2866
2670
  }
2867
- .repo-banner-inline {
2868
- justify-self: start;
2869
- max-width: none;
2870
- }
2871
- .repo-banner-path {
2872
- max-width: none;
2873
- white-space: normal;
2874
- }
2875
2671
  .pool-toolbar {
2876
2672
  justify-items: start;
2877
2673
  }
@@ -2888,9 +2684,6 @@ body.is-resizing-dag-graph {
2888
2684
  #app {
2889
2685
  width: min(100% - 24px, 1680px);
2890
2686
  }
2891
- .site-header {
2892
- margin-bottom: var(--space-6);
2893
- }
2894
2687
  .kpi-grid {
2895
2688
  grid-template-columns: repeat(2, minmax(0, 1fr));
2896
2689
  }
@@ -16,7 +16,7 @@ import {
16
16
  DAG_EFFECTIVE_STATUS_LABELS,
17
17
  STATUS_LABELS,
18
18
  } from "../constants.js";
19
- import { el, reconcileKeyed, syncCompatibleNode } from "../dom.js";
19
+ import { clearNode, el, reconcileKeyed, syncCompatibleNode } from "../dom.js";
20
20
  import {
21
21
  badge,
22
22
  badgeClass,
@@ -65,7 +65,7 @@ import {
65
65
  } from "../dag-model.js";
66
66
  import { layoutDag } from "../dag-layout.js";
67
67
  import { showView, setBreadcrumb, updateHeaderRefresh } from "../shell-chrome.js";
68
- import { kpiCard, renderRepoBanner, renderProjectionFault, failuresInboxLink } from "../kpi.js";
68
+ import { kpiCard, renderProjectionFault, failuresInboxLink } from "../kpi.js";
69
69
  import {
70
70
  dagSortTime,
71
71
  compareActiveDagUrgency,
@@ -82,6 +82,55 @@ let kpiDagGroup = null;
82
82
  // removed once on first success so subsequent polls never see it again.
83
83
  let dashboardLoadingNode = null;
84
84
 
85
+ /**
86
+ * 「昨夜班次」条带:夜间结果在观测侧的唯一一级呈现(替代旧「夜间任务」tab)。
87
+ * 管理动作(晨审处置)始终落在操作侧 /#/night,保持 Inspect 只读不变式。
88
+ */
89
+ async function renderNightStrip() {
90
+ const strip = document.getElementById("dashboard-night");
91
+ if (!strip) return;
92
+ const morning = await fetchJson("/api/night-jobs/morning");
93
+ const summary = morning?.summary ?? null;
94
+ if (!morning || !summary || !(summary.total > 0)) {
95
+ strip.hidden = true;
96
+ clearNode(strip);
97
+ return;
98
+ }
99
+ clearNode(strip);
100
+ strip.hidden = false;
101
+
102
+ const iconWrap = el("span", "night-strip-icon");
103
+ const moon = el("i", "ri-moon-clear-line");
104
+ moon.setAttribute("aria-hidden", "true");
105
+ iconWrap.appendChild(moon);
106
+ strip.appendChild(iconWrap);
107
+
108
+ const main = el("div", "night-strip-main");
109
+ main.appendChild(el("div", "night-strip-title", `昨夜班次 · ${morning.date ?? ""}`));
110
+ const parts = [
111
+ `${summary.succeeded ?? 0} 通过`,
112
+ `${summary.failed ?? 0} 失败`,
113
+ ];
114
+ if (summary.humanRequired > 0) parts.push(`${summary.humanRequired} 需人工`);
115
+ if (summary.pendingHarvest > 0) parts.push(`${summary.pendingHarvest} 待 harvest`);
116
+ main.appendChild(el("div", "night-strip-sub", parts.join(" · ")));
117
+ strip.appendChild(main);
118
+
119
+ const actions = el("div", "night-strip-actions");
120
+ const detail = el("a", "page-action-btn", "夜间详情");
121
+ detail.href = "#/night";
122
+ detail.addEventListener("click", (e) => {
123
+ e.preventDefault();
124
+ navigate("/night");
125
+ });
126
+ actions.appendChild(detail);
127
+ const manage = el("a", "page-action-btn night-strip-manage", "晨审处置 →");
128
+ manage.href = "/#/night";
129
+ manage.title = "夜间计划的管理与晨审处置在操作台完成(Inspect 保持只读)";
130
+ actions.appendChild(manage);
131
+ strip.appendChild(actions);
132
+ }
133
+
85
134
  export async function renderDashboard(scrollTo) {
86
135
  showView("dashboard");
87
136
  setBreadcrumb([{ label: UI_TEXT.dashboard }]);
@@ -92,7 +141,6 @@ export async function renderDashboard(scrollTo) {
92
141
  const riskEl = document.getElementById("dashboard-risk");
93
142
  const dagsEl = document.getElementById("dashboard-dags");
94
143
  const batchesEl = document.getElementById("dashboard-batches");
95
- const repoEl = document.getElementById("dashboard-repo");
96
144
  const host = kpiEl || featureEl || activeEl;
97
145
  // Loading only mounts before the first successful snapshot. Subsequent
98
146
  // successful polls reuse the stable DOM and never flash the loading state.
@@ -211,7 +259,7 @@ export async function renderDashboard(scrollTo) {
211
259
  },
212
260
  );
213
261
 
214
- renderRepoBanner(repoEl, snapshot);
262
+ renderNightStrip();
215
263
 
216
264
  const dagHealth = snapshot.health?.dag;
217
265
  const dagActiveRuns = dagHealth?.activeRuns ?? activeDags.length;
@@ -19,6 +19,8 @@ export const DEFAULT_DAG_RETRY_CATEGORIES = [
19
19
  ];
20
20
  export const STRUCTURED_OUTPUT_RETRY_CATEGORY = "output-too-large";
21
21
  export const PROTOCOL_INVALID_RETRY_CATEGORY = "protocol-invalid";
22
+ /** Recoverable model artifact/schema formatting failure on read-only structured nodes. */
23
+ export const STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY = "invalid-output";
22
24
  /** Retry only a proven no-op from an explicitly opt-in bounded Pi writer. */
23
25
  export const WRITER_EMPTY_DIFF_RETRY_CATEGORY = "writer-empty-diff";
24
26
  /** Retry when a backend-test writer finished but Completeness Gate found missing/broken targets. */
@@ -26,16 +28,19 @@ export const INCOMPLETE_WRITE_SET_RETRY_CATEGORY = "incomplete-write-set";
26
28
  export const STRUCTURED_REQUIRED_DAG_RETRY_CATEGORIES = [
27
29
  ...DEFAULT_DAG_RETRY_CATEGORIES,
28
30
  STRUCTURED_OUTPUT_RETRY_CATEGORY,
31
+ STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
29
32
  ];
30
33
  /** Categories allowed on nodes that declare a machine-readable outputProtocol. */
31
34
  export const PROTOCOL_AWARE_DAG_RETRY_CATEGORIES = [
32
35
  ...DEFAULT_DAG_RETRY_CATEGORIES,
33
36
  PROTOCOL_INVALID_RETRY_CATEGORY,
37
+ STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
34
38
  ];
35
39
  export const ALL_DAG_RETRY_CATEGORIES = [
36
40
  ...DEFAULT_DAG_RETRY_CATEGORIES,
37
41
  STRUCTURED_OUTPUT_RETRY_CATEGORY,
38
42
  PROTOCOL_INVALID_RETRY_CATEGORY,
43
+ STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
39
44
  WRITER_EMPTY_DIFF_RETRY_CATEGORY,
40
45
  INCOMPLETE_WRITE_SET_RETRY_CATEGORY,
41
46
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.33.0",
3
+ "version": "0.33.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -1,67 +1,108 @@
1
1
  ---
2
2
  name: analyze-product-dependencies
3
- description: 基于 complete Product Requirement 探索代码库,按 frontend、backend 或 both 范围将故事、输出规范和验收标准映射到真实文件、组件、服务、数据、权限与证据,并仅为 API 型后端故事生成精简 Swagger 风格 Markdown API 文档。用于代码影响分析、依赖分析、API 文档或需求到代码映射。
3
+ description: 面向采用 Product Requirement V4、Web/Remote 接口分类和统一 HTTP 200 响应包络的组织内部项目,先根据选中需求故事强制检索项目知识库,再探索代码库,按 frontend、backend 或 both 范围将故事、验收标准及 API 型后端故事映射到真实文件、路由、组件、服务、数据、权限与证据;API 场景从同一规范中间模型生成纯 HTTP 契约文档,或用 api-only 模式单独更新 API 文档。用于代码影响分析、依赖分析、API 文档或需求到代码映射;也适用于“这个需求要改哪些文件”“帮我生成接口文档”“需求对应到哪段代码”“评估下这个需求的开发影响”这类请求。
4
4
  ---
5
5
 
6
6
  # Analyze Product Dependencies
7
7
 
8
8
  IRON LAW:`product-requirement.md` 是唯一需求事实源。不得从原始需求、Product Analysis、Clarification 或聊天重新解释需求,不得修改代码或上游产物。
9
9
 
10
+ ## 运行环境与外部能力
11
+
12
+ - 必需:可读写当前项目文件,并能运行 Node.js `.mjs` 校验器。
13
+ - 前置第三方 skill:`kb-design-assist`,用于根据选中需求故事检索项目知识库中的设计系统、业务规范、API 规范和其他项目约定。
14
+ - 可选能力:隔离的只读代码侦察 agent。
15
+ - 缺少隔离代码侦察 agent 时执行对应 fallback;`kb-design-assist` 为 `unavailable` 时必须先让用户选择“修复后重试”或“跳过知识库”,不得直接 fallback,也不得伪造知识库结果、代码证据或 agent 结果。
16
+ - 调用第三方 skill 时使用当前宿主提供的原生 skill 加载机制,不假定具体命令或工具名称。
17
+
10
18
  ## 输入与产物
11
19
 
12
- - 必填:complete `product-requirement.md` 的实际路径和可读取代码仓库路径;Product Requirement 必须位于项目根的需求目录。
20
+ - 必填:complete `product-requirement.md` 的实际路径和可读取代码仓库路径。Product Requirement 必须位于 `<project-root>/docs/product-analysis/<requirement-id>/`。
13
21
  - 可选:`target=frontend|backend|both` 或 `--target frontend|backend|both`;两种写法等价,默认继承上游 scope。
14
- - 输出写回 Product Requirement 所在目录;始终生成 `dependency-analysis.md`,选中 API 型后端故事时额外生成 `api-documentation.md`。
22
+ - 可选:`mode=full|api-only` `--mode full|api-only`;两种写法等价,默认 `full`。`full` 生成依赖分析并按需生成 API 文档;`api-only` 只生成 `api-documentation.md`,不创建或修改 `dependency-analysis.md`。
23
+ - 可选:frontend/both 可传 `api_documentation=<任意可读 Markdown API 文档路径>` 或 `--api-doc <path>`。外部文档只读,前端产物只记录实际引用的 Method+Path。
24
+ - 输出写回 Product Requirement 所在目录。`api-only` 仅适用于 `backend|both` 且至少一个选中后端故事以 API 触发。用户显式要求刷新依赖/API 产物时,视为授权原子更新本 skill 在本次运行创建的文件;覆盖既有 complete 产物、非本 skill 创建的文件或来源无法确认的文件前先确认。
15
25
 
16
26
  ## Workflow
17
27
 
18
28
  - [ ] Step 0:输入门禁 ⛔ BLOCKING
19
- - [ ] 读取 `references/input-contract.md` 并运行 Product Requirement 输入校验器。
20
- - [ ] 将 `target=<value>` 和 `--target <value>` 归一化为唯一 target;缺省时继承上游 scope,非法值或冲突的多个值必须停止。
21
- - [ ] Product Requirement 继承 `requirement_id`、`project_root` 和输出目录;显式 target 必须是上游 scope 的子集,只校验和分析选中故事。
29
+ - [ ] `target=<value>` `--target <value>` 归一化为唯一 target;缺省时继承上游 scope,非法值或冲突的多个值必须停止。显式 target 必须是上游 `analysis_scope` 的子集,只分析选中故事,不改写上游产物。
30
+ - [ ] 将 `mode=<value>` 和 `--mode <value>` 归一化为唯一 mode;缺省时为 `full`,非法值或冲突值必须停止。`api-only + frontend` 或没有 API 型后端故事时必须停止,不得生成空 API 文档。
31
+ - [ ] 读取 `references/input-contract.md`,向输入校验器传入已归一化 target。输入必须是上游交付的 complete 产物(不得依赖 `--allow-pending`);V2 + API 触发故事必须先升级为 V4。
32
+ - [ ] 从 Product Requirement 继承 `requirement_id` 和输出目录;项目根由固定目录结构推导。`requirement-missing` 且无法从用户显式 `requirement_id`/输出路径恢复时:`full` 不写产物,只在会话报告;能恢复 ID 时才写 blocked Dependency。
22
33
  - [ ] 在代码侦察前明确回显“分析范围:frontend | backend | both”;新增产物的 `analysis_scope` 必须等于该归一化 target,后续不得自动扩大范围。
23
- - [ ] 门禁失败时只生成 blocked Dependency Analysis,不伪造 API 或代码落点。
34
+ - [ ] `full` 门禁失败且可确定输出位置时只生成 blocked Dependency Analysis,不伪造 API 或代码落点;`api-only` 门禁失败时不写任何产物,只报告阻断原因和恢复条件。
24
35
  - [ ] Step 1:完整代码侦察 ⚠️ REQUIRED
25
- - [ ] 读取 `references/scouting-rules.md`。
26
- - [ ] 逐个读取选中故事及其同 ID 输出规范和 AC,再定位入口、调用链、状态、类型、数据、权限、错误、日志和测试;`frontend` 不分析 `BE-US-*`,`backend` 不分析 `FE-US-*`。
27
- - [ ] 区分 confirmed、inferred、unknown,并为每项结论提供证据。
36
+ - [ ] 从选中故事、同 ID 输出规范和 AC 提取业务词、领域对象、状态/边界、权限、接口形态与技术约定,形成仅覆盖当前分析范围的知识库检索计划;不得先探索代码库再反推检索词。
37
+ - [ ] 首先读取 `references/kb-integration.md` `references/scouting-rules.md`,并按前者的状态机、用户确认门禁和降级边界真实调用 `kb-design-assist`。每次分析都必须执行,不得因故事简单或未显式提到项目规范而跳过。
38
+ - [ ] 知识库状态为 `executed-hit` 或 `executed-no-match`,或 `unavailable` 后用户明确确认跳过,才允许预扫描项目资料并探索代码库;其余情况保持阻断。
39
+ - [ ] 知识库门禁通过后、常规代码侦察前,依次预扫描 `<project-root>/ai_workspace/project-how-to`、`code-specification`、`project-business`:先枚举文件,再读取入口文档及与选中故事直接相关的内容;缺失目录记录 `not-found` 并继续。
40
+ - [ ] 按 scouting rules 的只读代码侦察 agent 委派、confirmed/inferred/unknown 分级、双证据要求与前后端必查项执行;当前宿主不支持隔离 agent 时由当前 agent 执行同等范围的定向搜索。当前会话只保留精简规范证据、代码证据、冲突和未定位项。知识库证据不得替代真实代码落点。
41
+ - [ ] `full` 逐个读取选中故事及其同 ID 输出规范和 AC,再定位入口、调用链、状态、类型、数据、权限、错误、日志和测试;`frontend` 不分析 `BE-US-*`,`backend` 不分析 `FE-US-*`。`api-only` 只侦察生成 HTTP 契约所需的路由惯例、共享 DTO/Schema、统一响应/错误、分页、时间与标识符规范,不做影响文件清单和非 API 依赖分析。
42
+ - [ ] unknown 不在本 skill 内发起产品澄清:目标行为已明确但代码落点/复用点未知时记入对应故事风险或跨故事未定位项;缺失 API 业务语义、权限、核心规则、安全边界或验收所需产品决策时阻断并返回上游需求 skill,不得自行补齐。
28
43
  - [ ] Step 2:判断 API 适用性 ⚠️ REQUIRED
29
- - [ ] 只要一个选中的 `BE-US-*` 触发方式为 API,就必须生成 API 文档。
30
- - [ ] target `frontend` 时不读取或分析后端故事,不生成 API 文档或 API 实现映射。
44
+ - [ ] 只要一个选中的 `BE-US-*` 触发方式为 `API(Web)`、`API(Remote)` 或 `API(Web + Remote)`,就必须生成 API 文档。Web 与 Remote 使用同一 HTTP 契约;Web + Remote 必须建模为两个独立接口,路径以知识库、项目资料与仓库规范为准。
45
+ - [ ] 笼统的 `触发方式:API` 视为接口范围未确认,阻断并返回上游需求 skill。
46
+ - [ ] target 为 `frontend` 时不读取或分析后端故事,也不新生成 API 文档;Dependency 仍保留 API 实现映射章节。传入外部 API 文档时,只登记被选中前端故事实际依赖且可在文档中核验的 Method+Path,不记录 API ID、Operation ID、文档路径或完整契约;外部文档中的无关接口不参与孤立检查。未传入或未定位时写带原因的不适用。
31
47
  - [ ] 定时任务、事件、消息、数据迁移或纯内部调用且不形成 HTTP 契约时,不生成空 API 文档。
32
- - [ ] 无 API 时 Dependency Analysis 使用 `source_api_documentation: none`,API 实现映射明确写不适用。
33
- - [ ] Step 3:建立 Canonical API Model ⚠️ REQUIRED
34
- - [ ] API 场景读取 `references/api-documentation-schema.md`。
35
- - [ ] 结合 Product Requirement 的业务契约与仓库现有 API 规范,确定方法、路径、参数、响应、错误、Schema、分页、示例和代码落点。API 文档不输出认证方式或权限要求。
36
- - [ ] 定义接口字段前,先搜索共享 DTO/SchemaOpenAPI components、统一响应和分页模型;命中时直接复用,不重复定义,不在 API 文档中输出搜索过程或“复用检查”。
37
- - [ ] 定义返回 code 前,先搜索全局错误枚举、code 映射和错误响应外壳;命中时直接复用,仅未命中时才定义局部 code,不在 API 文档中输出搜索过程或“复用检查”。
38
- - [ ] 分页接口将每页条数参数定义为可选,可选值必须完整包含 `10`、`20`、`50`、`100`;默认值仅在 Product Requirement 或仓库通用分页定义明确时写入。
48
+ - [ ] 无 API 时 API 实现映射明确写不适用。
49
+ - [ ] Step 3:建立规范 API 中间模型(仅 API 场景)⚠️ REQUIRED when applicable
50
+ - [ ] 仅当选中范围内存在 API 型后端故事,或 `mode=api-only` 时执行本步;`frontend`、非 API 场景或已判定不适用时跳过,不建立中间模型。
51
+ - [ ] 读取 `references/api-documentation-schema.md`,按契约建模与渲染。
52
+ - [ ] 用同一次代码侦察建立不落盘的规范中间模型(最小字段:API IDOperation ID、Method+Path、变更类型、请求/响应字段、错误码、分页是否适用);`full` API Documentation 与 Dependency Analysis 的 API 映射均从该模型渲染;`api-only` 只渲染 API Documentation。
53
+ - [ ] 所有成功与失败响应的 HTTP 状态码统一为 `200`,只通过响应体顶层 `code` 区分结果;成功与失败 `code` 必须不同。定义字段/错误码前,先通过 `kb-design-assist` 检索当前有效的 DTO/Response、统一响应和错误码规范,再核验仓库共享 DTO、响应类型和全局错误枚举;命中则复用,不在 API 文档输出搜索过程、知识库证据或“复用检查”。
54
+ - [ ] 分页允许值以 Product Requirement 为准;上游确实未说明时按“有效知识库分页规范 > 仓库通用分页定义 > 默认集合 `10`、`20`、`50`、`100`”补齐。参数必填性与默认值沿用 Product Requirement;PR 未规定时优先采用有效知识库规范并由仓库定义核验。
39
55
  - [ ] 产品需求优先于现状;业务语义缺失时阻断,不由本 skill 发明产品决策。
40
- - [ ] API 文档只保留 HTTP 契约、字段、响应、错误与分页参数;完全移除认证方式、权限要求、业务规则、处理流程、分支逻辑、数据读写逻辑和实现算法及其相关内容。
41
- - [ ] API 文档与依赖分析必须从同一模型渲染。
56
+ - [ ] API 文档只保留 HTTP 契约;不得包含故事/AC 追溯、认证、权限、业务规则与实现逻辑。只有实际分页时才生成“分页约定”。
42
57
  - [ ] Step 4:生成产物 ⚠️ REQUIRED
43
- - [ ] 读取 `references/dependency-analysis-schema.md`。
44
- - [ ] API 场景先写 Swagger 风格 Markdown `api-documentation.md`,再写 `dependency-analysis.md`。
45
- - [ ] `frontend` 产物只包含前端故事覆盖和前端依赖详情;`backend` 只包含后端故事覆盖、后端依赖详情和适用的 API 映射;`both` 才包含两端。
46
- - [ ] `影响文件` 是每个故事的完整权威文件清单,使用 `F1`、`F2` 编号和 add/modify/reuse;其他落点字段引用这些编号,不重复完整路径。
47
- - [ ] Dependency API 实现映射只保留 API ID、Operation ID、方法路径和代码入口,不复制故事、AC 或完整接口文档。
48
- - [ ] 所有新增产物与 Product Requirement 使用相同 `requirement_id`,同目录引用使用 `./文件名`。
58
+ - [ ] `full` 读取 `references/dependency-analysis-schema.md`;`api-only` 不读取或生成 Dependency Analysis。
59
+ - [ ] `full` 的 API 场景先写 `api-documentation.md`,再写 `dependency-analysis.md`;`api-only` 只写 `api-documentation.md`,不得创建、覆盖或追加 `dependency-analysis.md`。
60
+ - [ ] target 生成对应故事覆盖与依赖详情;三种范围都保留 API 实现映射章节。
61
+ - [ ] `影响文件` 是每个故事独立完整的权威清单(故事内 `F1`/`F2` + `add|modify|reuse` 或中文同义 `新增|修改|复用`);不得建立全局文件索引。
62
+ - [ ] 前端详情必须覆盖 Product Requirement 的目标页面路由。传入外部 API 文档且依赖 HTTP 时只写 Method+Path;否则按 schema 写“`不适用;…`”模板。
63
+ - [ ] 故事局部风险与文末跨故事风险不得重复;API 实现映射只保留 API ID、Operation ID、方法路径和代码入口(现有路径或拟新增路径,禁止「未定位」占位)。
64
+ - [ ] 所有新增产物与 Product Requirement 使用相同 `requirement_id`;V4 frontmatter 不记录可由目录或运行参数推导的来源路径。
49
65
  - [ ] Step 5:验证并交付 ⛔ BLOCKING
50
66
  - [ ] Product Requirement 输入校验必须通过。
51
- - [ ] Product Requirement、Dependency 和适用的 API 校验器传入归一化 target;API 场景运行 API 和 Dependency 两个校验器,非 API 场景只运行 Dependency 校验器。
52
- - [ ] 覆盖矩阵承担全局追溯,不生成重复的文末追溯汇总。
53
- - [ ] 运行 validator matrix,修复全部错误后再声明完成。
67
+ - [ ] 向所有适用校验器传入归一化 target。`full` API 场景运行输入、API 和 Dependency 三个校验器;非 API 场景运行输入和 Dependency;`api-only` 只运行输入和 API。
68
+ - [ ] 当前产物校验通过即可交付。`test-validators.mjs` 仅在修改输出契约、schema、validator、artifact version、示例、forward-test contract,或发布/安装/回归验证时运行。
69
+
70
+ 完整示例按需读取 `references/example.md`;维护或 forward-test 时读取 `references/forward-test-cases.md`。不要为了执行校验而阅读脚本,直接运行。
71
+
72
+ ## Anti-Patterns
54
73
 
55
- 完整示例按需读取 `references/example.md`;维护或 forward-test 时读取 `references/forward-test-cases.md`。
74
+ - 从聊天、Product Analysis Clarification 重新解释需求。
75
+ - 修改代码或上游 `product-requirement.md`。
76
+ - 消费带 `--allow-pending` 的未交付产物,或对 V2 + API 故事直接生成 API 文档。
77
+ - 在 API 文档写入故事/AC、认证、权限、业务规则或“复用检查”。
78
+ - 在 API 文档写入 `kb-design-assist` 调用过程、`KB-EVIDENCE-*`、知识库路径、命中日志或规范复用证据。
79
+ - Product Requirement 已给出分页允许值时,用知识库规范、默认 `10/20/50/100` 或仓库惯例覆盖。
80
+ - 建立全局文件索引,或把完整 API 契约/JSON 示例复制进 Dependency Analysis。
81
+ - `api-only` 模式下创建或修改 `dependency-analysis.md`。
82
+ - 自行补齐缺失的产品决策而不返回上游需求 skill。
83
+ - `kb-design-assist` 为 `unavailable` 时,未让用户选择修复或跳过就继续代码库降级或生成 complete 产物。
84
+ - 跳过需求驱动的知识库检索、先探索代码库,或用“故事不涉及项目规范”记录未执行状态。
85
+
86
+ ## 交付前自检
87
+
88
+ 声明完成前逐项确认,任一不满足不得交付:
89
+
90
+ - [ ] 新增 V4 产物与 Product Requirement 同目录、同 `requirement_id`,且 frontmatter 不含可推导的来源、项目根或仓库路径。
91
+ - [ ] 所有产物 `analysis_scope` 等于归一化 target,未自动扩大范围。
92
+ - [ ] 按场景运行对应校验器:`full` API 场景跑「输入 + API + Dependency」,`full` 非 API 场景跑「输入 + Dependency」,`api-only` 只跑「输入 + API」。
93
+ - [ ] 所有适用校验命令返回 exit 0;任一非 0 都不算完成。
56
94
 
57
95
  ## Validation
58
96
 
97
+ 把 `<skill-root>` 解析为本 `SKILL.md` 所在目录,全部参数使用绝对路径:
98
+
59
99
  ```bash
60
100
  node <skill-root>/scripts/validate-product-requirement-input.mjs <product-requirement.md> --target <frontend|backend|both>
61
101
  node <skill-root>/scripts/validate-api-documentation.mjs <product-requirement.md> <api-documentation.md> --target <backend|both>
62
- node <skill-root>/scripts/validate-dependency-analysis.mjs <product-requirement.md> <dependency-analysis.md> [api-documentation.md] --target <frontend|backend|both>
63
- node <skill-root>/scripts/test-validators.mjs
102
+ node <skill-root>/scripts/validate-dependency-analysis.mjs <product-requirement.md> <dependency-analysis.md> [api-documentation.md] [--api-doc <external-api.md>] --target <frontend|backend|both>
64
103
  ```
65
104
 
105
+ `api-only` 只运行前两条适用命令;不得调用 Dependency 校验器。
66
106
  非 API 场景省略 API 校验器和 Dependency 校验命令的第三个参数。
67
107
  缺少 Product Requirement 的 blocked 场景使用 `none` 作为 Dependency 校验命令的第一个参数。
108
+ 维护或发布时额外运行 `node <skill-root>/scripts/test-validators.mjs`。