@tea-agent/loop-agent 0.7.3 → 0.7.4

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 CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ### 新增
8
+
9
+ - 新增 repository-owned Worker nightly wrapper 与 GitHub Actions 手动/定时适配面:按 feature 互斥、保留超时/批次退出码,并收集 controller、morning report、Observe snapshot 等 artifacts。
10
+ - 新增 `agent-worker task validate-feature` 和可初始化投影的语言无关产品线模板,校验 AC 唯一性、任务依赖、TaskSpec 路径/验证命令及 QA verdict → success closeout 顺序,并接入仓库治理门禁。
11
+
12
+ ## [0.7.4] - 2026-07-11
13
+
14
+ ### 修复
15
+
16
+ - Worker materialize 现在把 TaskSpec 中所有 required `verify.commands` 写入 task config,并强制合并到 DAG 最终 shell verification;review 不再只能看到仓库级治理命令而缺少任务契约要求的 typecheck/focused tests。
17
+
7
18
  ## [0.7.3] - 2026-07-11
8
19
 
9
20
  ### 修复
package/README.md CHANGED
@@ -88,6 +88,15 @@ loop-agent pi-prompt --cwd . --tools read,grep,find,ls "只读评审这个任务
88
88
  loop-agent pi-prompt --cwd . --tools read,bash,edit,write,grep,find,ls "<包含 allowedPaths 和 forbiddenPaths 的有边界任务说明>"
89
89
  ```
90
90
 
91
+ 产品线 feature packet 可用伴生 Worker CLI 做 docs CI;仓库也提供外部 CI/cron wrapper,调度仍由外部系统负责:
92
+
93
+ ```bash
94
+ agent-worker task validate-feature <feature-dir>
95
+ bash scripts/worker-nightly.sh <feature-dir> <target-repo> <batch-run-id>
96
+ ```
97
+
98
+ nightly wrapper 按 feature 互斥,保留批次/超时退出码,并输出 morning report、Observe snapshot 和 controller 版本 artifacts。
99
+
91
100
  ## 核心概念
92
101
 
93
102
  - **Agent DAG**:把一次任务拆成 contract、scout、plan、implement、verify、closeout 等可审查节点。
@@ -418,6 +418,7 @@ checks=(
418
418
  "scripts/check-harness-runtime-clean.sh"
419
419
  "scripts/check-architecture-boundaries.sh"
420
420
  "scripts/check-skill-entry.sh"
421
+ "scripts/check-product-line-docs.sh"
421
422
  )
422
423
 
423
424
  for check in "\${checks[@]}"; do
@@ -428,6 +429,32 @@ done
428
429
 
429
430
  echo "仓库治理检查全部通过"
430
431
  `;
432
+ const INIT_CHECK_PRODUCT_LINE_DOCS_SH = `#!/usr/bin/env bash
433
+ set -euo pipefail
434
+
435
+ ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
436
+ cd "\${ROOT_DIR}"
437
+
438
+ if ! command -v agent-worker >/dev/null 2>&1; then
439
+ echo "product-line docs check skipped: agent-worker is not installed"
440
+ exit 0
441
+ fi
442
+
443
+ feature_dirs=()
444
+ for root in features product/features dogfood/features; do
445
+ [[ -d "\${root}" ]] || continue
446
+ while IFS= read -r acceptance; do
447
+ feature_dirs+=("$(dirname "\${acceptance}")")
448
+ done < <(find "\${root}" -mindepth 2 -maxdepth 2 -name acceptance.yaml -type f | sort)
449
+ done
450
+
451
+ for feature_dir in "\${feature_dirs[@]}"; do
452
+ echo "==> validate product-line feature: \${feature_dir}"
453
+ agent-worker task validate-feature "\${feature_dir}"
454
+ done
455
+
456
+ echo "product-line docs checks passed: \${#feature_dirs[@]} feature packet(s)"
457
+ `;
431
458
  const INIT_CI_GOVERNANCE_SH = `#!/usr/bin/env bash
432
459
  set -euo pipefail
433
460
 
@@ -570,6 +597,7 @@ const INIT_SCRIPT_FILES = {
570
597
  "scripts/check-harness-runtime-clean.sh": INIT_CHECK_HARNESS_RUNTIME_CLEAN_SH,
571
598
  "scripts/check-architecture-boundaries.sh": INIT_CHECK_ARCHITECTURE_BOUNDARIES_SH,
572
599
  "scripts/check-skill-entry.sh": INIT_CHECK_SKILL_ENTRY_SH,
600
+ "scripts/check-product-line-docs.sh": INIT_CHECK_PRODUCT_LINE_DOCS_SH,
573
601
  "scripts/check-repo.sh": INIT_CHECK_REPO_SH,
574
602
  "scripts/ci-governance.sh": INIT_CI_GOVERNANCE_SH,
575
603
  "scripts/ci-tests.sh": INIT_CI_TESTS_SH,
@@ -25,6 +25,11 @@ export const piSubagentModeSchema = z.enum(["off", "analyze-plan", "full"]);
25
25
  export const verifyModeSchema = z.enum(["parallel", "serial"]);
26
26
  export const verifyPresetSchema = z.enum(["auto", "quick", "standard", "full"]);
27
27
  export const verifyQuotaSchema = z.enum(["1", "3", "full"]);
28
+ export const taskVerifyCommandSchema = z.object({
29
+ label: z.string().min(1),
30
+ command: z.string().min(1),
31
+ timeoutMs: z.number().int().positive().optional(),
32
+ });
28
33
  export const dagVerifyStrategySchema = z
29
34
  .object({
30
35
  intermediateQuota: verifyQuotaSchema.optional(),
@@ -73,6 +78,8 @@ export const taskConfigSchema = z.object({
73
78
  capabilities: z.array(taskCapabilitySchema).optional(),
74
79
  verifyMode: verifyModeSchema.optional().default("parallel"),
75
80
  verifyPreset: verifyPresetSchema.optional().default("auto"),
81
+ /** Task-contract commands that must be included in final DAG shell verification. */
82
+ verifyCommands: z.array(taskVerifyCommandSchema).optional().default([]),
76
83
  /** Verification selection policy. Intermediate loops may use quota; final gates still run full required verification. */
77
84
  verifyQuota: verifyQuotaSchema.optional().default("full"),
78
85
  /** DAG supervised verify strategy. Final quota is fixed to full; intermediate quota may reduce cost. */
@@ -104,6 +104,7 @@ export async function createTask(repoRoot, taskId, title) {
104
104
  contextProfile: 'full',
105
105
  verifyMode: 'parallel',
106
106
  verifyPreset: 'auto',
107
+ verifyCommands: [],
107
108
  verifyQuota: 'full',
108
109
  verifyRetryCount: 0,
109
110
  verifyFailFast: false,
@@ -16,6 +16,7 @@ import { createObserveServer } from "./observe/server.js";
16
16
  import { prepareTaskPoolRetry } from "./pool/run-store.js";
17
17
  import { taskSpecSchema } from "./task-spec/schema.js";
18
18
  import { validateTaskSpec } from "./task-spec/validate.js";
19
+ import { validateFeatureTaskGraph } from "./task-graph/validate.js";
19
20
  export function buildAgentWorkerProgram() {
20
21
  const program = new Command();
21
22
  program
@@ -49,6 +50,16 @@ export function buildAgentWorkerProgram() {
49
50
  const result = resolveLoopAgentProfile(parsed);
50
51
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
51
52
  });
53
+ task
54
+ .command("validate-feature")
55
+ .argument("<feature-dir>", "Feature directory containing acceptance.yaml and tasks/")
56
+ .description("Validate acceptance ids, task graph, TaskSpecs, paths, and verification commands")
57
+ .action(async (featureDir) => {
58
+ const result = await validateFeatureTaskGraph(path.resolve(featureDir));
59
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
60
+ if (!result.ok)
61
+ process.exitCode = 1;
62
+ });
52
63
  task
53
64
  .command("retry")
54
65
  .argument("<task-id>", "Failed Task Pool task to requeue")
@@ -44,6 +44,13 @@ export async function materializeTaskSpec(options) {
44
44
  verifyMode: options.taskSpec.verify.mode,
45
45
  verifyPreset: options.taskSpec.verify.preset,
46
46
  verifyQuota: options.taskSpec.verify.quota,
47
+ verifyCommands: options.taskSpec.verify.commands
48
+ .filter((command) => command.required)
49
+ .map((command) => ({
50
+ label: command.label,
51
+ command: command.command,
52
+ timeoutMs: command.timeout_ms,
53
+ })),
47
54
  autoCommitAfterVerify: false,
48
55
  };
49
56
  await writeTaskConfig(options.repoRoot, harnessTaskId, taskConfig);
@@ -2,6 +2,7 @@ import { access, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import YAML from "yaml";
4
4
  import { taskSpecSchema } from "../task-spec/schema.js";
5
+ import { validateTaskSpec } from "../task-spec/validate.js";
5
6
  import { acceptanceSpecSchema } from "./acceptance-schema.js";
6
7
  import { computeReadyQueue } from "./ready-queue.js";
7
8
  import { taskGraphSpecSchema } from "./task-graph-schema.js";
@@ -13,12 +14,57 @@ export async function validateFeatureTaskGraph(featureDir, options = {}) {
13
14
  return result("", acceptance, graph, errors);
14
15
  }
15
16
  checkDuplicateNodeIds(graph, errors);
17
+ checkDuplicateAcceptanceIds(acceptance, errors);
16
18
  checkAcceptanceTaskRefs(acceptance, graph, errors);
17
19
  checkUnknownDependencies(graph, errors);
18
20
  checkCycles(graph, errors);
19
- await checkTaskFiles(featureDir, graph, errors);
21
+ await checkTaskFiles(featureDir, graph, acceptance, errors);
22
+ await checkQaCloseoutOrder(featureDir, options, errors);
20
23
  return result(graph.feature_id, acceptance, graph, errors);
21
24
  }
25
+ async function checkQaCloseoutOrder(featureDir, options, errors) {
26
+ const closeoutPath = path.join(featureDir, "closeout.yaml");
27
+ let raw = options.closeoutOverride;
28
+ if (raw === undefined) {
29
+ if (!(await exists(closeoutPath)))
30
+ return;
31
+ raw = YAML.parse(await readFile(closeoutPath, "utf-8"));
32
+ }
33
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
34
+ errors.push({ code: "closeout-schema-invalid", message: "closeout.yaml must be an object", path: "closeout.yaml" });
35
+ return;
36
+ }
37
+ const record = raw;
38
+ if (record.status !== "success")
39
+ return;
40
+ if (record.qa_verdict !== "pass") {
41
+ errors.push({
42
+ code: "qa-verdict-required-before-closeout",
43
+ message: "success closeout requires qa_verdict: pass",
44
+ path: "closeout.yaml:qa_verdict",
45
+ });
46
+ }
47
+ if (!Array.isArray(record.qa_evidence) || record.qa_evidence.length === 0) {
48
+ errors.push({
49
+ code: "qa-evidence-required-before-closeout",
50
+ message: "success closeout requires non-empty qa_evidence",
51
+ path: "closeout.yaml:qa_evidence",
52
+ });
53
+ }
54
+ }
55
+ function checkDuplicateAcceptanceIds(acceptance, errors) {
56
+ const seen = new Set();
57
+ for (const item of acceptance.acceptance) {
58
+ if (seen.has(item.id)) {
59
+ errors.push({
60
+ code: "duplicate-acceptance-id",
61
+ message: `duplicate acceptance id: ${item.id}`,
62
+ path: `acceptance.${item.id}`,
63
+ });
64
+ }
65
+ seen.add(item.id);
66
+ }
67
+ }
22
68
  async function loadAcceptanceSpec(featureDir, options, errors) {
23
69
  const raw = options.acceptanceOverride ??
24
70
  YAML.parse(await readFile(path.join(featureDir, "acceptance.yaml"), "utf-8"));
@@ -115,7 +161,8 @@ function checkCycles(graph, errors) {
115
161
  for (const node of graph.nodes)
116
162
  visit(node.id, []);
117
163
  }
118
- async function checkTaskFiles(featureDir, graph, errors) {
164
+ async function checkTaskFiles(featureDir, graph, acceptance, errors) {
165
+ const acceptanceIds = new Set(acceptance.acceptance.map((item) => item.id));
119
166
  for (const node of graph.nodes) {
120
167
  const taskPath = path.join(featureDir, "tasks", node.task);
121
168
  if (!(await exists(taskPath))) {
@@ -126,7 +173,8 @@ async function checkTaskFiles(featureDir, graph, errors) {
126
173
  });
127
174
  continue;
128
175
  }
129
- const parsedTask = taskSpecSchema.safeParse(YAML.parse(await readFile(taskPath, "utf-8")));
176
+ const rawTask = YAML.parse(await readFile(taskPath, "utf-8"));
177
+ const parsedTask = taskSpecSchema.safeParse(rawTask);
130
178
  if (!parsedTask.success) {
131
179
  errors.push({
132
180
  code: "task-spec-schema-invalid",
@@ -136,6 +184,23 @@ async function checkTaskFiles(featureDir, graph, errors) {
136
184
  continue;
137
185
  }
138
186
  const taskSpec = parsedTask.data;
187
+ const detailed = await validateTaskSpec(rawTask, { taskSpecPath: taskPath });
188
+ for (const issue of detailed.errors) {
189
+ errors.push({
190
+ code: `task-${issue.code}`,
191
+ message: `${node.id}: ${issue.message}`,
192
+ path: `${node.task}:${issue.path ?? ""}`,
193
+ });
194
+ }
195
+ for (const acceptanceRef of taskSpec.acceptance_refs) {
196
+ if (!acceptanceIds.has(acceptanceRef)) {
197
+ errors.push({
198
+ code: "task-acceptance-ref-missing",
199
+ message: `${node.id} references missing acceptance ${acceptanceRef}`,
200
+ path: `${node.task}:acceptance_refs`,
201
+ });
202
+ }
203
+ }
139
204
  if (taskSpec.id !== node.id) {
140
205
  errors.push({
141
206
  code: "task-id-mismatch",
@@ -426,12 +426,12 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
426
426
  phase: "intermediate",
427
427
  taskConfig,
428
428
  }),
429
- final: adapter.getVerifyCommands(repoRoot, {
429
+ final: mergeFinalVerifyCommands(repoRoot, taskConfig, adapter.getVerifyCommands(repoRoot, {
430
430
  preset,
431
431
  quota: "full",
432
432
  phase: "final",
433
433
  taskConfig,
434
- }),
434
+ })),
435
435
  };
436
436
  if (verifyCommands.final.length === 0) {
437
437
  throw new Error("adapter returned no final verification commands");
@@ -455,6 +455,22 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
455
455
  verifyCommands,
456
456
  };
457
457
  }
458
+ function mergeFinalVerifyCommands(repoRoot, taskConfig, adapterCommands) {
459
+ const taskCommands = taskConfig.verifyCommands.map((command) => ({
460
+ args: ["bash", "-lc", command.command],
461
+ cwd: repoRoot,
462
+ label: command.label,
463
+ timeoutMs: command.timeoutMs,
464
+ }));
465
+ const seen = new Set();
466
+ return [...taskCommands, ...adapterCommands].filter((command) => {
467
+ const key = `${command.cwd}\0${command.args.join("\0")}`;
468
+ if (seen.has(key))
469
+ return false;
470
+ seen.add(key);
471
+ return true;
472
+ });
473
+ }
458
474
  export function buildStandardHybridDagFromTask(sources) {
459
475
  const { taskConfig } = sources;
460
476
  const forbiddenPaths = mergeForbiddenPaths(taskConfig);
package/docs/README.md CHANGED
@@ -54,6 +54,7 @@
54
54
  - `templates/worker-dogfood-setup.md` — 发布控制器下的真实 Worker sample setup 与 retry 纪律
55
55
  - `templates/worker-dogfood-evidence.md` — BE/FE/QA sample、Observe、morning report 与 coverage evidence 模板
56
56
  - `templates/interactive-ui-round2-experiment.md` — interactive UI prompt/model A/B/C 对照实验与统一指标模板
57
+ - `templates/product-line/` — 可投影的 Feature/Task/QA/Links 产品线包;配合 `agent-worker task validate-feature` 做 docs CI
57
58
  - `templates/production-readiness-checklist.md` — 低/中风险单仓库 DAG readiness 检查清单
58
59
  - `templates/init-evolution-review.md` — 初始化能力演化审查报告模板
59
60
  - `templates/adr.md` — 架构决策记录(ADR)
@@ -17,6 +17,17 @@
17
17
  "docs/templates/worker-dogfood-setup.md",
18
18
  "docs/templates/worker-dogfood-evidence.md",
19
19
  "docs/templates/interactive-ui-round2-experiment.md",
20
+ "docs/templates/product-line/README.md",
21
+ "docs/templates/product-line/AGENTS.md",
22
+ "docs/templates/product-line/requirement.md",
23
+ "docs/templates/product-line/acceptance.yaml",
24
+ "docs/templates/product-line/design.md",
25
+ "docs/templates/product-line/test-plan.md",
26
+ "docs/templates/product-line/task-graph.yaml",
27
+ "docs/templates/product-line/task.yaml",
28
+ "docs/templates/product-line/closeout.yaml",
29
+ "docs/templates/product-line/links.md",
30
+ "scripts/check-product-line-docs.sh",
20
31
  "docs/templates/agent-dag.schema.json",
21
32
  "examples/example-dag.json",
22
33
  "skills/loop-agent/SKILL.md",
@@ -46,6 +57,17 @@
46
57
  "docs/templates/worker-dogfood-setup.md",
47
58
  "docs/templates/worker-dogfood-evidence.md",
48
59
  "docs/templates/interactive-ui-round2-experiment.md",
60
+ "docs/templates/product-line/README.md",
61
+ "docs/templates/product-line/AGENTS.md",
62
+ "docs/templates/product-line/requirement.md",
63
+ "docs/templates/product-line/acceptance.yaml",
64
+ "docs/templates/product-line/design.md",
65
+ "docs/templates/product-line/test-plan.md",
66
+ "docs/templates/product-line/task-graph.yaml",
67
+ "docs/templates/product-line/task.yaml",
68
+ "docs/templates/product-line/closeout.yaml",
69
+ "docs/templates/product-line/links.md",
70
+ "scripts/check-product-line-docs.sh",
49
71
  "scripts/check-repo.sh",
50
72
  "scripts/ci-governance.sh",
51
73
  "scripts/ci-tests.sh",
@@ -91,6 +113,17 @@
91
113
  "docs/templates/worker-dogfood-setup.md": "copied",
92
114
  "docs/templates/worker-dogfood-evidence.md": "copied",
93
115
  "docs/templates/interactive-ui-round2-experiment.md": "copied",
116
+ "docs/templates/product-line/README.md": "copied",
117
+ "docs/templates/product-line/AGENTS.md": "copied",
118
+ "docs/templates/product-line/requirement.md": "copied",
119
+ "docs/templates/product-line/acceptance.yaml": "copied",
120
+ "docs/templates/product-line/design.md": "copied",
121
+ "docs/templates/product-line/test-plan.md": "copied",
122
+ "docs/templates/product-line/task-graph.yaml": "copied",
123
+ "docs/templates/product-line/task.yaml": "copied",
124
+ "docs/templates/product-line/closeout.yaml": "copied",
125
+ "docs/templates/product-line/links.md": "copied",
126
+ "scripts/check-product-line-docs.sh": "copied",
94
127
  "scripts/check-repo.sh": "generated",
95
128
  "scripts/ci-governance.sh": "generated",
96
129
  "scripts/ci-tests.sh": "generated",
@@ -0,0 +1,8 @@
1
+ # Product-line execution rules
2
+
3
+ - Read requirement, acceptance, design, test plan, task graph, and the selected TaskSpec before writing.
4
+ - Treat `constraints.allowed_paths` and `constraints.forbidden_paths` as machine-enforced write boundaries; review DAG writer `writeSet` before execution.
5
+ - Advance only tasks whose dependencies are complete. Preserve failed run records; retries receive new worker run IDs.
6
+ - A Ready task must have acceptance references, non-empty allowed/forbidden paths, and deterministic verification commands.
7
+ - QA records an independent verdict and evidence. Do not write `status: success` closeout until `qa_verdict: pass` and non-empty `qa_evidence` exist.
8
+ - Human gates remain human decisions. Record owner, time, reason, evidence, and follow-up without rewriting failed history.
@@ -0,0 +1,9 @@
1
+ # Product-line feature packet
2
+
3
+ Copy this directory for each product feature. Replace angle-bracket placeholders, keep IDs stable, and run:
4
+
5
+ ```bash
6
+ agent-worker task validate-feature <feature-dir>
7
+ ```
8
+
9
+ The validator checks acceptance ID uniqueness, task references and dependencies, cycles, TaskSpec/path/verification completeness, and QA evidence before a successful closeout.
@@ -0,0 +1,14 @@
1
+ schema_version: 1
2
+ feature_id: F-YYYY-NNN
3
+ acceptance:
4
+ - id: AC-FEATURE-001
5
+ title: Observable outcome is delivered
6
+ priority: must
7
+ type: behavior
8
+ given: the documented precondition
9
+ when: the user or system performs the action
10
+ then: the observable result occurs
11
+ verification:
12
+ expected_task_refs:
13
+ - BE-001
14
+ - QA-001
@@ -0,0 +1,9 @@
1
+ schema_version: 1
2
+ feature_id: F-YYYY-NNN
3
+ status: success
4
+ qa_verdict: pass
5
+ qa_evidence:
6
+ - <run-or-report-path>
7
+ owner: <human owner>
8
+ decided_at: <ISO-8601 timestamp>
9
+ summary: <what shipped and why evidence is sufficient>
@@ -0,0 +1,13 @@
1
+ # Design — <Feature title>
2
+
3
+ ## Context and boundaries
4
+
5
+ Describe existing components, public contracts, trust boundaries, and protected areas.
6
+
7
+ ## Proposed change
8
+
9
+ Describe the smallest end-to-end path and its failure behavior.
10
+
11
+ ## Decisions and alternatives
12
+
13
+ Record material trade-offs and link ADRs where needed.
@@ -0,0 +1,10 @@
1
+ # Traceability links
2
+
3
+ | Relationship | Link or ID |
4
+ |---|---|
5
+ | Feature | `F-YYYY-NNN` |
6
+ | Acceptance → tests | `AC-FEATURE-001` → `TC-001` |
7
+ | Acceptance → tasks | `AC-FEATURE-001` → `BE-001`, `QA-001` |
8
+ | Task → worker/DAG run | `<workerRunId>` / `<dagRunId>` |
9
+ | Change/MR/PR | `<link>` |
10
+ | QA evidence | `<artifact path or link>` |
@@ -0,0 +1,17 @@
1
+ # <Feature title>
2
+
3
+ - Feature ID: `<F-YYYY-NNN>`
4
+ - Owner: `<human owner>`
5
+ - Outcome: `<user or business outcome>`
6
+
7
+ ## In scope
8
+
9
+ - `<observable capability>`
10
+
11
+ ## Out of scope
12
+
13
+ - `<explicit non-goal>`
14
+
15
+ ## Constraints
16
+
17
+ - `<security, compatibility, cost, or operational boundary>`
@@ -0,0 +1,15 @@
1
+ schema_version: 1
2
+ feature_id: F-YYYY-NNN
3
+ nodes:
4
+ - id: BE-001
5
+ task: BE-001.yaml
6
+ type: backend-feature
7
+ depends_on: []
8
+ - id: QA-001
9
+ task: QA-001.yaml
10
+ type: qa-execute
11
+ depends_on:
12
+ - BE-001
13
+ parallel_policy:
14
+ max_parallel_tasks: 2
15
+ disallow_same_file_parallel_writes: true
@@ -0,0 +1,65 @@
1
+ schema_version: 1
2
+ id: BE-001
3
+ feature_id: F-YYYY-NNN
4
+ title: <Bounded tracer-bullet task>
5
+ description: <Observable delivery>
6
+ repo:
7
+ name: <repository-name>
8
+ path_ref: <repository-path-reference>
9
+ default_branch: main
10
+ type: backend-feature
11
+ priority: P1
12
+ risk_level: medium
13
+ depends_on: []
14
+ source_docs:
15
+ requirement: ../requirement.md
16
+ acceptance: ../acceptance.yaml
17
+ design: ../design.md
18
+ test_plan: ../test-plan.md
19
+ acceptance_refs:
20
+ - AC-FEATURE-001
21
+ scope:
22
+ goals:
23
+ - <goal>
24
+ non_goals:
25
+ - <non-goal>
26
+ constraints:
27
+ allowed_paths:
28
+ - <owned/path/**>
29
+ forbidden_paths:
30
+ - <protected/path/**>
31
+ hard_constraints:
32
+ - Do not access production secrets
33
+ verify:
34
+ preset: standard
35
+ mode: serial
36
+ commands:
37
+ - id: focused-test
38
+ label: Focused deterministic verification
39
+ command: <project-specific verification command>
40
+ required: true
41
+ timeout_ms: 300000
42
+ worker:
43
+ execution_mode: generate-validate-run
44
+ max_attempts: 1
45
+ timeout_ms: 7200000
46
+ require_clean_worktree: true
47
+ require_human_review: true
48
+ create_worktree: false
49
+ loop_agent:
50
+ profile_policy: mapped
51
+ strict_models: true
52
+ strict_governance: true
53
+ no_cursor: false
54
+ max_concurrent: 1
55
+ outputs:
56
+ required:
57
+ - dag_json
58
+ - run_record
59
+ - shell_verification
60
+ - diff_patch
61
+ - closeout_or_failure_handoff
62
+ failure_policy:
63
+ failed_run_promote: false
64
+ generate_closeout_draft: true
65
+ create_followup_task: true
@@ -0,0 +1,7 @@
1
+ # Test plan — <Feature title>
2
+
3
+ | Test ID | Acceptance | Level | Scenario | Deterministic command |
4
+ |---|---|---|---|---|
5
+ | TC-001 | AC-FEATURE-001 | integration | happy path and primary failure | `<project-specific command>` |
6
+
7
+ Include negative cases, regression scope, environment assumptions, and evidence locations. Commands must match the target project's real toolchain.
@@ -26,6 +26,14 @@ npm run dev -- --help
26
26
  npm pack --dry-run
27
27
  ```
28
28
 
29
+ 产品线 Feature/Task/QA 文档或 TaskSpec 约束变更还应运行:
30
+
31
+ ```bash
32
+ bash scripts/check-product-line-docs.sh
33
+ ```
34
+
35
+ 该检查对仓库中的 feature packet 执行 `agent-worker task validate-feature`,覆盖 AC 唯一性、依赖/环、TaskSpec 路径与验证命令、QA verdict 与 success closeout 顺序。
36
+
29
37
  Windows 上通过 Git Bash 或已配置的兼容 Bash 运行 `scripts/*.sh`。跨平台 CLI 代码对真实文件操作用原生路径;`/` 仅用于稳定 repo 引用、JSON/Markdown 证据引用和 glob 约定。
30
38
 
31
39
  没有相关门禁的新鲜命令输出,不得声明完成。
package/harness.json CHANGED
@@ -50,6 +50,7 @@
50
50
  "checkHarnessRuntimeClean": "scripts/check-harness-runtime-clean.sh",
51
51
  "checkInitEvolutionNeeded": "scripts/check-init-evolution-needed.sh",
52
52
  "checkInitSurface": "scripts/check-init-surface.sh",
53
+ "checkProductLineDocs": "scripts/check-product-line-docs.sh",
53
54
  "ci": "scripts/ci.sh"
54
55
  },
55
56
  "executors": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -25,6 +25,7 @@
25
25
  "examples/",
26
26
  "harness.json",
27
27
  "skills/",
28
+ "scripts/check-product-line-docs.sh",
28
29
  "README.md",
29
30
  "CHANGELOG.md"
30
31
  ],
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ cd "${ROOT_DIR}"
6
+
7
+ feature_dirs=()
8
+ while IFS= read -r acceptance; do
9
+ feature_dirs+=("$(dirname "$acceptance")")
10
+ done < <(find dogfood/features -mindepth 2 -maxdepth 2 -name acceptance.yaml -type f | sort)
11
+
12
+ if [[ ${#feature_dirs[@]} -eq 0 ]]; then
13
+ echo "product-line docs check skipped: no feature packets found"
14
+ exit 0
15
+ fi
16
+
17
+ for feature_dir in "${feature_dirs[@]}"; do
18
+ echo "==> validate product-line feature: ${feature_dir}"
19
+ node --import tsx/esm src/worker/cli.ts task validate-feature "${feature_dir}"
20
+ done
21
+
22
+ echo "product-line docs checks passed: ${#feature_dirs[@]} feature packet(s)"