@fitlab-ai/agent-infra 0.11.5 → 0.11.7

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 (155) hide show
  1. package/dist/lib/git/primary-worktree.js +58 -0
  2. package/dist/lib/merge.js +62 -10
  3. package/dist/lib/platform/github-provider.js +9 -14
  4. package/dist/lib/platform/github-release-notes.js +80 -11
  5. package/dist/lib/platform/none-provider.js +1 -1
  6. package/dist/lib/platform/provider-contract.d.ts +39 -16
  7. package/dist/lib/platform/provider-contract.js +1 -1
  8. package/dist/lib/platform/provider-validation.js +83 -13
  9. package/dist/lib/platform/pull-requests.js +29 -3
  10. package/dist/lib/platform/release-notes.js +7 -8
  11. package/dist/lib/sandbox/capture.js +1 -1
  12. package/dist/lib/sandbox/commands/create.js +46 -1
  13. package/dist/lib/sandbox/commands/enter.js +1 -1
  14. package/dist/lib/sandbox/commands/start.js +1 -1
  15. package/dist/lib/sandbox/config.js +10 -0
  16. package/dist/lib/sandbox/control/lifecycle.js +18 -13
  17. package/dist/lib/sandbox/control/server.js +6 -2
  18. package/dist/lib/sandbox/task-resolver.js +62 -2
  19. package/dist/lib/server/config.js +7 -27
  20. package/dist/lib/server/daemon.js +4 -3
  21. package/dist/lib/server/process-control.js +4 -2
  22. package/dist/lib/server/runner.js +4 -3
  23. package/dist/lib/task/artifact-lifecycle.js +42 -10
  24. package/dist/lib/task/artifact-receipts.js +62 -22
  25. package/dist/lib/task/capabilities.js +22 -4
  26. package/dist/lib/task/completion-facts.js +56 -0
  27. package/dist/lib/task/create-service.js +6 -2
  28. package/dist/lib/task/create.js +46 -82
  29. package/dist/lib/task/events.js +268 -195
  30. package/dist/lib/task/finalization-handoff.js +2 -1
  31. package/dist/lib/task/invalidation-command.js +4 -2
  32. package/dist/lib/task/invalidation.js +38 -9
  33. package/dist/lib/task/local-artifact-finalization.js +17 -3
  34. package/dist/lib/task/qualification-audit.js +23 -105
  35. package/dist/lib/task/resolve-ref.js +3 -3
  36. package/dist/lib/task/review-finalization.js +7 -0
  37. package/dist/lib/task/review-identity.js +69 -0
  38. package/dist/lib/task/sections.js +6 -5
  39. package/dist/lib/task/task-execution-lock.js +6 -3
  40. package/dist/lib/task/verification-engine.js +14 -63
  41. package/dist/lib/task/verification.js +35 -38
  42. package/dist/lib/task/write.js +121 -1
  43. package/dist/lifecycle-build-manifest.json +3 -3
  44. package/lib/git/primary-worktree.ts +62 -0
  45. package/lib/merge.ts +59 -10
  46. package/lib/platform/github-provider.ts +11 -18
  47. package/lib/platform/github-release-notes.ts +89 -14
  48. package/lib/platform/none-provider.ts +1 -1
  49. package/lib/platform/provider-contract.ts +38 -12
  50. package/lib/platform/provider-validation.ts +83 -12
  51. package/lib/platform/pull-requests.ts +27 -3
  52. package/lib/platform/release-notes.ts +11 -12
  53. package/lib/sandbox/capture.ts +1 -1
  54. package/lib/sandbox/commands/create.ts +52 -1
  55. package/lib/sandbox/commands/enter.ts +1 -1
  56. package/lib/sandbox/commands/start.ts +1 -1
  57. package/lib/sandbox/config.ts +12 -0
  58. package/lib/sandbox/control/lifecycle.ts +22 -13
  59. package/lib/sandbox/control/server.ts +5 -2
  60. package/lib/sandbox/task-resolver.ts +51 -2
  61. package/lib/server/config.ts +7 -29
  62. package/lib/server/daemon.ts +5 -3
  63. package/lib/server/process-control.ts +4 -2
  64. package/lib/server/runner.ts +7 -4
  65. package/lib/server/server.schema.json +0 -4
  66. package/lib/task/artifact-lifecycle.ts +35 -11
  67. package/lib/task/artifact-receipts.ts +55 -15
  68. package/lib/task/capabilities.ts +17 -3
  69. package/lib/task/completion-facts.ts +66 -0
  70. package/lib/task/create-service.ts +7 -3
  71. package/lib/task/create.ts +44 -71
  72. package/lib/task/events.ts +248 -182
  73. package/lib/task/finalization-handoff.ts +2 -1
  74. package/lib/task/invalidation-command.ts +4 -2
  75. package/lib/task/invalidation.ts +35 -10
  76. package/lib/task/local-artifact-finalization.ts +13 -3
  77. package/lib/task/qualification-audit.ts +21 -114
  78. package/lib/task/resolve-ref.ts +3 -3
  79. package/lib/task/review-finalization.ts +7 -0
  80. package/lib/task/review-identity.ts +85 -0
  81. package/lib/task/sections.ts +6 -6
  82. package/lib/task/task-execution-lock.ts +7 -3
  83. package/lib/task/verification-engine.ts +13 -86
  84. package/lib/task/verification.ts +34 -38
  85. package/lib/task/write.ts +115 -1
  86. package/package.json +1 -1
  87. package/templates/.agents/rules/README.en.md +1 -0
  88. package/templates/.agents/rules/README.zh-CN.md +1 -0
  89. package/templates/.agents/rules/decision-qualification.en.md +4 -3
  90. package/templates/.agents/rules/decision-qualification.zh-CN.md +4 -3
  91. package/templates/.agents/rules/evidence-reporting.en.md +1 -2
  92. package/templates/.agents/rules/evidence-reporting.zh-CN.md +1 -2
  93. package/templates/.agents/rules/validation-output.en.md +19 -0
  94. package/templates/.agents/rules/validation-output.zh-CN.md +19 -0
  95. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -1
  96. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  97. package/templates/.agents/skills/archive-tasks/SKILL.en.md +2 -2
  98. package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +2 -2
  99. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +109 -6
  100. package/templates/.agents/skills/block-task/SKILL.en.md +2 -0
  101. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
  102. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -0
  103. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +1 -1
  104. package/templates/.agents/skills/code-task/SKILL.en.md +3 -1
  105. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  106. package/templates/.agents/skills/code-task/reference/report-template.en.md +4 -8
  107. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +4 -8
  108. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  109. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  110. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
  111. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
  112. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -0
  113. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -0
  114. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
  115. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
  116. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -11
  117. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -11
  118. package/templates/.agents/skills/create-task/SKILL.en.md +2 -0
  119. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  120. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -0
  121. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  122. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -0
  123. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  124. package/templates/.agents/skills/import-issue/SKILL.en.md +2 -0
  125. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  126. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -1
  127. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
  128. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -1
  129. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -1
  130. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +4 -8
  131. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +4 -8
  132. package/templates/.agents/skills/review-code/SKILL.en.md +13 -12
  133. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -12
  134. package/templates/.agents/skills/review-code/config/verify.en.json +1 -3
  135. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -3
  136. package/templates/.agents/skills/review-code/reference/output-templates.en.md +23 -46
  137. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +27 -48
  138. package/templates/.agents/skills/review-code/reference/report-template.en.md +8 -11
  139. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +8 -11
  140. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -2
  141. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -2
  142. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -1
  143. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -1
  144. package/templates/.agents/skills/review-plan/reference/report-template.en.md +5 -9
  145. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +5 -9
  146. package/templates/.agents/skills/review-pr/SKILL.en.md +2 -0
  147. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -0
  148. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  149. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  150. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  151. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  152. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -0
  153. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
  154. package/templates/.agents/templates/task.en.md +2 -2
  155. package/templates/.agents/templates/task.zh-CN.md +2 -2
@@ -6,12 +6,9 @@ import { execFileSync } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
8
  import {
9
- extractReviewBaseline,
10
9
  extractReviewTargetHead,
11
10
  extractReviewedHead,
12
11
  extractReviewDiffBase,
13
- extractReviewDiffFingerprint,
14
- extractReviewedSnapshotTree,
15
12
  findAuthoritativeReviewCodeArtifact,
16
13
  loadPostReviewConfig,
17
14
  parseReviewVerdict,
@@ -23,7 +20,6 @@ import { inspectPlatformPullRequest } from "../platform/pull-requests.ts";
23
20
  import { resolveMaterializedReviewedHeadRelation } from "../platform/change-request-git-evidence.ts";
24
21
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.ts";
25
22
  import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.ts";
26
- import { resolveBranchWorktree } from "../git/branch-worktree.ts";
27
23
  import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
28
24
  import { LEDGER_TERMINAL, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
29
25
  import type { LedgerRow } from "./ledger.ts";
@@ -33,12 +29,10 @@ import { inspectDecisionDetailDuplicates } from "./decision-details.ts";
33
29
  import { parseImplementationInputs, type ImplementationInput } from "./implementation-inputs.ts";
34
30
  import { scanVisibleMarkdown } from "./markdown.ts";
35
31
  import { loadVerificationConfig } from "./verification-config.ts";
36
- import { snapshotReview } from "../git/review-snapshot.ts";
37
32
  import { inspectActivityLog } from "./activity-log.ts";
38
33
  import { parseWorkflowWarnings } from "./workflow-warnings.ts";
39
34
  import { OrchestrationStateError, readRun } from "./orchestration.ts";
40
35
  import type { OrchestrationRun } from "./orchestration.ts";
41
- import { resolveDeliveryTarget } from "./delivery-target.ts";
42
36
  import { readPrDeliveryFact } from "./pr-delivery-fact.ts";
43
37
  import { validateLocalArtifact } from "./local-artifact-finalization.ts";
44
38
  import { getArtifactSchema } from "./artifact-schema.ts";
@@ -50,6 +44,8 @@ import { readManualValidationCompletion } from "./manual-validation-completion.t
50
44
  import { sha256File } from "./artifact-receipts.ts";
51
45
  import { summaryCommentState } from "../platform/pr-summary.ts";
52
46
  import { taskIssueIdentity } from "../platform/task-identities.ts";
47
+ import { hasArtifactCompletionFact, hasArtifactCompletionLog } from './completion-facts.ts';
48
+ import { inspectReviewIdentity } from './review-identity.ts';
53
49
 
54
50
  const TASK_ENUMS = {
55
51
  type: ["feature", "bugfix", "refactor", "docs", "chore"],
@@ -1159,12 +1155,16 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
1159
1155
  }
1160
1156
 
1161
1157
  const content = fs.readFileSync(resolvedArtifact.path, "utf8");
1158
+ if (!hasArtifactCompletionFact(task.content, resolvedArtifact.path, 'review-code.completed')) {
1159
+ return failResult("review-fact", `Review Code completion fact does not match ${path.basename(resolvedArtifact.path)}`);
1160
+ }
1161
+ if (!hasArtifactCompletionLog(task.content, path.basename(resolvedArtifact.path), 'review-code.completed')) {
1162
+ return failResult("review-fact", `Review Code completion entry does not reference ${path.basename(resolvedArtifact.path)}`);
1163
+ }
1162
1164
  const verdict = parseReviewVerdict(content);
1163
1165
  const reviewTargetHead = extractReviewTargetHead(content);
1164
1166
  const reviewReviewedHead = extractReviewedHead(content);
1165
1167
  const reviewDiffBase = extractReviewDiffBase(content);
1166
- const reviewedFingerprint = extractReviewDiffFingerprint(content);
1167
- const reviewedTree = extractReviewedSnapshotTree(content);
1168
1168
 
1169
1169
  if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
1170
1170
  return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
@@ -1179,86 +1179,20 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
1179
1179
  return failResult("review-fact", "Review fact must record target head, reviewed head, and diff base");
1180
1180
  }
1181
1181
 
1182
- let gitRoot;
1183
- let head;
1184
- let baseline;
1185
- let targetHead;
1186
- let diffBase;
1187
- try {
1188
- const taskRepositoryRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
1189
- gitRoot = reviewWorktreeForTask(taskRepositoryRoot, String(task.metadata.branch || "").trim()) ?? taskRepositoryRoot;
1190
- head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
1191
- const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
1192
- if (!target.ok) throw new Error(target.message);
1193
- const reviewedRef = reviewReviewedHead;
1194
- baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewedRef}^{commit}`], { encoding: "utf8" }).trim();
1195
- targetHead = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewTargetHead}^{commit}`], { encoding: "utf8" }).trim();
1196
- const computed = execFileSync("git", ["-C", gitRoot, "merge-base", baseline, targetHead], { encoding: "utf8" }).trim();
1197
- diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase}^{commit}`], { encoding: "utf8" }).trim();
1198
- if (diffBase !== computed) throw new Error('saved review diff base does not match merge-base(reviewed head, target head)');
1199
- } catch {
1200
- return blockedResult(
1201
- "review-fact",
1202
- `Unable to resolve saved review commits in the task repository; re-run review-code`
1203
- );
1204
- }
1205
-
1206
- if (baseline !== head) {
1207
- return failResult(
1208
- "review-fact",
1209
- `Reviewed head ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`
1210
- );
1211
- }
1212
-
1213
- let actualSnapshot;
1214
- try {
1215
- actualSnapshot = snapshotReview({
1216
- cwd: gitRoot,
1217
- mode: "worktree",
1218
- baseline,
1219
- diffBase,
1220
- globs: resolvePostReviewGlobs({}, loadPostReviewConfig(repositoryRoot))
1221
- });
1222
- } catch {
1223
- return blockedResult(
1224
- "review-fact",
1225
- `Unable to recompute reviewed diff fingerprint from baseline ${baseline.slice(0, 8)}; re-run review-code`
1226
- );
1227
- }
1228
-
1229
- if (actualSnapshot.fingerprint !== reviewedFingerprint) {
1230
- return failResult(
1231
- "review-fact",
1232
- `Reviewed diff fingerprint does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
1233
- );
1234
- }
1235
-
1236
- if (!reviewedTree || actualSnapshot.tree !== reviewedTree) {
1237
- return failResult(
1238
- "review-fact",
1239
- `Reviewed snapshot tree does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
1240
- );
1182
+ const identity = inspectReviewIdentity(taskDir, content, repositoryRoot);
1183
+ if (identity.status !== 'matched' || !identity.reviewedHead) {
1184
+ return failResult('review-fact', `Review identity is ${identity.status}: ${identity.message}; re-run review-code`);
1241
1185
  }
1186
+ const baseline = identity.reviewedHead;
1242
1187
 
1243
1188
  if (["通过", "Approved"].includes(verdict)) {
1244
1189
  const lastReviewedCommit = String(task.metadata.last_reviewed_commit || "").trim();
1245
- const cleanSnapshot = actualSnapshot.tree === execFileSync(
1246
- "git",
1247
- ["-C", gitRoot, "rev-parse", `${baseline}^{tree}`],
1248
- { encoding: "utf8" }
1249
- ).trim();
1250
- if (cleanSnapshot && lastReviewedCommit !== baseline) {
1190
+ if (lastReviewedCommit !== baseline) {
1251
1191
  return failResult(
1252
1192
  "review-fact",
1253
1193
  `Approved clean review must set task last_reviewed_commit to reviewed head ${baseline.slice(0, 8)}`
1254
1194
  );
1255
1195
  }
1256
- if (!cleanSnapshot && lastReviewedCommit) {
1257
- return failResult(
1258
- "review-fact",
1259
- "Approved review with uncommitted changes must remain unanchored until commit"
1260
- );
1261
- }
1262
1196
  }
1263
1197
 
1264
1198
  return passResult(
@@ -1267,13 +1201,6 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
1267
1201
  );
1268
1202
  }
1269
1203
 
1270
- function reviewWorktreeForTask(repositoryRoot: string, branch: string): string | null {
1271
- if (!branch) return null;
1272
- const worktree = resolveBranchWorktree(repositoryRoot, branch);
1273
- if (worktree) return worktree;
1274
- throw new Error(`Task branch '${branch}' is not checked out in a registered worktree`);
1275
- }
1276
-
1277
1204
  function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }: any): any {
1278
1205
  if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
1279
1206
  return { ok: true, sha: lastReviewedCommit };
@@ -134,46 +134,42 @@ function statusLabel(status: unknown): string {
134
134
  return status === 'fail' ? 'FAIL' : status === 'blocked' ? 'BLOCKED' : 'pass';
135
135
  }
136
136
 
137
- function renderPayload(payload: Record<string, unknown>): string {
138
- const lines: string[] = [];
139
- if (Array.isArray(payload.checks)) {
140
- const gateMode = typeof payload.gate === 'string';
141
- lines.push(gateMode
142
- ? `Verification: ${payload.gate} | Skill: ${payload.skill}`
143
- : `Check: ${payload.status} | Skill: ${payload.skill} | Type: ${payload.type}`, '');
144
- for (const check of payload.checks as Array<Record<string, unknown>>) {
145
- const raw = statusLabel(check.status);
146
- const effective = statusLabel(check.effectiveStatus ?? check.status);
147
- const reason = check.reason ?? 'CHECK_FAILED';
148
- const message = check.message ?? reason;
149
- lines.push(` [${effective}] ${check.checkId ?? check.type} (${check.classification ?? 'hard'}; raw ${raw}; reason ${reason}) - ${message}; ${check.action ?? 'Review validation output'}`);
150
- }
151
- const summary = payload.summary ?? (() => {
152
- const checks = payload.checks as Array<Record<string, unknown>>;
153
- const statuses = checks.map((check) => check.effectiveStatus ?? check.status);
154
- return `${statuses.filter((status) => status === 'pass').length} passed, ${statuses.filter((status) => status === 'fail').length} failed${statuses.includes('blocked') ? `, ${statuses.filter((status) => status === 'blocked').length} blocked` : ''}`;
155
- })();
156
- lines.push('', `Result: ${summary} - ${payload.action}`);
157
- } else {
158
- lines.push(`Check: ${payload.status} | Skill: ${payload.skill} | Type: ${payload.type}`, '');
159
- const raw = statusLabel(payload.status);
160
- const effective = statusLabel(payload.effectiveStatus ?? payload.status);
161
- const reason = payload.reason ?? 'CHECK_FAILED';
162
- lines.push(` [${effective}] ${payload.checkId ?? payload.type} (${payload.classification ?? 'hard'}; raw ${raw}; reason ${reason}) - ${payload.message ?? reason}; ${payload.action ?? 'Review validation output'}`);
163
- const status = payload.effectiveStatus ?? payload.status;
164
- const summary = status === 'pass' ? '1 passed, 0 failed'
165
- : status === 'blocked' ? '0 passed, 0 failed, 1 blocked' : '0 passed, 1 failed';
166
- const action = status === 'pass' ? 'Requested check passed'
167
- : status === 'blocked' ? `Resolve blocked ${payload.type} check and re-run check`
168
- : `Fix ${payload.type} issues and re-run check`;
169
- lines.push('', `Result: ${summary} - ${action}`);
170
- }
171
- return `${lines.join('\n')}\n`;
172
- }
173
-
174
137
  function renderTaskVerification(result: TaskVerificationResult): string {
175
138
  if (result.status === 'failed') return `Verification failed: ${result.error?.code ?? 'VERIFY_FAILED'} - ${result.error?.message ?? 'unknown error'}\n`;
176
- return result.invocations.map((invocation) => renderPayload(invocation.payload)).join('');
139
+ const target = `${result.event}${result.artifact ? ` (${result.artifact})` : ''}`;
140
+ const lines = [`Verification: ${result.status} | Target: ${target} | Skill: ${result.skill}`];
141
+ const checks = result.invocations.flatMap(({ payload }) => {
142
+ if (Array.isArray(payload.checks)) return payload.checks as Array<Record<string, unknown>>;
143
+ return [payload];
144
+ });
145
+ const counts = {
146
+ pass: checks.filter((check) => (check.effectiveStatus ?? check.status) === 'pass').length,
147
+ fail: checks.filter((check) => (check.effectiveStatus ?? check.status) === 'fail').length,
148
+ blocked: checks.filter((check) => (check.effectiveStatus ?? check.status) === 'blocked').length
149
+ };
150
+ const summary = `${counts.pass} passed, ${counts.fail} failed${counts.blocked > 0 ? `, ${counts.blocked} blocked` : ''}`;
151
+ lines.push('', `Result: ${summary}`);
152
+
153
+ for (const check of checks) {
154
+ const rawStatus = check.status;
155
+ const effectiveStatus = check.effectiveStatus ?? rawStatus;
156
+ const isSoftWarning = (rawStatus === 'fail' || rawStatus === 'blocked') && effectiveStatus === 'pass';
157
+ const reason = check.reason ?? 'CHECK_FAILED';
158
+ const message = check.message ?? reason;
159
+ const action = check.action ?? 'Review validation output';
160
+ const id = check.checkId ?? check.type;
161
+ const isHumanExemptionNotice = id === 'post-review-commit'
162
+ && typeof message === 'string'
163
+ && /\bhuman-decided (?:post-review )?exemption\b/i.test(message)
164
+ && /\bPRC-\d+\s*:/.test(message);
165
+ if (effectiveStatus === 'pass' && !isSoftWarning && !isHumanExemptionNotice) continue;
166
+ lines.push(isSoftWarning
167
+ ? `Warning: ${id} (${rawStatus === 'blocked' ? `raw BLOCKED; ` : ''}${reason}) - ${message}; ${action}`
168
+ : isHumanExemptionNotice
169
+ ? `Notice: ${id} - ${message}; ${action}`
170
+ : ` [${statusLabel(effectiveStatus)}] ${id} (${reason}) - ${message}; ${action}`);
171
+ }
172
+ return `${lines.join('\n')}\n`;
177
173
  }
178
174
 
179
175
  export { VERIFICATION_CATALOG, renderTaskVerification, verifyTaskEvent };
package/lib/task/write.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import crypto from 'node:crypto';
3
4
 
4
5
  import { isValidAgentInfraVersion, VERSION } from '../version.ts';
5
6
  import {
@@ -223,7 +224,7 @@ function errorDetails(error: unknown, fallback: TaskWriteErrorCode): TaskWriteEr
223
224
  };
224
225
  }
225
226
 
226
- function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): TaskWriteResult {
227
+ function writeTaskCore(request: TaskWriteRequest, options: TaskWriteOptions = {}): TaskWriteResult {
227
228
  const resolved = options.taskLocation
228
229
  ? {
229
230
  ok: true as const,
@@ -479,6 +480,119 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
479
480
  return { ...successBase, status: 'applied', changed: true };
480
481
  }
481
482
 
483
+ function archiveContentsHash(localDir: string): string {
484
+ const files: string[] = [];
485
+ const walk = (dir: string) => {
486
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
487
+ if (entry.name.includes('\n') || entry.name.includes('\r') || entry.name === 'manifest.md') {
488
+ throw new Error(`Archive source path is not valid: ${path.join(dir, entry.name)}`);
489
+ }
490
+ const target = path.join(dir, entry.name);
491
+ const stat = fs.lstatSync(target);
492
+ if (stat.isSymbolicLink()) throw new Error(`Archive source cannot contain symbolic links: ${target}`);
493
+ if (stat.isDirectory()) walk(target);
494
+ else if (stat.isFile()) {
495
+ if (entry.name !== 'contents.sha256') files.push(target);
496
+ }
497
+ else throw new Error(`Archive source cannot contain special files: ${target}`);
498
+ }
499
+ };
500
+ walk(localDir);
501
+ const rows = files
502
+ .map((file) => ({ file, relative: path.relative(localDir, file).split(path.sep).join('/') }))
503
+ .sort((left, right) => Buffer.compare(Buffer.from(left.relative), Buffer.from(right.relative)))
504
+ .map(({ file, relative }) => `${crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')} ${relative}`);
505
+ return `${rows.join('\n')}${rows.length ? '\n' : ''}`;
506
+ }
507
+
508
+ function writeArchiveContentsHash(localDir: string): void {
509
+ const contents = archiveContentsHash(localDir);
510
+ const hashPath = path.join(localDir, 'contents.sha256');
511
+ const tempPath = path.join(localDir, `.contents.sha256.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`);
512
+ try {
513
+ fs.writeFileSync(tempPath, contents, { flag: 'wx' });
514
+ fs.renameSync(tempPath, hashPath);
515
+ } catch (error) {
516
+ try {
517
+ fs.unlinkSync(tempPath);
518
+ } catch (cleanupError) {
519
+ if ((cleanupError as NodeJS.ErrnoException).code !== 'ENOENT') {
520
+ throw new Error(`${error instanceof Error ? error.message : String(error)}; failed to remove ${tempPath}: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
521
+ }
522
+ }
523
+ throw error;
524
+ }
525
+ }
526
+
527
+ function restoreArchivedTask(taskMdPath: string, contents: Buffer, mode: number): void {
528
+ const tempPath = `${taskMdPath}.${process.pid}.${Math.random().toString(36).slice(2)}.rollback`;
529
+ fs.writeFileSync(tempPath, contents, { flag: 'wx', mode });
530
+ try {
531
+ fs.renameSync(tempPath, taskMdPath);
532
+ } catch (error) {
533
+ try {
534
+ fs.unlinkSync(tempPath);
535
+ } catch (cleanupError) {
536
+ if ((cleanupError as NodeJS.ErrnoException).code !== 'ENOENT') {
537
+ throw new Error(`${error instanceof Error ? error.message : String(error)}; failed to remove ${tempPath}: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
538
+ }
539
+ }
540
+ throw error;
541
+ }
542
+ }
543
+
544
+ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): TaskWriteResult {
545
+ const resolved = options.taskLocation
546
+ ? {
547
+ ok: true as const,
548
+ repoRoot: options.taskLocation.repoRoot,
549
+ taskId: options.taskLocation.taskId,
550
+ taskDir: path.dirname(options.taskLocation.taskMdPath),
551
+ taskMdPath: options.taskLocation.taskMdPath,
552
+ state: options.taskLocation.state
553
+ }
554
+ : resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
555
+ if (!resolved.ok) {
556
+ return failure(request, { taskId: resolved.taskId, taskMdPath: null, actualState: null }, resolved.code, resolved.message);
557
+ }
558
+ if (resolved.state !== 'archive' || request.expectedState !== 'archive' || request.dryRun) {
559
+ return writeTaskCore(request, { ...options, taskLocation: {
560
+ repoRoot: resolved.repoRoot,
561
+ taskId: resolved.taskId,
562
+ taskMdPath: resolved.taskMdPath,
563
+ state: resolved.state
564
+ } });
565
+ }
566
+
567
+ const identity = { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, actualState: resolved.state };
568
+ try {
569
+ const originalTask = fs.readFileSync(resolved.taskMdPath);
570
+ const originalMode = fs.statSync(resolved.taskMdPath).mode;
571
+ archiveContentsHash(resolved.taskDir);
572
+ const result = writeTaskCore(request, { ...options, taskLocation: {
573
+ repoRoot: resolved.repoRoot,
574
+ taskId: resolved.taskId,
575
+ taskMdPath: resolved.taskMdPath,
576
+ state: resolved.state
577
+ } });
578
+ if (result.status === 'applied') {
579
+ try {
580
+ writeArchiveContentsHash(resolved.taskDir);
581
+ } catch (error) {
582
+ try {
583
+ restoreArchivedTask(resolved.taskMdPath, originalTask, originalMode);
584
+ } catch (rollbackError) {
585
+ throw new Error(`Archive checksum update failed: ${error instanceof Error ? error.message : String(error)}; task rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
586
+ }
587
+ throw error;
588
+ }
589
+ }
590
+ return result;
591
+ } catch (error) {
592
+ return failure(request, identity, 'TASK_READ_FAILED', error instanceof Error ? error.message : String(error));
593
+ }
594
+ }
595
+
482
596
  export { writeTask, captureTaskWriteMetadata, canonicalTimestamp };
483
597
  export type {
484
598
  FrontmatterMutation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Antigravity CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,6 +14,7 @@ so you can quickly find "which ones to read" without opening each file.
14
14
  - [`debugging-guide.md`](debugging-guide.md) — Structured debugging flow: gather evidence → form hypothesis → verify hypothesis → fix the root cause; no blind patch-and-retry.
15
15
  - [`compatibility-policy.md`](compatibility-policy.md) — Compatibility by exception: admission evidence, implementation boundaries, exit conditions, and lifecycle review requirements.
16
16
  - [`evidence-reporting.md`](evidence-reporting.md) — Minimum evidence semantics for state checks, successful summaries, exceptional results, identity fields, and sensitive data boundaries.
17
+ - [`validation-output.md`](validation-output.md) — User-facing validation summaries, failure diagnostics, soft warnings, and consumer boundaries.
17
18
  - [`sync-content-generation.md`](sync-content-generation.md) — Producer-side Markdown constraints for task and lifecycle content synchronized to Issues.
18
19
  - [`decision-qualification.md`](decision-qualification.md) — Constraint, candidate, human-confirmation, and six-artifact qualification-audit contract.
19
20
 
@@ -13,6 +13,7 @@
13
13
  - [`debugging-guide.md`](debugging-guide.md) — 结构化调试流程:收集证据→形成假设→验证假设→修复根因,禁止盲目改代码重试。
14
14
  - [`compatibility-policy.md`](compatibility-policy.md) — 兼容性默认关闭:准入证据、实现边界、退出条件与生命周期审查要求。
15
15
  - [`evidence-reporting.md`](evidence-reporting.md) — 生命周期报告的状态核对、成功摘要、异常证据、身份字段和敏感信息边界。
16
+ - [`validation-output.md`](validation-output.md) — 面向用户的校验状态摘要、失败诊断、软警告及消费边界。
16
17
  - [`sync-content-generation.md`](sync-content-generation.md) — 同步到 Issue 的任务和生命周期 Markdown 生成约束。
17
18
  - [`decision-qualification.md`](decision-qualification.md) — 约束、候选、人工确认和六类 artifact 资格审计契约。
18
19
 
@@ -6,7 +6,8 @@ Any analysis, plan, implementation, or review that decides whether a human decis
6
6
 
7
7
  - The `### Constraints` and `### Candidate and Rejected Options` tables in `task.md` are the only constraint and candidate fact source.
8
8
  - The constraint table uses `constraint_id`, `statement`, `status`, `authority`, `source`, `evidence`, `derived_from`, and `approval_evidence`; the candidate table uses `candidate_id`, `statement`, `status`, `constraint_ids`, `impact`, and `evidence`.
9
- - All six artifact families must include `## Qualification Audit` with actual constraint dependencies, candidate qualification, classification results, upstream relations, and a dependency snapshot. Each upstream relation records family, file name, round, and SHA-256.
9
+ - Qualification audits are optional in all six lifecycle artifact families. When `## Qualification Audit` is present, it must contain three decision tables (constraint dependencies, candidate qualification, classification results) and one snapshot row (`task_input_digest`, `non_constraint_input_digest`). Candidate qualification must cover the complete candidate set in the task.
10
+ - Lifecycle input relationships belong in the `Artifact Lifecycle Receipts` section of `task.md`. Freeze each stage's actual artifact inputs and SHA-256 values at stage start, then verify and record each input edge at completion. Do not duplicate lifecycle edges in qualification audits.
10
11
 
11
12
  ## Status and confirmation
12
13
 
@@ -17,6 +18,6 @@ Any analysis, plan, implementation, or review that decides whether a human decis
17
18
 
18
19
  ## Invalidation and review
19
20
 
20
- When a constraint changes, only artifacts declaring the affected `C-N` can be direct invalidation seeds; the change then follows the real downstream closure. All six families, including reviews, are symmetric. If snapshots, relations, or change classification cannot prove a pure constraint change, use the existing full invalidation path. The newly completed source artifact, its receipt, and its derived snapshot are excluded from the old target graph.
21
+ When a lifecycle artifact is replaced, start from the old artifact and follow verified input edges in the task receipts to find and invalidate every downstream consumer. Exclude the new artifact from the old graph. If any relation is missing, identity or SHA-256 does not match, or the graph cannot be verified, use the existing static downstream invalidation scope.
21
22
 
22
- Missing or unknown references, digest mismatches, dangling relations, and cycles fail closed. Formatting-only changes must not change semantic digests; changes to meaning, provenance, status, candidates, or upstream identity require a new audit.
23
+ When a qualification audit is present, missing or unknown references and digest mismatches block finalization. Audits in the old format with an upstream-relations table must be regenerated in the current format. Formatting-only changes must not change semantic digests; changes to meaning, provenance, status, or candidates require a new audit.
@@ -6,7 +6,8 @@
6
6
 
7
7
  - `task.md` 的 `### 约束` 和 `### 候选与否决方案` 是唯一约束/候选事实源。
8
8
  - 约束表必须使用 `constraint_id`、`statement`、`status`、`authority`、`source`、`evidence`、`derived_from`、`approval_evidence`;候选表必须使用 `candidate_id`、`statement`、`status`、`constraint_ids`、`impact`、`evidence`。
9
- - 六类阶段产物都要在 `## 资格审计` 中声明实际约束依赖、候选资格、分类结果、上游关系和依赖快照。上游关系必须保存 artifact 的 family、文件名、round 和 SHA-256。
9
+ - 六类阶段产物可以省略资格审计。若填写 `## 资格审计`,必须包含约束依赖、候选资格、分类结果三张决策表,以及一行资格快照(`task_input_digest`、`non_constraint_input_digest`)。候选资格必须覆盖任务中的完整候选集合。
10
+ - 生命周期输入关系由 `task.md` 的“产物生命周期收据”记录;阶段开始时冻结实际输入 artifact 和 SHA-256,完成时验证后写入每条输入边。不要在资格审计中复制生命周期关系。
10
11
 
11
12
  ## 状态与确认
12
13
 
@@ -17,6 +18,6 @@
17
18
 
18
19
  ## 失效与审查
19
20
 
20
- 约束变化时,只有声明受影响 `C-N` 的 artifact 才能成为直接失效种子,并沿真实上游关系的下游闭包传播;六类 artifact(含 review)对等处理。快照、关系图或变化分类无法证明为纯约束变化时,回退既有全量失效路径。新完成的 source artifact、自身 receipt 和派生快照不作为旧图目标。
21
+ 替换某个阶段产物时,系统从被替换的旧 artifact 出发,沿任务收据中验证过的输入边计算所有下游消费者,并使旧消费者失效;新产物不进入旧图。若任一关系缺失、身份不匹配、SHA-256 不匹配或图无法验证,则使用既有静态下游失效范围。
21
22
 
22
- 缺失、未知引用、digest 不匹配、悬空关系或环都必须 fail closed。排版变化不应改变语义 digest;语义、来源、状态、候选或上游 identity 变化必须触发重新审计。
23
+ 资格审计存在时,缺失、未知引用或 digest 不匹配都必须拒绝收尾。旧版包含“上游关系”表的资格审计不再读取,应按当前格式重新生成。排版变化不应改变语义 digest;语义、来源、状态或候选变化必须触发重新审计。
@@ -21,8 +21,7 @@ Record a reproducible command or entry point, the exact file/line/object/version
21
21
  - Preserve artifact SHA-256, semantic digest, review head, receipt, request-id, snapshot tree, and platform marker exactly.
22
22
  - The Issue continues to mirror the complete report; the report must not depend on logs accessible only on the local machine.
23
23
  - Manual validation keeps its stricter basename-only and sanitized-result rules; this shared rule does not expand public fields.
24
- - A `$ ` command line satisfies the structural gate but does not require complete successful output.
25
24
 
26
25
  ## Evidence Pairing
27
26
 
28
- Pair every verification claim with a `$ ` command and a proportionate result summary. Include a decisive raw excerpt only for failure, blocking, or dispute conclusions. When a command contains sensitive arguments, use its allowed public name or a redacted command.
27
+ Pair every verification claim with an executable command and a proportionate result summary. Include a decisive raw excerpt only for failure, blocking, or dispute conclusions. When a command contains sensitive arguments, use its allowed public name or a redacted command.
@@ -21,8 +21,7 @@
21
21
  - artifact SHA-256、semantic digest、review head、receipt、request-id、快照树和平台 marker 必须精确保留。
22
22
  - Issue 继续镜像报告全文;报告不得依赖只有本机可访问的日志。
23
23
  - 人工验证继续使用 basename-only、sanitized result 等更严格规则;共享规则不扩大可公开字段。
24
- - `$ ` 命令行标记满足结构门禁,但不要求附带完整成功输出。
25
24
 
26
25
  ## 证据配对
27
26
 
28
- 报告中的每个验证断言都应配一个 `$ ` 命令和相称结果摘要;只有失败、阻塞或争议结论才附决定性原文摘录。命令含敏感参数时使用允许公开的命令名或脱敏命令。
27
+ 报告中的每个验证断言都应配一个可执行命令和相称结果摘要;只有失败、阻塞或争议结论才附决定性原文摘录。命令含敏感参数时使用允许公开的命令名或脱敏命令。
@@ -0,0 +1,19 @@
1
+ # Validation Output Summary
2
+
3
+ This rule governs how `task-verify --format text` and lifecycle skills present validation results to users. It does not change validation logic, structured results, or persisted evidence requirements.
4
+
5
+ ## Overall Summary
6
+
7
+ - Show the overall status, validation target, and pass, fail, and blocked counts.
8
+ - For successful results, show only the summary; do not list passing check names.
9
+ - Keep failure and blocked states distinct. List only effective failed or blocked checks, with a stable identifier, concise reason, and actionable step.
10
+ - A soft check whose raw status is failed but effective status is pass must not count as a failure. When a user-facing notice is needed, show a concise warning with the necessary reason and action.
11
+ - A soft check whose raw status is blocked but effective status is pass must also show a concise warning that preserves the raw blocked state, necessary reason, and action.
12
+ - If a passing result carries a consumed human exemption or other required exception evidence, show a concise notice that preserves the exception evidence; continue to count the check by its effective status.
13
+
14
+ ## Consumer Boundaries
15
+
16
+ - Do not copy complete successful stdout into the final response. Read structured details or rerun the command when passing check details are needed.
17
+ - Consume structured host finalization results through their status, receipt, and warning projection; do not present receipt contents as the user summary.
18
+ - Persisted lifecycle reports continue to follow `evidence-reporting.md`, recording reproducible scope, status, and necessary evidence. Issue artifact comments continue to sync the required report content.
19
+ - JSON payloads, check order, check set, effective-status decisions, and exit codes remain unchanged.
@@ -0,0 +1,19 @@
1
+ # 校验输出摘要规则
2
+
3
+ 本规则约束 `task-verify --format text` 及生命周期技能向用户展示校验结果的方式。它不改变校验逻辑、结构化结果或持久化证据要求。
4
+
5
+ ## 总体摘要
6
+
7
+ - 展示总体状态、校验目标,以及通过、失败和阻塞计数。
8
+ - 成功结果只展示摘要,不列出通过项名称。
9
+ - 失败与阻塞保持不同状态;只列出有效失败或阻塞项的稳定标识、简明原因和可执行动作。
10
+ - raw 状态失败但 effective 状态通过的软检查,不得计入失败;需要提醒用户时用简短警告及必要原因和动作说明。
11
+ - raw 状态阻塞但 effective 状态通过的软检查,也必须显示简短警告,并保留 raw 阻塞状态、必要原因和动作说明。
12
+ - 通过结果若包含已消费的人工作出豁免或其他必须保留的例外证据,应显示简短提示并保留例外证据;该检查仍按 effective 状态计数。
13
+
14
+ ## 消费边界
15
+
16
+ - 不把完整成功 stdout 复制到最终回复。需要检查通过项明细时,按需读取结构化结果或重新运行命令。
17
+ - 宿主 finalization 等结构化结果按其状态、receipt 和 warning projection 消费;不要将 receipt 原文当作用户摘要。
18
+ - 持久化生命周期报告继续遵循 `evidence-reporting.md`,记录可复现范围、状态和必要证据;Issue artifact 评论继续同步所需报告内容。
19
+ - JSON payload、检查顺序、检查集合、effective status 判定和退出码均不受本规则影响。
@@ -21,7 +21,7 @@ The analysis artifact must record `Path`, `Basis`, `Unmet Higher-path Conditions
21
21
 
22
22
  Before generating the analysis report, read `.agents/rules/evidence-reporting.md`. Record the command, target scope, status or structured result, actual result, and uncovered parts for state checks and successful checks; include decisive raw excerpts only for failures, blocking conditions, or disputes.
23
23
 
24
- - When evaluating candidate qualification or `HD-N`, read `.agents/rules/decision-qualification.md`, audit normalized task constraints/candidates, and record the five qualification-audit tables in the analysis artifact; unknown or unconfirmed constraints must not automatically exclude a candidate
24
+ - When evaluating candidate qualification or `HD-N`, read `.agents/rules/decision-qualification.md`, audit normalized task constraints/candidates, and record the three qualification-audit decision tables and snapshot in the analysis artifact; unknown or unconfirmed constraints must not automatically exclude a candidate
25
25
  - This skill only outputs a requirements analysis document (`analysis.md` or `analysis-r{N}.md`) and does not modify any business code
26
26
  - Base the analysis strictly on the existing task input, requirements, context, and source information in `task.md`
27
27
  - Before generating task or lifecycle Markdown that will be synchronized to an Issue, read `.agents/rules/sync-content-generation.md` and apply its producer-side constraints; the sync path does not parse or rewrite the body
@@ -258,6 +258,8 @@ Run the verification gate to confirm the task artifact and sync state are valid:
258
258
  agent-infra-internal task-verify {task-id} analyze.completed --artifact {analysis-artifact} --format text
259
259
  ```
260
260
 
261
+ Present the verification summary per `.agents/rules/validation-output.md`, retaining necessary failure, blocked, and soft-warning diagnostics.
262
+
261
263
  Handle the result as follows:
262
264
  - exit code 0 (all checks passed) -> continue to the "Inform User" step
263
265
  - exit code 1 (validation failed) -> fix the reported issues and run the gate again
@@ -21,7 +21,7 @@ description: >
21
21
 
22
22
  生成分析报告时,先读取 `.agents/rules/evidence-reporting.md`。状态核对和成功检查记录命令、目标范围、状态/结构化结果、实际结果和未覆盖部分;失败、阻塞或争议才附决定性原文摘录。
23
23
 
24
- - 涉及候选资格或 `HD-N` 判断时,先读取 `.agents/rules/decision-qualification.md`,基于 task.md 规范化约束/候选完成资格审计,并在分析产物记录五张资格审计表;不得把来源不明或未确认约束自动升级为排除条件
24
+ - 涉及候选资格或 `HD-N` 判断时,先读取 `.agents/rules/decision-qualification.md`,基于 task.md 规范化约束/候选完成资格审计,并在分析产物记录三张资格审计决策表和资格快照;不得把来源不明或未确认约束自动升级为排除条件
25
25
  - 本技能仅产出需求分析文档(`analysis.md` 或 `analysis-r{N}.md`)—— 不修改任何业务代码
26
26
  - 严格基于 `task.md` 中已有的任务输入、需求、上下文和来源信息展开分析
27
27
  - 生成会同步到 Issue 的任务或生命周期 Markdown 前,先读取 `.agents/rules/sync-content-generation.md` 并遵循其中的生成端约束;同步端不解析或改写正文
@@ -201,7 +201,7 @@ agent-infra-internal task-artifact {task-id} init --family analysis --artifact {
201
201
 
202
202
  ## 资格审计
203
203
 
204
- > 按 `.agents/rules/decision-qualification.md` 填写约束依赖、候选资格、分类结果、上游关系、依赖快照五张表;没有 artifact 上游时保留空表头,不虚构关系。
204
+ > 仅当本阶段实际判断候选资格、约束依赖或人工裁决资格时填写本节,并按 `.agents/rules/decision-qualification.md` 完整记录三张决策表和资格快照;否则删除整个审计段。
205
205
 
206
206
  ## 假设
207
207
 
@@ -263,7 +263,7 @@ agent-infra-internal task-verify {task-id} analyze.completed --artifact {analysi
263
263
  - 退出码 1(校验失败)-> 根据输出修复问题后重新运行校验
264
264
  - 退出码 2(网络中断)-> 停止执行并告知用户需要人工介入
265
265
 
266
- 将校验输出保留在回复中作为当次验证输出。没有当次校验输出,不得声明完成。
266
+ 按 `.agents/rules/validation-output.md` 展示当次校验摘要;没有当次校验输出,不得声明完成。
267
267
 
268
268
  ### 9. 告知用户
269
269
 
@@ -7,7 +7,7 @@ description: >
7
7
 
8
8
  # Archive Completed Tasks
9
9
 
10
- Move completed tasks from `.agents/workspace/completed/` into `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/` and rebuild a three-level archive index:
10
+ Archive completed tasks from `.agents/workspace/completed/` into `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/local/` and rebuild a three-level archive index. Local task materials live in `local/` with a path-sorted `contents.sha256`; the TASK root contains no ordinary files.
11
11
  - root manifest: `.agents/workspace/archive/manifest.md`
12
12
  - yearly manifest: `.agents/workspace/archive/YYYY/manifest.md`
13
13
  - monthly manifest: `.agents/workspace/archive/YYYY/MM/manifest.md`
@@ -32,7 +32,7 @@ bash .agents/skills/archive-tasks/scripts/archive-tasks.sh [--days N | --before
32
32
 
33
33
  The script is responsible for:
34
34
  - reading `completed_at` from `task.md` frontmatter and falling back to `updated_at`
35
- - moving task directories directly into `YYYY/MM/DD/TASK-xxx/` without compression
35
+ - storing local task materials under `YYYY/MM/DD/TASK-xxx/local/` without compression
36
36
  - skipping already archived, missing, or malformed tasks
37
37
  - rebuilding root, yearly, and monthly manifests from all archived tasks
38
38
  - printing an archive and skip summary
@@ -7,7 +7,7 @@ description: >
7
7
 
8
8
  # 归档已完成任务
9
9
 
10
- 将 `.agents/workspace/completed/` 中的已完成任务移动到 `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/`,并重建三级归档索引:
10
+ 将 `.agents/workspace/completed/` 中的已完成任务归档到 `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/local/`,并重建三级归档索引。TASK 本地材料位于 `local/`,含按路径排序的 `contents.sha256`;TASK 根目录不存放普通文件。
11
11
  - 根 manifest:`.agents/workspace/archive/manifest.md`
12
12
  - 年 manifest:`.agents/workspace/archive/YYYY/manifest.md`
13
13
  - 月 manifest:`.agents/workspace/archive/YYYY/MM/manifest.md`
@@ -32,7 +32,7 @@ bash .agents/skills/archive-tasks/scripts/archive-tasks.sh [--days N | --before
32
32
 
33
33
  脚本负责:
34
34
  - 解析 `task.md` frontmatter 中的 `completed_at`(缺失时回退到 `updated_at`)
35
- - 按 `YYYY/MM/DD/TASK-xxx/` 目录直接移动任务,不压缩
35
+ - 按 `YYYY/MM/DD/TASK-xxx/local/` 保存任务本地材料,不压缩
36
36
  - 跳过已归档、缺少元数据或不存在的任务
37
37
  - 全量重建根 / 年 / 月三级 manifest
38
38
  - 输出归档与跳过摘要