@tea-agent/loop-agent 0.23.1 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -1
- package/README.md +1 -1
- package/bin/agent-worker.js +0 -0
- package/dist/commands/init.js +2 -2
- package/dist/executors/shell-executor.js +20 -7
- package/dist/shared/operator/capabilities.js +475 -2
- package/dist/task/dag-source-paths.js +50 -0
- package/dist/task/frontend-project-capability.js +316 -0
- package/dist/task/task-demand-routing.js +432 -0
- package/dist/worker/console/app-data.js +2 -0
- package/dist/worker/console/chat/artifact-card.js +23 -0
- package/dist/worker/console/chat/chat-event-store.js +495 -0
- package/dist/worker/console/chat/chat-ui-policy.js +25 -0
- package/dist/worker/console/chat/compaction-errors.js +64 -0
- package/dist/worker/console/chat/composer-draft-store.js +45 -0
- package/dist/worker/console/chat/context-panel.js +54 -0
- package/dist/worker/console/chat/contract-apply-receipt-store.js +174 -0
- package/dist/worker/console/chat/explore-tools.js +299 -0
- package/dist/worker/console/chat/human-gate-card.js +37 -0
- package/dist/worker/console/chat/interview-adapter.js +149 -0
- package/dist/worker/console/chat/operation-card.js +23 -0
- package/dist/worker/console/chat/pi-console-config.js +158 -0
- package/dist/worker/console/chat/pi-runtime.js +581 -43
- package/dist/worker/console/chat/repo-browser.js +140 -0
- package/dist/worker/console/chat/repo-walk.js +116 -0
- package/dist/worker/console/chat/resource-loader.js +18 -17
- package/dist/worker/console/chat/routes.js +1359 -65
- package/dist/worker/console/chat/runtime-context.js +24 -0
- package/dist/worker/console/chat/runtime-selection.js +37 -0
- package/dist/worker/console/chat/session-store.js +210 -11
- package/dist/worker/console/chat/shortcuts.js +15 -0
- package/dist/worker/console/chat/tool-adapter.js +81 -194
- package/dist/worker/console/chat/tools.js +72 -48
- package/dist/worker/console/chat/usage.js +37 -0
- package/dist/worker/console/chat/workspace-landing.js +56 -0
- package/dist/worker/console/dag-confirmation.js +42 -8
- package/dist/worker/console/human-gate-token.js +130 -0
- package/dist/worker/console/interview/grill-me.js +28 -5
- package/dist/worker/console/mutation-gate-receipt-store.js +184 -0
- package/dist/worker/console/operation-runner.js +6 -2
- package/dist/worker/console/operation-sse.js +26 -0
- package/dist/worker/console/operator-actions.js +420 -7
- package/dist/worker/console/server.js +14 -2
- package/dist/worker/console/static/assets/index-Cv68Ge5n.js +27 -0
- package/dist/worker/console/static/assets/index-Dp_2sKGk.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/observe/routes.js +8 -4
- package/dist/workflows/dag/backend-test-intake-context.js +10 -12
- package/dist/workflows/dag/backend-test-markdown-workflow.js +232 -47
- package/dist/workflows/dag/backend-test-result-contract.js +233 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +23 -17
- package/dist/workflows/dag/frontend-lint-baseline.js +4 -4
- package/dist/workflows/dag/frontend-project-capability.js +1 -316
- package/dist/workflows/dag/frontend-worktree-diff.js +4 -1
- package/dist/workflows/dag/init-hybrid.js +46 -43
- package/dist/workflows/dag/rerun-plan.js +6 -10
- package/dist/workflows/dag/task-demand-routing.js +1 -383
- package/docs/README.md +1 -1
- package/docs/architecture/README.md +5 -5
- package/docs/architecture/evolution.md +4 -4
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/templates/backend-test-dag.json +2 -2
- package/docs/templates/frontend-implementation-contract.schema.json +1 -1
- package/harness.json +1 -1
- package/package.json +1 -1
- package/skills/frontend-implementation/SKILL.md +7 -0
- package/skills/frontend-implementation/references/node-contracts.md +1 -1
- package/skills/frontend-verification/SKILL.md +4 -3
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/index-DVl7Jxt5.js +0 -25
- package/dist/worker/console/static/assets/index-lVcIr9Ju.css +0 -1
|
@@ -1,383 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const FRONTEND_TASK_TYPE = /^\s*(?:[-*]\s*)?task\s+type\s*:\s*frontend-feature\s*$/im;
|
|
3
|
-
const BACKEND_TASK_TYPE = /^\s*(?:[-*]\s*)?task\s+type\s*:\s*backend-feature\s*$/im;
|
|
4
|
-
const FRONTEND_NEGATION_PATTERNS = [
|
|
5
|
-
/(?:不涉及|无需|不需要|不包含|不修改|不实现|不交付|不要|禁止|不得|排除).{0,16}(?:前端(?:页面|组件|界面|功能|交互)?|页面|组件|界面|用户界面|浏览器交互)/,
|
|
6
|
-
/(?:前端(?:页面|组件|界面|功能|交互)?|页面|组件|界面|用户界面).{0,12}(?:不在(?:本次)?范围|无需|不需要|不修改|不实现|不交付|不要|禁止|不得)/,
|
|
7
|
-
/(?:不涉及|无需|不需要|不包含|不修改|不实现|不交付|不要|禁止|不得|排除).{0,16}(?<![A-Za-z0-9_])UI(?![A-Za-z0-9_])/i,
|
|
8
|
-
/(?<![A-Za-z0-9_])UI(?![A-Za-z0-9_]).{0,12}(?:不在(?:本次)?范围|无需|不需要|不修改|不实现|不交付|不要|禁止|不得)/i,
|
|
9
|
-
/\b(?:backend|server[-\s]?side)\s+only\b/i,
|
|
10
|
-
/\b(?:no|without)\s+(?:frontend|front[-\s]?end|ui|user\s+interface)\b/i,
|
|
11
|
-
/\b(?:do\s+not|don't|must\s+not|should\s+not)\s+(?:(?:add|implement|build|create|update|modify|fix|refactor|render|style|change|touch)\s+)?(?:(?:the|any|a|an)\s+)?(?:frontend|front[-\s]?end|ui|user\s+interface|page|component|form|modal|dialog)\b/i,
|
|
12
|
-
/\bwithout\s+(?:(?:adding|implementing|building|creating|updating|modifying|fixing|refactoring|rendering|styling|changing|touching)\s+)?(?:(?:the|any|a|an)\s+)?(?:frontend|front[-\s]?end|ui|user\s+interface|page|component|form|modal|dialog)\b/i,
|
|
13
|
-
/\b(?:frontend|front[-\s]?end|ui|user\s+interface|page|component|form|modal|dialog)\b[^.?!;\n]{0,20}\b(?:is|are)?\s*(?:out\s+of\s+scope|excluded|not\s+(?:required|needed)|unchanged)\b/i,
|
|
14
|
-
];
|
|
15
|
-
const FRONTEND_DELIVERY_PATTERNS = [
|
|
16
|
-
/(?:新增|实现|开发|修改|重构|修复|优化|创建|渲染|搭建|改造)[^。!?;,,\n]{0,24}(?:前端(?:页面|组件|界面|功能|交互)|页面|组件|界面|表单|弹窗|对话框|用户界面|浏览器(?:页面|交互|行为))/,
|
|
17
|
-
/(?:前端(?:页面|组件|界面|功能|交互)|页面|组件|界面|表单|弹窗|对话框|用户界面|浏览器交互)[^。!?;,,\n]{0,16}(?:新增|实现|开发|修改|重构|修复|优化|创建|渲染|搭建|改造)/,
|
|
18
|
-
/\b(?:add|implement|build|create|update|modify|fix|refactor|render|style)\b[^.?!;\n]{0,48}\b(?:front[-\s]?end\s+(?:page|component|view|form|modal|interaction|ui)|ui|form|modal|dialog|dashboard|navigation|toast|button|dropdown|user\s+interface|browser\s+interaction)\b/i,
|
|
19
|
-
];
|
|
20
|
-
const PATH_SUPPORTED_FRONTEND_DELIVERY_PATTERNS = [
|
|
21
|
-
/(?:新增|实现|开发|修改|重构|修复|优化|创建|渲染|搭建|改造)[^。!?;,,\n]{0,24}(?:页(?!\p{Script=Han})|表格|列表|按钮|导航|菜单|标签页|下拉框|选择器|提示|通知|加载状态|空状态|错误状态|交互|样式|布局)/u,
|
|
22
|
-
/(?:新增|实现|开发|修改|重构|修复|优化|创建|渲染|搭建|改造)[^。!?;,,\n]{0,24}(?<![A-Za-z0-9_])(?:DOM|UI|React|Vue|Svelte|Angular)(?![A-Za-z0-9_])/i,
|
|
23
|
-
/\b(?:add|implement|build|create|update|modify|fix|refactor|render|style)\b[^.?!;\n]{0,48}\b(?:page|component|screen|layout|dom|react|vue|svelte|angular)\b/i,
|
|
24
|
-
];
|
|
25
|
-
const BACKEND_DELIVERY_PATTERNS = [
|
|
26
|
-
/(?:新增|实现|开发|修改|重构|修复|提供|创建|调整|优化|迁移|扩展)[^。!?;,,\n]{0,24}(?:后端(?:服务|接口|逻辑|API)?|服务端(?:服务|接口|逻辑)?|接口|数据库|数据表(?!格)|数据库索引|索引|控制器|服务层|仓储层)/i,
|
|
27
|
-
/(?:后端(?:服务|接口|逻辑|API)?|服务端(?:服务|接口|逻辑)?|接口|数据库|数据表(?!格)|数据库索引|控制器|服务层|仓储层)[^。!?;,,\n]{0,16}(?:新增|实现|开发|修改|重构|修复|提供|创建|调整|优化|迁移|扩展)/i,
|
|
28
|
-
/(?:新增|实现|开发|修改|重构|修复|提供|创建|调整|优化|迁移|扩展)[^。!?;,,\n]{0,24}(?<![A-Za-z0-9_])API(?![A-Za-z0-9_])/i,
|
|
29
|
-
/(?<![A-Za-z0-9_])API(?![A-Za-z0-9_])[^。!?;,,\n]{0,16}(?:新增|实现|开发|修改|重构|修复|提供|创建|调整|优化|迁移|扩展)/i,
|
|
30
|
-
/\b(?:add|implement|build|create|update|modify|fix|refactor|migrate|extend)\b[^.?!;\n]{0,48}\b(?:backend|server[-\s]?side|api|endpoint|database|migration|controller|service|repository)\b/i,
|
|
31
|
-
];
|
|
32
|
-
const BACKEND_NEGATION_PATTERNS = [
|
|
33
|
-
/(?:不涉及|无需|不需要|不包含|不修改|不实现|不交付|不要|禁止|不得|排除).{0,16}(?:后端|服务端|接口|数据库|数据表(?!格)|索引)/,
|
|
34
|
-
/(?:不涉及|无需|不需要|不包含|不修改|不实现|不交付|不要|禁止|不得|排除).{0,16}(?<![A-Za-z0-9_])API(?![A-Za-z0-9_])/i,
|
|
35
|
-
/\b(?:no|without)\s+(?:backend|server[-\s]?side|api|endpoint|database)\b/i,
|
|
36
|
-
/\b(?:do\s+not|don't|must\s+not|should\s+not)\s+(?:(?:add|implement|build|create|update|modify|fix|refactor|migrate|change|touch)\s+)?(?:(?:the|any|a|an)\s+)?(?:backend|server[-\s]?side|api|endpoint|database)\b/i,
|
|
37
|
-
];
|
|
38
|
-
const FRONTEND_PATH_PATTERNS = [
|
|
39
|
-
/(?:^|\/)apps\/web\/src(?:\/|$)/i,
|
|
40
|
-
/(?:^|\/)(?:frontend|client)(?:\/|$)/i,
|
|
41
|
-
/(?:^|\/)src\/(?:pages?|components?|views?)(?:\/|$)/i,
|
|
42
|
-
/\.(?:tsx|jsx|vue|svelte|css|scss|less)(?:$|[*?])/i,
|
|
43
|
-
];
|
|
44
|
-
const BACKEND_PATH_PATTERNS = [
|
|
45
|
-
/(?:^|\/)(?:backend|server|api|controllers?|services?|repositories?|db|database|migrations?)(?:\/|$)/i,
|
|
46
|
-
/\.(?:sql)(?:$|[*?])/i,
|
|
47
|
-
];
|
|
48
|
-
const NON_PRODUCT_PATH_SEGMENT = /(?:^|\/)(?:docs?|skills?|tests?|__tests__|e2e|cypress)(?:\/|$)/i;
|
|
49
|
-
const FRONTEND_PATH_EXCLUSIONS = NON_PRODUCT_PATH_SEGMENT;
|
|
50
|
-
const DOCUMENTATION_ONLY_MARKERS = /(?:文档|说明|指南|规范|测试用例|测试计划|评审|审查|\b(?:docs?|documentation|guide|specification|test\s+cases?|test\s+plan|review)\b)/i;
|
|
51
|
-
const DOCUMENTATION_ONLY_ACTIONS = /(?:新增|实现|开发|创建|编写|更新|修改|重构|修复|优化|渲染|搭建|改造|补充|生成|评审|审查|\b(?:add|implement|build|create|write|update|modify|refactor|fix|optimize|render|style|document|review|generate)\b)/i;
|
|
52
|
-
const PRODUCT_AND_DOCUMENT_DELIVERY = /(?:新增|实现|开发|修改|重构|修复|优化|创建|渲染|搭建|改造)[^。!?;,,\n]{0,24}(?:前端|页面|组件|界面|表单|交互|样式|布局|DOM|UI|React|Vue|Svelte|Angular|源码|代码)[^。!?;,,\n]{0,16}(?:并|同时|以及|和|与|\band\b)[^。!?;,,\n]{0,16}(?:文档|说明|指南|规范|\b(?:docs?|documentation|guide)\b)/i;
|
|
53
|
-
const TEST_ONLY_MARKERS = /(?:测试|用例|\b(?:e2e|unit|integration|component)\s+tests?\b|\btests?\b)/i;
|
|
54
|
-
const PRODUCT_AND_TEST_DELIVERY = /(?:并|同时|以及|和|与|\band\b).{0,12}(?:添加|新增|编写|补充|测试|\b(?:add|write|test)\b)/i;
|
|
55
|
-
const NON_PRODUCT_ALLOWED_PATH_PATTERNS = [
|
|
56
|
-
NON_PRODUCT_PATH_SEGMENT,
|
|
57
|
-
/\.(?:test|spec)\.[^/]+$/i,
|
|
58
|
-
/\.cy\.[^/]+$/i,
|
|
59
|
-
/\.mdx?(?:$|[*?])/i,
|
|
60
|
-
];
|
|
61
|
-
const SPECIAL_TEMPLATE_BY_TASK_KIND = {
|
|
62
|
-
"frontend-implementation": "frontend-implementation",
|
|
63
|
-
"frontend-test": "frontend-test-dag",
|
|
64
|
-
"backend-test": "backend-test-dag",
|
|
65
|
-
"knowledge-sync": "knowledge-sync-dag",
|
|
66
|
-
"knowledge-graph-bootstrap": "knowledge-graph-bootstrap-dag",
|
|
67
|
-
};
|
|
68
|
-
const TASK_KIND_ROUTING_REASON = {
|
|
69
|
-
"frontend-implementation": 'taskKind "frontend-implementation" selects the dedicated frontend DAG template',
|
|
70
|
-
"frontend-test": 'taskKind "frontend-test" selects the dedicated frontend test DAG template',
|
|
71
|
-
"backend-test": 'taskKind "backend-test" selects the dedicated backend test DAG template',
|
|
72
|
-
"knowledge-sync": 'taskKind "knowledge-sync" selects the dedicated knowledge-sync DAG template',
|
|
73
|
-
"knowledge-graph-bootstrap": 'taskKind "knowledge-graph-bootstrap" selects the dedicated knowledge-graph bootstrap DAG template',
|
|
74
|
-
};
|
|
75
|
-
const SPECIAL_REQUESTED_TEMPLATES = new Set([
|
|
76
|
-
"frontend-implementation",
|
|
77
|
-
"frontend-test-dag",
|
|
78
|
-
"backend-test-dag",
|
|
79
|
-
"knowledge-sync-dag",
|
|
80
|
-
"knowledge-graph-bootstrap-dag",
|
|
81
|
-
]);
|
|
82
|
-
function stripOutOfScopeSections(markdown) {
|
|
83
|
-
const output = [];
|
|
84
|
-
let skippedHeadingLevel = null;
|
|
85
|
-
for (const line of markdown.split(/\r?\n/)) {
|
|
86
|
-
const heading = /^(#{1,6})\s+(.+?)\s*$/.exec(line.trim());
|
|
87
|
-
if (heading) {
|
|
88
|
-
const level = heading[1].length;
|
|
89
|
-
if (skippedHeadingLevel !== null && level <= skippedHeadingLevel) {
|
|
90
|
-
skippedHeadingLevel = null;
|
|
91
|
-
}
|
|
92
|
-
if (OUT_OF_SCOPE_HEADING.test(heading[2].trim())) {
|
|
93
|
-
skippedHeadingLevel = level;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (skippedHeadingLevel === null)
|
|
98
|
-
output.push(line);
|
|
99
|
-
}
|
|
100
|
-
return output.join("\n");
|
|
101
|
-
}
|
|
102
|
-
function stripFencedCodeBlocks(markdown) {
|
|
103
|
-
const output = [];
|
|
104
|
-
let fenceCharacter = null;
|
|
105
|
-
for (const line of markdown.split(/\r?\n/)) {
|
|
106
|
-
const fence = /^\s*(`{3,}|~{3,})/.exec(line);
|
|
107
|
-
if (fence) {
|
|
108
|
-
const character = fence[1][0];
|
|
109
|
-
if (fenceCharacter === null)
|
|
110
|
-
fenceCharacter = character;
|
|
111
|
-
else if (fenceCharacter === character)
|
|
112
|
-
fenceCharacter = null;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if (fenceCharacter === null)
|
|
116
|
-
output.push(line);
|
|
117
|
-
}
|
|
118
|
-
return output.join("\n");
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Normalize task prose before deterministic demand/capability signal checks.
|
|
122
|
-
* Examples and explicit non-goals must not become positive routing evidence.
|
|
123
|
-
*/
|
|
124
|
-
export function normalizeTaskRequirementText(markdown) {
|
|
125
|
-
return stripOutOfScopeSections(stripFencedCodeBlocks(markdown));
|
|
126
|
-
}
|
|
127
|
-
function splitClauses(text) {
|
|
128
|
-
return text
|
|
129
|
-
.split(/[。!?!?;;,,\r\n]+/)
|
|
130
|
-
.map((clause) => clause.trim())
|
|
131
|
-
.filter(Boolean);
|
|
132
|
-
}
|
|
133
|
-
function matchesAny(value, patterns) {
|
|
134
|
-
return patterns.some((pattern) => pattern.test(value));
|
|
135
|
-
}
|
|
136
|
-
function isFrontendNegated(clause) {
|
|
137
|
-
return matchesAny(clause, FRONTEND_NEGATION_PATTERNS);
|
|
138
|
-
}
|
|
139
|
-
function isDocumentationOnlyClause(clause) {
|
|
140
|
-
if (!DOCUMENTATION_ONLY_ACTIONS.test(clause))
|
|
141
|
-
return false;
|
|
142
|
-
if (DOCUMENTATION_ONLY_MARKERS.test(clause)) {
|
|
143
|
-
return !PRODUCT_AND_DOCUMENT_DELIVERY.test(clause);
|
|
144
|
-
}
|
|
145
|
-
return TEST_ONLY_MARKERS.test(clause) && !PRODUCT_AND_TEST_DELIVERY.test(clause);
|
|
146
|
-
}
|
|
147
|
-
function masksExistingBackendDependency(clause) {
|
|
148
|
-
return clause
|
|
149
|
-
.replace(/(?:调用|使用|消费|对接|依赖)(?:现有|已有)?[^。!?;,,\n]{0,12}(?:后端|服务端|API|接口)/gi, " ")
|
|
150
|
-
.replace(/\b(?:call|consume|use|integrate\s+with|depend\s+on)\b[^.?!;\n]{0,24}\b(?:existing|current)?\s*(?:backend|server[-\s]?side|api|endpoint)\b/gi, " ");
|
|
151
|
-
}
|
|
152
|
-
function addSignal(signals, signal) {
|
|
153
|
-
if (signals.some((current) => current.id === signal.id))
|
|
154
|
-
return;
|
|
155
|
-
signals.push(signal);
|
|
156
|
-
}
|
|
157
|
-
function collectTextSignals(input) {
|
|
158
|
-
let frontendDelivery = false;
|
|
159
|
-
let pathSupportedFrontendDelivery = false;
|
|
160
|
-
let backendDelivery = false;
|
|
161
|
-
let frontendNegated = false;
|
|
162
|
-
for (const clause of splitClauses(input.text)) {
|
|
163
|
-
const negated = isFrontendNegated(clause);
|
|
164
|
-
frontendNegated ||= negated;
|
|
165
|
-
const documentationOnly = isDocumentationOnlyClause(clause);
|
|
166
|
-
if (!negated &&
|
|
167
|
-
!documentationOnly &&
|
|
168
|
-
matchesAny(clause, FRONTEND_DELIVERY_PATTERNS)) {
|
|
169
|
-
frontendDelivery = true;
|
|
170
|
-
addSignal(input.signals, {
|
|
171
|
-
id: `frontend-delivery-${input.source}`,
|
|
172
|
-
source: input.source,
|
|
173
|
-
message: `${input.source} contains an implementation action and a frontend UI deliverable`,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
if (!negated &&
|
|
177
|
-
!documentationOnly &&
|
|
178
|
-
matchesAny(clause, PATH_SUPPORTED_FRONTEND_DELIVERY_PATTERNS)) {
|
|
179
|
-
pathSupportedFrontendDelivery = true;
|
|
180
|
-
}
|
|
181
|
-
const backendClause = masksExistingBackendDependency(clause);
|
|
182
|
-
if (!matchesAny(clause, BACKEND_NEGATION_PATTERNS) &&
|
|
183
|
-
matchesAny(backendClause, BACKEND_DELIVERY_PATTERNS)) {
|
|
184
|
-
backendDelivery = true;
|
|
185
|
-
addSignal(input.signals, {
|
|
186
|
-
id: `backend-delivery-${input.source}`,
|
|
187
|
-
source: input.source,
|
|
188
|
-
message: `${input.source} contains an implementation action and a backend deliverable`,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return {
|
|
193
|
-
frontendDelivery,
|
|
194
|
-
pathSupportedFrontendDelivery,
|
|
195
|
-
backendDelivery,
|
|
196
|
-
frontendNegated,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
function normalizedAllowedPath(value) {
|
|
200
|
-
return value.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
201
|
-
}
|
|
202
|
-
function reasonsForClassification(input) {
|
|
203
|
-
const signalIds = input.signals.map((signal) => signal.id);
|
|
204
|
-
const reasons = [`task demand classified as ${input.kind}`];
|
|
205
|
-
if (signalIds.length > 0)
|
|
206
|
-
reasons.push(`signals: ${signalIds.join(", ")}`);
|
|
207
|
-
if (input.blockers.length > 0)
|
|
208
|
-
reasons.push(`blockers: ${input.blockers.join(", ")}`);
|
|
209
|
-
return reasons;
|
|
210
|
-
}
|
|
211
|
-
export function classifyTaskDemand(input) {
|
|
212
|
-
const signals = [];
|
|
213
|
-
const blockers = [];
|
|
214
|
-
const requirement = normalizeTaskRequirementText(input.requirementMarkdown);
|
|
215
|
-
const hasFrontendTaskType = FRONTEND_TASK_TYPE.test(requirement);
|
|
216
|
-
const hasBackendTaskType = BACKEND_TASK_TYPE.test(requirement);
|
|
217
|
-
if (hasFrontendTaskType) {
|
|
218
|
-
addSignal(signals, {
|
|
219
|
-
id: "task-type-frontend-feature",
|
|
220
|
-
source: "requirement",
|
|
221
|
-
message: "requirement contains the materialized frontend-feature task type",
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (hasBackendTaskType) {
|
|
225
|
-
addSignal(signals, {
|
|
226
|
-
id: "task-type-backend-feature",
|
|
227
|
-
source: "requirement",
|
|
228
|
-
message: "requirement contains the materialized backend-feature task type",
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
const titleSignals = collectTextSignals({
|
|
232
|
-
text: input.title,
|
|
233
|
-
source: "title",
|
|
234
|
-
signals,
|
|
235
|
-
});
|
|
236
|
-
const requirementSignals = collectTextSignals({
|
|
237
|
-
text: requirement,
|
|
238
|
-
source: "requirement",
|
|
239
|
-
signals,
|
|
240
|
-
});
|
|
241
|
-
const frontendNegated = titleSignals.frontendNegated || requirementSignals.frontendNegated;
|
|
242
|
-
if (frontendNegated)
|
|
243
|
-
blockers.push("frontend-negated");
|
|
244
|
-
let frontendPath = false;
|
|
245
|
-
let backendPath = false;
|
|
246
|
-
for (const rawPath of input.allowedPaths) {
|
|
247
|
-
const allowedPath = normalizedAllowedPath(rawPath);
|
|
248
|
-
if (!FRONTEND_PATH_EXCLUSIONS.test(allowedPath) &&
|
|
249
|
-
matchesAny(allowedPath, FRONTEND_PATH_PATTERNS)) {
|
|
250
|
-
frontendPath = true;
|
|
251
|
-
}
|
|
252
|
-
if (matchesAny(allowedPath, BACKEND_PATH_PATTERNS))
|
|
253
|
-
backendPath = true;
|
|
254
|
-
}
|
|
255
|
-
if (frontendPath) {
|
|
256
|
-
addSignal(signals, {
|
|
257
|
-
id: "frontend-path",
|
|
258
|
-
source: "allowed-path",
|
|
259
|
-
message: "allowedPaths include a frontend-specific source path",
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (backendPath) {
|
|
263
|
-
addSignal(signals, {
|
|
264
|
-
id: "backend-path",
|
|
265
|
-
source: "allowed-path",
|
|
266
|
-
message: "allowedPaths include a backend-specific source path",
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
const pathSupportedFrontendDelivery = frontendPath &&
|
|
270
|
-
(titleSignals.pathSupportedFrontendDelivery ||
|
|
271
|
-
requirementSignals.pathSupportedFrontendDelivery);
|
|
272
|
-
if (pathSupportedFrontendDelivery) {
|
|
273
|
-
addSignal(signals, {
|
|
274
|
-
id: requirementSignals.pathSupportedFrontendDelivery
|
|
275
|
-
? "frontend-path-supported-delivery-requirement"
|
|
276
|
-
: "frontend-path-supported-delivery-title",
|
|
277
|
-
source: requirementSignals.pathSupportedFrontendDelivery
|
|
278
|
-
? "requirement"
|
|
279
|
-
: "title",
|
|
280
|
-
message: "task text contains a frontend UI artifact and allowedPaths provide frontend source-path evidence",
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
const frontendDelivery = titleSignals.frontendDelivery ||
|
|
284
|
-
requirementSignals.frontendDelivery ||
|
|
285
|
-
pathSupportedFrontendDelivery;
|
|
286
|
-
const allowedPathsOnlyCoverNonProductArtifacts = input.allowedPaths.length > 0 &&
|
|
287
|
-
input.allowedPaths.every((rawPath) => {
|
|
288
|
-
const allowedPath = normalizedAllowedPath(rawPath);
|
|
289
|
-
return matchesAny(allowedPath, NON_PRODUCT_ALLOWED_PATH_PATTERNS);
|
|
290
|
-
});
|
|
291
|
-
if (allowedPathsOnlyCoverNonProductArtifacts && frontendDelivery) {
|
|
292
|
-
blockers.push("non-product-allowed-paths");
|
|
293
|
-
}
|
|
294
|
-
const eligibleFrontendDelivery = frontendDelivery && !allowedPathsOnlyCoverNonProductArtifacts;
|
|
295
|
-
const backendDelivery = titleSignals.backendDelivery || requirementSignals.backendDelivery;
|
|
296
|
-
const taskTypeConflict = (hasFrontendTaskType && (backendDelivery || frontendNegated)) ||
|
|
297
|
-
(hasBackendTaskType && eligibleFrontendDelivery);
|
|
298
|
-
if (taskTypeConflict)
|
|
299
|
-
blockers.push("task-type-conflict");
|
|
300
|
-
let kind;
|
|
301
|
-
if ((hasFrontendTaskType && hasBackendTaskType) || taskTypeConflict) {
|
|
302
|
-
kind = "mixed";
|
|
303
|
-
}
|
|
304
|
-
else if (hasFrontendTaskType) {
|
|
305
|
-
kind = "frontend";
|
|
306
|
-
}
|
|
307
|
-
else if (hasBackendTaskType) {
|
|
308
|
-
kind = "backend";
|
|
309
|
-
}
|
|
310
|
-
else if (eligibleFrontendDelivery && backendDelivery) {
|
|
311
|
-
kind = "mixed";
|
|
312
|
-
}
|
|
313
|
-
else if (eligibleFrontendDelivery && frontendNegated) {
|
|
314
|
-
kind = "mixed";
|
|
315
|
-
}
|
|
316
|
-
else if (eligibleFrontendDelivery) {
|
|
317
|
-
kind = "frontend";
|
|
318
|
-
}
|
|
319
|
-
else if (backendDelivery) {
|
|
320
|
-
kind = "backend";
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
kind = "unknown";
|
|
324
|
-
}
|
|
325
|
-
return {
|
|
326
|
-
kind,
|
|
327
|
-
signals,
|
|
328
|
-
blockers,
|
|
329
|
-
reasons: reasonsForClassification({ kind, signals, blockers }),
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
export function resolveTaskDagTemplateSelection(input) {
|
|
333
|
-
const requestedTemplate = input.requestedTemplate ?? "standard-dag";
|
|
334
|
-
const taskKindTemplate = SPECIAL_TEMPLATE_BY_TASK_KIND[input.taskKind];
|
|
335
|
-
if (taskKindTemplate) {
|
|
336
|
-
return {
|
|
337
|
-
template: taskKindTemplate,
|
|
338
|
-
source: "taskKind",
|
|
339
|
-
reasons: [TASK_KIND_ROUTING_REASON[input.taskKind]],
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
if (SPECIAL_REQUESTED_TEMPLATES.has(requestedTemplate)) {
|
|
343
|
-
return {
|
|
344
|
-
template: requestedTemplate,
|
|
345
|
-
source: "requested-template",
|
|
346
|
-
reasons: [],
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
if (input.taskKind !== "standard") {
|
|
350
|
-
return {
|
|
351
|
-
template: requestedTemplate,
|
|
352
|
-
source: "requested-template",
|
|
353
|
-
reasons: [],
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
if (requestedTemplate !== "standard-dag") {
|
|
357
|
-
return {
|
|
358
|
-
template: requestedTemplate,
|
|
359
|
-
source: "requested-template",
|
|
360
|
-
reasons: [],
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
const classification = classifyTaskDemand(input);
|
|
364
|
-
if (classification.kind === "frontend") {
|
|
365
|
-
const frontendSignals = classification.signals
|
|
366
|
-
.map((signal) => signal.id)
|
|
367
|
-
.filter((id) => id === "task-type-frontend-feature" || id.startsWith("frontend-"));
|
|
368
|
-
return {
|
|
369
|
-
template: "frontend-implementation",
|
|
370
|
-
source: "task-source",
|
|
371
|
-
reasons: [
|
|
372
|
-
`task source classified as frontend from signals: ${frontendSignals.join(", ")}`,
|
|
373
|
-
],
|
|
374
|
-
classification,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
return {
|
|
378
|
-
template: requestedTemplate,
|
|
379
|
-
source: "requested-template",
|
|
380
|
-
reasons: [],
|
|
381
|
-
classification,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
1
|
+
export * from "../../task/task-demand-routing.js";
|
package/docs/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`docs/` 是 loop-agent 源仓的治理文档根目录。新读者不需要先浏览所有文件,先按任务选择入口,再由目录 README 或 `docs/document-catalog.json` 判断文档身份。
|
|
4
4
|
|
|
5
|
-
**维护日:2026-07-
|
|
5
|
+
**维护日:2026-07-27** — 对齐 `@tea-agent/loop-agent@0.24.0`。版本事实以根 `CHANGELOG.md` 与 `package.json` 为准;能力短摘要见 `reports/current-capability-summary.md`。
|
|
6
6
|
|
|
7
7
|
## 我现在要做什么?
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
本目录是 loop-agent 维护者架构文档入口。每篇文档回答一个具体问题,不重复 `runtime-boundaries.md` 的依赖方向表与 governance-hook 表;遇到契约级事实请回到该文件。
|
|
4
4
|
|
|
5
|
-
**维护日:2026-07-
|
|
5
|
+
**维护日:2026-07-27** — 索引与演进叙述对齐仓库头 `@tea-agent/loop-agent@0.24.0`。边界表仍以 `runtime-boundaries.md` 与 `scripts/check-*.sh` 为准。
|
|
6
6
|
|
|
7
7
|
## 阅读路径
|
|
8
8
|
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
1. `runtime-boundaries.md` — runtime 层边界、依赖方向与治理 hook 的机器校验契约(**先读,是其他文档的边界真源**)。
|
|
12
12
|
2. `agents-invariants.md` — `AGENTS.md`(source)与 init managed block 的「什么不能删」不变量契约(短语真源 + 行数 guardrail 语义)。
|
|
13
13
|
3. `system-overview.md` — loop-agent / agent-worker / 治理层 / 外部系统的全景关系。
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
4. `dag-execution.md` — Agent DAG 主调用链、rank 调度、executor、skill snapshot、生命周期。
|
|
15
|
+
5. `worker-and-feature.md` — agent-worker 子进程边界、controller identity、Task Pool、Feature、Observe 与 Console。
|
|
16
|
+
6. `facts-and-state.md` — `.harness/` 各根目录、canonical facts、derived read models 与不可变规则。
|
|
17
|
+
7. `evolution.md` — 当前已实现能力 vs 第 3–6 月未来方向(明确标注规划/未实现)。
|
|
18
18
|
|
|
19
19
|
## 事实与规划的区分
|
|
20
20
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
本页区分 loop-agent **当前已实现**的架构能力与**未来规划**。当前事实以代码、发布 CLI、已完成计划为准;未来能力一律标「规划 / 未实现 / 前瞻」。权威源:`CHANGELOG.md`、`docs/reports/current-capability-summary.md`、ADR 0001–0005、`docs/exec-plans/completed/` 与 `docs/exec-plans/active/`。
|
|
4
4
|
|
|
5
|
-
**对照版本:`@tea-agent/loop-agent@0.
|
|
5
|
+
**对照版本:`@tea-agent/loop-agent@0.24.0`(2026-07-27)**。细节版本条目见根 `CHANGELOG.md`;本表只保留架构层可读摘要。
|
|
6
6
|
|
|
7
|
-
## 当前已实现(累计至 0.
|
|
7
|
+
## 当前已实现(累计至 0.24.0)
|
|
8
8
|
|
|
9
9
|
| 域 | 现状 | 权威入口 |
|
|
10
10
|
| --- | --- | --- |
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| Frontend-test | 默认 short-chain RAG;内容/evidence 质量进入 advisory findings,路径与安全边界仍 fail-closed | `CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
22
22
|
| Frontend-implementation | Contract / trace / repair / Mock assess;`ai_workspace` + 知识库 + `openspec` 规范发现;lint 基线债务隔离 | `CHANGELOG.md [0.22.0]` |
|
|
23
23
|
| Inspect(原 Observe) | 统一 Console 的 `/inspect/` 只读运营面与富时间线;兼容期仍保留独立 `observe serve` | `website/docs/guides/observe-ui.md`、`CHANGELOG.md [Unreleased]` |
|
|
24
|
-
| Local Operator Console | `agent-worker console serve\|doctor`(loopback);Operate + Inspect、Happy Path / Interview / split view / recovery CTA
|
|
24
|
+
| Local Operator Console | `agent-worker console serve\|doctor`(loopback);Operate + Inspect、Happy Path / Interview / split view / recovery CTA;**0.23.x 起含 General Operator Chat**(Pi SDK services session、operator-only tool surface);**非**远端多用户 Console | unify design;ADR 0005;completed `2026-07-25-console-phase-4-general-operator-chat.md` |
|
|
25
25
|
| Task Contract / operator surface | journaled `task contract *`;machine envelope;**DagSpec v4 `taskContractBinding`**(exclusive writer 强制) | `CHANGELOG.md [0.17.0]`–`[0.17.2]`;ADR 0005 |
|
|
26
26
|
| DagSpec / repair | v3 `runtimeContract` + 显式 `repairNodeId`;新 writer 生成默认 v4 binding | `dag-execution.md`、`CHANGELOG.md [0.11.0]` / `[0.17.0]` |
|
|
27
27
|
| 自适应 liveness | runner lease、Provider/tool/output 活动与 meaningful progress 分离;静默 stall 受控终止,退出未确认时禁止自动重试 | `dag-execution.md`、active `2026-07-23-dag-adaptive-liveness.md` |
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
| fullstack Delivery / Final Verification live 闭环 | 进行中(active train) | `docs/exec-plans/active/2026-07-19-fullstack-dogfood-016x-release-train.md` |
|
|
53
53
|
| Dynamic Workflow runtime limits 强执法、更广 profile | 设计输入 | `docs/design/active/dynamic-workflow-dag-engine-roadmap.md` |
|
|
54
54
|
| Loop 与 Dynamic Workflow 更深双向集成 / 自动恢复 | 设计输入 | 同上;当前已有基础 `workflow` action |
|
|
55
|
-
|
|
|
55
|
+
| Operator Chat 作为主操作会话(裸 bash 收敛、真 human gate、durable session/turn、Operation 闭环、完整 CLI action coverage、默认入口切换) | 设计输入 / 未达主会话门槛 | `docs/design/active/operator-chat-primary-session-roadmap.md`;Phase 4 General Operator Chat 本体已实现归档(`docs/exec-plans/completed/2026-07-25-console-phase-4-general-operator-chat.md`) |
|
|
56
56
|
| Eval Lab auto-promote / RSI Level 1 产品声称 | **禁止**写成已交付 | Eval Lab design + active plan |
|
|
57
57
|
|
|
58
58
|
> 本地 Operator Console 统一面(实际首发 0.19.0)已发布:单仓库、loopback、随 `@tea-agent/loop-agent` 同包、canonical mutation 只经 sibling CLI,Inspect 复用 GET-only Observe read model。它不是远端 Web Console,也不能把多租户/云编排需求偷渡进本地 Console。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
本页说明 `agent-worker` 如何通过冻结的已发布 `loop-agent` 子进程执行 DAG(不 in-process import runtime kernel),以及其上的产品线 read model:TaskSpec、Task Pool、Feature 与统一 Operator Console(Operate + Inspect)。边界契约权威是 `runtime-boundaries.md` §Worker adapter。
|
|
4
4
|
|
|
5
|
-
**维护校准:2026-07-
|
|
5
|
+
**维护校准:2026-07-27 / `@tea-agent/loop-agent@0.24.0`** — 统一 Operator Console(Operate + Inspect)、恢复操作面与自适应 liveness 已落地;细节以 `CHANGELOG.md` 与对应 active/completed plan 为准。
|
|
6
6
|
|
|
7
7
|
## 核心事实:子进程,非 in-process
|
|
8
8
|
|
|
@@ -202,8 +202,8 @@
|
|
|
202
202
|
".harness/dag-runs/**",
|
|
203
203
|
"artifacts/**"
|
|
204
204
|
],
|
|
205
|
-
"outputContract": "Convert every final automatable Markdown case into pytest assets whose actual test function region contains the exact Case ID, preferably in the function name or docstring;
|
|
206
|
-
"subtask_prompt": "Convert testcase/md/** to pytest using upstream environment and advisory validation evidence plus only bounded pytest config/conftest. A FAIL advisory report does not authorize inventing missing behavior; use the final Markdown facts that are present.\n\nEnsure every final Markdown Case ID appears in at least one real pytest test function or pytest test class method region, preferably as `test_BE_<MODULE>_<NNN>_<description>` and in that function/method docstring. Module-level functions and class-based pytest methods are both supported. Multiple test functions may cover one Case ID; assertions come only from 预期结果/Expected Results and setup comes only from 前置条件 plus any optional 测试数据/自动化映射 or their legacy English aliases.\n\nGenerate a reusable HTTP logging helper (or equivalent client wrapper) and call it for every interface request. The request log must include method, URL/path, and request parameters (query plus JSON/body/payload summary). The response log must include status code and response result (JSON/text/body summary), and both records must be visible in pytest stdout/stderr without changing assertions.\n\nCompare timestamps and other semantically equivalent protocol values by parsed meaning, not byte-for-byte serialization. In particular, normalize valid ISO-8601 instants before equality/order assertions so differences such as omitted trailing fractional seconds do not create TestBug failures; preserve exact-string assertions only when the Markdown explicitly requires representation equality.\n\nBefore logging, recursively redact sensitive keys and header values including authorization, proxy-authorization, cookie, set-cookie, token, password, secret, api key and credentials. Never print full Authorization/Cookie values. Apply bounded truncation to serialized request and response bodies (with an explicit truncation marker) so large payloads cannot flood pytest or report artifacts.\n\nDo not read source/**, add cases, reassign ACs, modify conftest/config/production code, use skip/xfail, swallow assertions, execute pytest, or emit JSON. For best-effort cleanup, catch only the narrow transport exception actually raised by the selected HTTP client (for example `requests.RequestException` or `urllib.error.URLError`); never use bare `except`, `Exception`, or `BaseException` with `pass`."
|
|
205
|
+
"outputContract": "Convert every final automatable Markdown case into pytest assets whose actual test function region contains the exact Case ID, preferably in the function name or docstring. Each testcase/md/<module>.md (excluding README.md) maps one-to-one to testcase/test_<module>.py; never merge or split modules. No JSON and no pytest execution.",
|
|
206
|
+
"subtask_prompt": "Convert testcase/md/** to pytest using upstream environment and advisory validation evidence plus only bounded pytest config/conftest. A FAIL advisory report does not authorize inventing missing behavior; use the final Markdown facts that are present.\n\nEnsure every final Markdown Case ID appears in at least one real pytest test function or pytest test class method region, preferably as `test_BE_<MODULE>_<NNN>_<description>` and in that function/method docstring. Module-level functions and class-based pytest methods are both supported. Multiple test functions may cover one Case ID; assertions come only from 预期结果/Expected Results and setup comes only from 前置条件 plus any optional 测试数据/自动化映射 or their legacy English aliases.\n\nName each generated pytest file so it corresponds one-to-one with its source Markdown module file: for each `testcase/md/<module>.md` (excluding README.md), emit exactly one `testcase/test_<module>.py`. The <module> stem is the Markdown filename without the `.md` extension, lowercased and with non-alphanumeric characters replaced by underscores. For example, `testcase/md/resource_notes.md` maps to `testcase/test_resource_notes.py`, and `testcase/md/order-api.md` maps to `testcase/test_order_api.py`. Never merge multiple Markdown modules into one pytest file, never split one module across several files, and never invent pytest filenames unrelated to the Markdown modules.\n\nGenerate a reusable HTTP logging helper (or equivalent client wrapper) and call it for every interface request. The request log must include method, URL/path, and request parameters (query plus JSON/body/payload summary). The response log must include status code and response result (JSON/text/body summary), and both records must be visible in pytest stdout/stderr without changing assertions.\n\nCompare timestamps and other semantically equivalent protocol values by parsed meaning, not byte-for-byte serialization. In particular, normalize valid ISO-8601 instants before equality/order assertions so differences such as omitted trailing fractional seconds do not create TestBug failures; preserve exact-string assertions only when the Markdown explicitly requires representation equality.\n\nBefore logging, recursively redact sensitive keys and header values including authorization, proxy-authorization, cookie, set-cookie, token, password, secret, api key and credentials. Never print full Authorization/Cookie values. Apply bounded truncation to serialized request and response bodies (with an explicit truncation marker) so large payloads cannot flood pytest or report artifacts.\n\nDo not read source/**, add cases, reassign ACs, modify conftest/config/production code, use skip/xfail, swallow assertions, execute pytest, or emit JSON. For best-effort cleanup, catch only the narrow transport exception actually raised by the selected HTTP client (for example `requests.RequestException` or `urllib.error.URLError`); never use bare `except`, `Exception`, or `BaseException` with `pass`."
|
|
207
207
|
},
|
|
208
208
|
{
|
|
209
209
|
"id": "backend-test-traceability-gate-shell",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"riskLevel": { "enum": ["small", "standard", "high-risk"] },
|
|
12
12
|
"targets": { "type": "object", "additionalProperties": false, "required": ["files"], "properties": { "files": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/path" } }, "routes": { "type": "array", "items": { "type": "string", "pattern": "^/" } }, "publicApiChanges": { "type": "array", "items": { "type": "string", "minLength": 1 } } } },
|
|
13
13
|
"requirements": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["id", "implementationTargets", "verificationTargetIds"], "properties": { "id": { "$ref": "#/$defs/requirementId" }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "evidenceGap": { "$ref": "#/$defs/gap" } } } },
|
|
14
|
-
"uiStates": { "type": "array", "
|
|
14
|
+
"uiStates": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "applicable"], "properties": { "name": { "type": "string", "minLength": 1 }, "applicable": { "type": "boolean" }, "expectedBehavior": { "type": "string", "minLength": 1 }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "notApplicableReason": { "type": "string", "minLength": 1 } }, "allOf": [{ "if": { "properties": { "applicable": { "const": true } }, "required": ["applicable"] }, "then": { "required": ["expectedBehavior", "implementationTargets", "verificationTargetIds"], "properties": { "implementationTargets": { "minItems": 1 }, "verificationTargetIds": { "minItems": 1 } } } }, { "if": { "properties": { "applicable": { "const": false } }, "required": ["applicable"] }, "then": { "required": ["notApplicableReason"] } }] } },
|
|
15
15
|
"interactions": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "implementationTargets", "verificationTargetIds"], "properties": { "name": { "type": "string", "minLength": 1 }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string" } } } } },
|
|
16
16
|
"mockApi": { "type": "object", "additionalProperties": false, "required": ["strategy", "productionDefaultOff", "activation", "endpoints"], "properties": { "strategy": { "enum": ["native", "browser-intercept", "request-adapter", "not-needed"] }, "productionDefaultOff": { "const": true }, "activation": { "type": "string", "minLength": 1 }, "endpoints": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["method", "path"], "properties": { "method": { "enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] }, "path": { "type": "string", "pattern": "^/" }, "fixture": { "$ref": "#/$defs/path" }, "consumer": { "$ref": "#/$defs/path" } } } } } },
|
|
17
17
|
"designEvidence": { "type": "object", "additionalProperties": false, "required": ["source", "paths", "conflicts"], "properties": { "source": { "type": "string", "minLength": 1 }, "paths": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "conflicts": { "type": "array", "items": { "type": "string", "minLength": 1 } } } },
|
package/harness.json
CHANGED
package/package.json
CHANGED
|
@@ -29,6 +29,13 @@ rules, commands, or retrievals. Scout/planners locate and explicitly read applic
|
|
|
29
29
|
specs; only successful paired reads count. Lockfile-only, fixture-only, or unread
|
|
30
30
|
search hits do not prove a reusable Mock service.
|
|
31
31
|
|
|
32
|
+
This broad discovery rule applies only to contract, scout, and plan phases. For
|
|
33
|
+
`frontend-implement-pi`, the run-owned
|
|
34
|
+
`contracts/frontend-implementation-contract.json` is the approved semantic input;
|
|
35
|
+
do not repeat task-source, OpenSpec, AI workspace, plan, or repository-wide
|
|
36
|
+
discovery. For `frontend-repair-pi`, consume that contract plus
|
|
37
|
+
`contracts/frontend-repair-assessment.json` and make only the bounded repair.
|
|
38
|
+
|
|
32
39
|
## Implementation Discipline
|
|
33
40
|
|
|
34
41
|
- Reuse confirmed specs, components, tokens, helpers, APIs, mocks, schemas, and tests.
|
|
@@ -6,7 +6,7 @@ Pre-write nodes are read-only. Preserve IDs, labels, commands, language, require
|
|
|
6
6
|
|
|
7
7
|
- **`frontend-contract-pi`**: `Scope`, `Non-goals`, `Acceptance Criteria`, `UI States`, `Target Runtime Environment`, `Risks`, `Verification Expectations`. No guessed requirements.
|
|
8
8
|
- **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets. Fact vs inference vs gap. Query the knowledge base when available and always search+read `openspec/schemas/`, `openspec/project-specs/`, and `ai_workspace/` before repo fallback. Output stack, routes, components, styling, conventions, state/data, test entry points, reuse, risks.
|
|
9
|
-
- **`frontend-plan-pi` + conditional design loop**: AC → steps, in-bound files, UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, real-integration gap, and exactly one `frontend-implementation-contract-v1` JSON object. Prefer native Mock; browser intercept only with existing e2e; request-adapter only for a reversible seam. `auto` may select `not-needed` when no project Mock capability exists, while keeping real requests default and recording the gap; `required` cannot. Initial design pass uses the original plan; only exact `request-revision` runs read-only revision plus final review. Small-risk runs one design review only.
|
|
9
|
+
- **`frontend-plan-pi` + conditional design loop**: AC → steps, in-bound files, applicable UI states, reuse, deps, Mock/API strategy, activation/rollback, frozen verify entrypoints, real-integration gap, and exactly one `frontend-implementation-contract-v1` JSON object. Use `uiStates: []` for logic-only changes with no user-visible UI state; do not invent UI states. Applicable states require behavior/implementation/verification, while non-applicable states require a reason and omit empty behavior placeholders. Prefer native Mock; browser intercept only with existing e2e; request-adapter only for a reversible seam. `auto` may select `not-needed` when no project Mock capability exists, while keeping real requests default and recording the gap; `required` cannot. Initial design pass uses the original plan; only exact `request-revision` runs read-only revision plus final review. Small-risk runs one design review only.
|
|
10
10
|
- **`frontend-prewrite-gate-shell`**: the sole write authorization. Resolve effective plan/review, require exact pass, retain every REQ/BR/AC id, enforce Mock policy, validate schema/source binding, and materialize `contracts/frontend-implementation-contract.json`. Fallback is allowed only when a conditional primary is absent; an existing malformed primary fails closed. Generation-time blocked Mock produces one deterministic blocking shell node and no writer.
|
|
11
11
|
- **`frontend-implement-pi`**: sole exclusive writer. Stay in `writeSet`; real requests default-on; Mock reversible, dev/test-only, production-off. Atomic handler/intercept/adapter with consumer+tests. Stop on forbidden paths or guesses. Output changed files, behavior, UI states, styling notes, verification attempted, residual risks. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. Skipped-Mock `not-needed` keeps real integration pending unless the real backend path has fresh evidence.
|
|
12
12
|
|
|
@@ -30,9 +30,10 @@ verdict/findings, and required browser, visual, manual, or knowledge evidence.
|
|
|
30
30
|
`frontend-lint-assessment-v1` proving only pre-writer diagnostics on unchanged
|
|
31
31
|
files. Report debt count, changed files, and raw evidence. Typecheck/build/test do
|
|
32
32
|
not support debt.
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
- Use design and requirement evidence already bound inside
|
|
34
|
+
`contracts/frontend-review-context.json`. Closeout must not query connectors,
|
|
35
|
+
re-open task sources, OpenSpec, AI workspace, upstream model prose, or start new
|
|
36
|
+
repository research.
|
|
36
37
|
- Separate Mock service/handler checks from page consumption and record the
|
|
37
38
|
dev/test-only boundary; handler tests alone do not prove page use.
|
|
38
39
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
此 policy 驱动 `dag run-task --profile auto`:CLI 仍要求显式 `dag run-task`、`dag validate`、`run-dag`,但 `--profile auto` 在确定性 candidate `governanceProfile` 推断后应用 `workflowPolicy.dag.profileRouting`。生成器还会把 `outputLanguage` 写入 DagSpec,runner 在每个 Pi/Cursor 节点 prompt 中注入语言规则;代码、命令、路径、JSON 字段与 gate token 保持原样。`humanGatePolicy` 是默认人机边界声明;真实暂停仍由 DAG 节点的 `decisionGate.mode: "pause-on-human"` 与 decision envelope 触发。无 profile 的 `dag run-task <task-id>` 仍为 standard-compatible,供 legacy/review workflow。
|
|
16
16
|
|
|
17
|
-
对于默认 `standard`
|
|
17
|
+
对于默认 `standard` 任务,生成器组合标题、`source/需求.md`、结构化 `allowedPaths`、React/Next/Vue 强工程证据与用户可见交互/状态语义,做保守、确定性的需求分类。即使需求没有显式写出“前端/UI/页面”,工程证据与产品交付语义共同成立时也会自动选择 `frontend-implementation` DAG;框架证据单独不足以触发。后端、前后端混合、明确排除前端、文档/测试维护或证据不足的需求继续使用 governance profile 选出的模板。分类不会把普通后端实现路由到 `backend-test`,也不会替换由显式 profile、`workflowPolicy` 或 supervised quality gate 选中的 supervised 模板。
|
|
18
18
|
|
|
19
19
|
前端专用链保留独立 contract/scout;plan 同时选择 Mock/API 策略并输出结构化 implementation contract。design initial pass 直接使用原计划,只有 request-revision 才运行 revision/final review;small-risk 只执行一次 design review。`frontend-prewrite-gate-shell` 合并生效 verdict、REQ/BR/AC 覆盖、Mock policy 和 contract 物化,是唯一写入授权。实现后 `frontend-verify-assess-shell` 合并 Mock/static/behavior/trace/assessment;只有 `eligible=true` 才运行同 writeSet 的 repair 和 `frontend-reverify-shell`。`frontend-review-context-shell` 绑定真实 diff 与有效验证证据后再 review/closeout。standard/high-risk 为 15 个顶层节点,small-risk 为 13;绿色路径执行 11 个节点、7 次 Pi。生成期 blocked Mock 只生成一个确定性阻塞节点且没有 writer。
|
|
20
20
|
|