@fitlab-ai/agent-infra 0.9.15 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
  2. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  3. package/dist/lib/fs/durable-write.js +2 -0
  4. package/dist/lib/internal/cli-route-inventory.js +1 -1
  5. package/dist/lib/internal/manual-validation.js +17 -8
  6. package/dist/lib/internal/task-artifact.js +1 -1
  7. package/dist/lib/internal/task-lifecycle.js +2 -2
  8. package/dist/lib/internal/task-operation-registry.js +0 -1
  9. package/dist/lib/internal/task-review.js +4 -4
  10. package/dist/lib/platform/issue-comments.js +16 -10
  11. package/dist/lib/platform/issues.js +1 -1
  12. package/dist/lib/platform/pr-change-report.js +5 -4
  13. package/dist/lib/platform/pr-summary.js +64 -32
  14. package/dist/lib/platform/pull-requests.js +3 -3
  15. package/dist/lib/platform/task-identities.js +2 -9
  16. package/dist/lib/platform/verification-sync.js +6 -2
  17. package/dist/lib/pr-review/evidence-grading.js +4 -8
  18. package/dist/lib/sandbox/commands/rm.js +21 -2
  19. package/dist/lib/sandbox/control/server.js +29 -9
  20. package/dist/lib/sandbox/control/state.js +7 -1
  21. package/dist/lib/sandbox/control/workflow-executor.js +4 -14
  22. package/dist/lib/sandbox/index.js +1 -2
  23. package/dist/lib/sandbox/removal.js +116 -1011
  24. package/dist/lib/sandbox/workspace-identity.js +17 -1
  25. package/dist/lib/task/artifact-command.js +6 -28
  26. package/dist/lib/task/artifact-operations.js +16 -230
  27. package/dist/lib/task/artifact-recovery.js +430 -0
  28. package/dist/lib/task/artifact-repair-intent.js +73 -39
  29. package/dist/lib/task/commit-operation.js +31 -4
  30. package/dist/lib/task/control-authority.js +38 -7
  31. package/dist/lib/task/control-recovery.js +14 -2
  32. package/dist/lib/task/create-service.js +1 -8
  33. package/dist/lib/task/delegation-receipts.js +64 -3
  34. package/dist/lib/task/events.js +164 -22
  35. package/dist/lib/task/human-override.js +5 -8
  36. package/dist/lib/task/intermediate-cleanup.js +66 -8
  37. package/dist/lib/task/lifecycle-recovery.js +549 -0
  38. package/dist/lib/task/lifecycle.js +12 -3
  39. package/dist/lib/task/local-artifact-finalization.js +148 -123
  40. package/dist/lib/task/manual-validation-receipt.js +5 -1
  41. package/dist/lib/task/orchestration.js +52 -2
  42. package/dist/lib/task/pr-delivery-fact.js +12 -56
  43. package/dist/lib/task/recovery-warning.js +19 -0
  44. package/dist/lib/task/review-command.js +1 -0
  45. package/dist/lib/task/review-finalization.js +104 -77
  46. package/dist/lib/task/short-id.js +15 -7
  47. package/dist/lib/task/task-execution-lock.js +1 -1
  48. package/dist/lib/task/verification-engine.js +3 -14
  49. package/dist/lib/task/workflow-command.js +0 -1
  50. package/dist/lib/version.js +1 -11
  51. package/dist/lifecycle-build-manifest.json +3 -3
  52. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
  53. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  54. package/lib/fs/durable-write.ts +1 -0
  55. package/lib/internal/cli-route-inventory.ts +1 -1
  56. package/lib/internal/manual-validation.ts +17 -8
  57. package/lib/internal/task-artifact.ts +1 -1
  58. package/lib/internal/task-lifecycle.ts +4 -3
  59. package/lib/internal/task-operation-registry.ts +0 -1
  60. package/lib/internal/task-review.ts +4 -4
  61. package/lib/platform/issue-comments.ts +18 -10
  62. package/lib/platform/issues.ts +1 -1
  63. package/lib/platform/pr-change-report.ts +7 -5
  64. package/lib/platform/pr-summary.ts +64 -32
  65. package/lib/platform/pull-requests.ts +3 -3
  66. package/lib/platform/task-identities.ts +3 -14
  67. package/lib/platform/verification-sync.ts +6 -2
  68. package/lib/pr-review/evidence-grading.ts +4 -9
  69. package/lib/sandbox/commands/rm.ts +34 -2
  70. package/lib/sandbox/control/protocol.ts +5 -0
  71. package/lib/sandbox/control/server.ts +30 -10
  72. package/lib/sandbox/control/state.ts +9 -1
  73. package/lib/sandbox/control/workflow-executor.ts +4 -14
  74. package/lib/sandbox/index.ts +1 -2
  75. package/lib/sandbox/removal.ts +126 -1128
  76. package/lib/sandbox/workspace-identity.ts +19 -3
  77. package/lib/task/artifact-command.ts +8 -27
  78. package/lib/task/artifact-operations.ts +21 -251
  79. package/lib/task/artifact-recovery.ts +519 -0
  80. package/lib/task/artifact-repair-intent.ts +99 -51
  81. package/lib/task/commit-operation.ts +35 -4
  82. package/lib/task/control-authority.ts +47 -14
  83. package/lib/task/control-recovery.ts +13 -1
  84. package/lib/task/create-service.ts +1 -10
  85. package/lib/task/delegation-receipts.ts +73 -2
  86. package/lib/task/events.ts +187 -23
  87. package/lib/task/human-override.ts +5 -8
  88. package/lib/task/intermediate-cleanup.ts +50 -12
  89. package/lib/task/lifecycle-recovery.ts +691 -0
  90. package/lib/task/lifecycle.ts +13 -4
  91. package/lib/task/local-artifact-finalization.ts +176 -132
  92. package/lib/task/manual-validation-receipt.ts +5 -1
  93. package/lib/task/orchestration.ts +65 -0
  94. package/lib/task/pr-delivery-fact.ts +15 -62
  95. package/lib/task/recovery-warning.ts +21 -0
  96. package/lib/task/review-command.ts +1 -0
  97. package/lib/task/review-finalization.ts +106 -87
  98. package/lib/task/short-id.ts +27 -5
  99. package/lib/task/task-execution-lock.ts +1 -1
  100. package/lib/task/verification-engine.ts +3 -17
  101. package/lib/task/verification-types.ts +0 -2
  102. package/lib/task/workflow-command.ts +0 -1
  103. package/lib/version.ts +1 -15
  104. package/package.json +1 -1
  105. package/templates/.agents/rules/create-issue.en.md +1 -1
  106. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  107. package/templates/.agents/rules/issue-sync.en.md +4 -2
  108. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  109. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  110. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  111. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  112. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  113. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  114. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  115. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  116. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  117. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  118. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  119. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  120. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  121. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  122. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  123. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  124. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  125. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  126. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  127. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  130. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  131. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  132. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  133. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  134. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  135. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  136. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  137. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  138. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  139. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  140. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  141. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  142. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  143. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  144. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  145. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  146. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  147. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  148. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  149. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  150. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  151. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  152. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  153. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  154. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  155. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  156. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  157. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  158. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  159. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  160. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  161. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  162. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  163. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  164. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  165. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  166. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  167. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  168. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  169. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  170. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  171. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  172. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  173. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  174. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  175. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  176. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  177. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  178. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  179. package/templates/.agents/templates/task.en.md +1 -1
  180. package/templates/.agents/templates/task.zh-CN.md +1 -1
  181. package/templates/.claude/commands/import-issue.en.md +1 -1
  182. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  183. package/templates/.claude/commands/restore-task.en.md +1 -1
  184. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
@@ -98,7 +98,23 @@ export function resolveSandboxCleanupTarget(requestedRef, repoRoot, options = {}
98
98
  throw new Error(`Invalid task short id '${requestedRef}': task short ids must use bare digits`);
99
99
  }
100
100
  if (TASK_ID_RE.test(requestedRef)) {
101
- const task = resolveTaskWorkspace(requestedRef, repoRoot);
101
+ let task;
102
+ try {
103
+ task = resolveTaskWorkspace(requestedRef, repoRoot);
104
+ }
105
+ catch (error) {
106
+ if (!(error instanceof Error && error.message === `Task not found: ${requestedRef}`))
107
+ throw error;
108
+ const branch = options.resolveMissingTask?.(requestedRef);
109
+ if (!branch)
110
+ throw error;
111
+ return {
112
+ requestedRef,
113
+ branch,
114
+ workspace: { mode: 'task-bound', taskId: requestedRef },
115
+ taskState: 'unknown'
116
+ };
117
+ }
102
118
  if (!options.allowProtected && (task.state === 'blocked' || task.state === 'archive')) {
103
119
  throw new Error(`SANDBOX_CLEANUP_STATE_UNSUPPORTED: task ${requestedRef} is ${task.state}`);
104
120
  }
@@ -1,24 +1,21 @@
1
1
  import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { readStableFileSync } from '../host-control/secure-fs.js';
4
2
  import { resolveArtifactContext, hasOpenArtifactRound } from './artifact-lifecycle.js';
5
3
  import { parseArtifactName } from './artifact-name.js';
6
4
  import { resolveTaskRef } from './resolve-ref.js';
7
5
  import { getArtifactSchema } from './artifact-schema.js';
8
6
  import { finalizeLocalArtifact } from './local-artifact-finalization.js';
9
- import { applyArtifactRepair, initializeArtifactSkeleton, inspectArtifactStructure } from './artifact-operations.js';
7
+ import { initializeArtifactSkeleton } from './artifact-operations.js';
10
8
  /** One option contract for the CLI and trusted projection executor. */
11
9
  export function parseArtifactCommand(args) {
12
10
  const [taskRef, operation] = args;
13
- if (!taskRef || !['inspect', 'init', 'repair', 'finalize-local'].includes(operation ?? '')) {
11
+ if (!taskRef || !['inspect', 'init', 'finalize-local'].includes(operation ?? '')) {
14
12
  throw new Error('task ref and a supported artifact operation are required');
15
13
  }
16
14
  const fields = {};
17
15
  const allowed = {
18
16
  inspect: ['--family'],
19
17
  init: ['--family', '--artifact', '--locale'],
20
- repair: ['--family', '--artifact', '--expected-sha256', '--expected-semantic-digest'],
21
- 'finalize-local': ['--family', '--artifact']
18
+ 'finalize-local': ['--family', '--artifact', '--recovery-id']
22
19
  }[operation];
23
20
  for (let index = 2; index < args.length; index += 1) {
24
21
  const flag = args[index];
@@ -38,18 +35,14 @@ export function parseArtifactCommand(args) {
38
35
  const locale = fields['--locale'];
39
36
  if (locale !== undefined && locale !== 'zh-CN' && locale !== 'en')
40
37
  throw new Error("option '--locale' must be 'zh-CN' or 'en'");
41
- if (operation === 'repair' && !['--expected-sha256', '--expected-semantic-digest'].every((flag) => /^[a-f0-9]{64}$/u.test(fields[flag] ?? ''))) {
42
- throw new Error('repair requires lowercase 64-character digests');
43
- }
44
38
  return {
45
39
  taskRef, operation: operation, family: fields['--family'],
46
- artifact: fields['--artifact'] ?? '', locale,
47
- expectedSha256: fields['--expected-sha256'] ?? '', expectedSemanticDigest: fields['--expected-semantic-digest'] ?? ''
40
+ artifact: fields['--artifact'] ?? '', locale, recoveryId: fields['--recovery-id']
48
41
  };
49
42
  }
50
43
  /** Resolve authoritative task metadata separately from a trusted candidate directory. */
51
44
  export function executeArtifactCommand(command, options = {}) {
52
- const { taskRef, operation, family, artifact, locale, expectedSha256, expectedSemanticDigest } = command;
45
+ const { taskRef, operation, family, artifact, locale, recoveryId } = command;
53
46
  const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
54
47
  if (operation === 'inspect') {
55
48
  const result = resolveArtifactContext(taskRef, family, { repoRoot: options.repoRoot });
@@ -78,24 +71,9 @@ export function executeArtifactCommand(command, options = {}) {
78
71
  }
79
72
  return { ...initializeArtifactSkeleton({ ...input, ...(locale ? { locale } : {}) }), ...identity };
80
73
  }
81
- if (operation === 'repair') {
82
- let content;
83
- try {
84
- content = readStableFileSync(path.join(taskDir, artifact), { maxBytes: 1024 * 1024 }).bytes.toString('utf8');
85
- }
86
- catch (error) {
87
- return fail('ARTIFACT_REPAIR_TARGET_INVALID', String(error));
88
- }
89
- const inspection = inspectArtifactStructure(content, schema);
90
- if (!inspection.repair) {
91
- return { ...fail('ARTIFACT_REPAIR_UNSAFE', inspection.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ') || 'no deterministic structural repair is available'),
92
- artifact, family, diagnostics: inspection.diagnostics };
93
- }
94
- return { ...applyArtifactRepair({ ...input, expectedSha256, expectedSemanticDigest, operation: inspection.repair }), ...identity };
95
- }
96
74
  if (family !== 'analysis' && family !== 'plan' && family !== 'code') {
97
75
  return fail('ARTIFACT_PAYLOAD_INVALID', "finalize-local only supports 'analysis', 'plan', and 'code'");
98
76
  }
99
- return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot }) };
77
+ return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot, recoveryId }) };
100
78
  }
101
79
  //# sourceMappingURL=artifact-command.js.map
@@ -1,12 +1,9 @@
1
1
  import fs from 'node:fs';
2
- import { parseArtifactName } from './artifact-name.js';
3
2
  import path from 'node:path';
4
3
  import { createHash } from 'node:crypto';
5
4
  import { readStableFileSync } from '../host-control/secure-fs.js';
5
+ import { parseArtifactName } from './artifact-name.js';
6
6
  import { scanVisibleMarkdown } from './markdown.js';
7
- import { hasOpenArtifactRound } from './artifact-lifecycle.js';
8
- import { readArtifactRepairIntent } from './artifact-repair-intent.js';
9
- import { resolveTaskRef } from './resolve-ref.js';
10
7
  import { getArtifactSchema, renderArtifactSkeleton } from './artifact-schema.js';
11
8
  import { withTaskExecutionLock } from './task-execution-lock.js';
12
9
  function sha256Content(content) {
@@ -60,70 +57,26 @@ function stripHtmlComments(content) {
60
57
  }
61
58
  return result;
62
59
  }
63
- function diagnostic(code, message, sectionId, line, repairable = false, operation) {
64
- return {
65
- code, message, sectionId, line, repairable,
66
- ...(operation ? { operation } : {})
67
- };
60
+ function diagnostic(code, message, sectionId, line) {
61
+ return { code, message, sectionId, line };
68
62
  }
69
63
  function sectionHeadings(schema, section) {
70
64
  return [section.headings.zh, section.headings.en];
71
65
  }
72
- function createHeadingRepair(content, section, heading) {
73
- const to = heading.text.slice(0, -1);
74
- return {
75
- kind: 'replace-line',
76
- sectionId: section.id,
77
- line: lineNumber(content, heading.start),
78
- from: heading.text,
79
- to,
80
- start: heading.start,
81
- end: heading.end
82
- };
83
- }
84
- function createSectionInsertionRepair(content, section, marker, locale) {
85
- return {
86
- kind: 'insert-section',
87
- sectionId: section.id,
88
- line: marker.line,
89
- from: '',
90
- to: `## ${section.headings[locale === 'en' ? 'en' : 'zh']}\n`,
91
- start: marker.start,
92
- end: marker.start
93
- };
94
- }
95
- function applyRepairOperation(content, operation) {
96
- if (operation.start < 0 || operation.end < operation.start || operation.end > content.length)
97
- return null;
98
- if (operation.kind === 'insert-section') {
99
- if (operation.from !== '' || operation.start !== operation.end || !operation.to)
100
- return null;
101
- return `${content.slice(0, operation.start)}${operation.to}${content.slice(operation.end)}`;
102
- }
103
- const raw = content.slice(operation.start, operation.end);
104
- const offset = raw.indexOf(operation.from);
105
- if (!operation.from || offset < 0 || raw.indexOf(operation.from, offset + operation.from.length) >= 0)
106
- return null;
107
- return `${content.slice(0, operation.start)}${raw.slice(0, offset)}${operation.to}${raw.slice(offset + operation.from.length)}${content.slice(operation.end)}`;
108
- }
109
- function canonicalContent(content, operation = null) {
66
+ function canonicalContent(content) {
110
67
  let normalized = content;
111
- if (operation) {
112
- normalized = applyRepairOperation(normalized, operation) ?? normalized;
113
- }
114
68
  normalized = normalized.replace(/\r\n/g, '\n');
115
69
  normalized = normalized.replace(/^<!--\s*artifact-context:[^\n]+-->\s*\n?/gm, '');
116
70
  normalized = normalized.replace(/^\s*<!--\s*artifact-section:[^\n]+-->\s*\n?/gm, '');
117
71
  normalized = normalized.replace(/^\s*<!--\s*artifact-slot:empty\s*-->\s*\n?/gm, '');
118
72
  return normalized;
119
73
  }
120
- function canonicalSemanticDigest(content, operation = null) {
121
- return sha256Content(canonicalContent(content, operation));
74
+ function canonicalSemanticDigest(content) {
75
+ return sha256Content(canonicalContent(content));
122
76
  }
123
77
  function inspectArtifactStructure(content, schema) {
124
78
  const diagnostics = [];
125
79
  const scanned = scanVisibleMarkdown(content);
126
- const repairCandidates = [];
127
80
  if (!content.trim())
128
81
  diagnostics.push(diagnostic('ARTIFACT_EMPTY', 'artifact is empty', null, null));
129
82
  if (scanned.hasUnclosedFence)
@@ -133,22 +86,19 @@ function inspectArtifactStructure(content, schema) {
133
86
  const aliases = sectionHeadings(schema, section);
134
87
  const exact = scanned.headings.filter((heading) => heading.level === 2 && aliases.includes(heading.text));
135
88
  const punctuated = scanned.headings.filter((heading) => heading.level === 2 && aliases.some((alias) => heading.text === `${alias}:` || heading.text === `${alias}:`));
136
- headingsBySection.set(section.id, exact.length === 0 && punctuated.length === 1 ? punctuated : exact);
89
+ headingsBySection.set(section.id, exact.length > 0 ? exact : punctuated);
137
90
  if (exact.length > 1 || punctuated.length > 0 && exact.length > 0 || punctuated.length > 1) {
138
91
  const first = exact[1] ?? punctuated[0] ?? exact[0];
139
92
  diagnostics.push(diagnostic('ARTIFACT_DUPLICATE_SECTION', `required section '${aliases[0]}' is duplicated or has an ambiguous punctuation variant`, section.id, first ? lineNumber(content, first.start) : null));
140
93
  }
141
94
  else if (exact.length === 0 && punctuated.length === 1) {
142
- repairCandidates.push(createHeadingRepair(content, section, punctuated[0]));
95
+ diagnostics.push(diagnostic('ARTIFACT_HEADING_TRAILING_PUNCTUATION', `visible required H2 '${punctuated[0].text}' has trailing punctuation`, section.id, lineNumber(content, punctuated[0].start)));
143
96
  }
144
97
  else if (exact.length === 0) {
145
98
  diagnostics.push(diagnostic('ARTIFACT_MISSING_SECTION', `required section '${aliases[0]}' is missing`, section.id, null));
146
99
  }
147
100
  }
148
101
  const markers = markerEntries(content, scanned);
149
- const englishHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.en === heading.text)).length;
150
- const chineseHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.zh === heading.text)).length;
151
- const locale = englishHeadings > chineseHeadings ? 'en' : 'zh-CN';
152
102
  for (const section of schema.sections) {
153
103
  const matches = markers.filter((entry) => entry.marker === section.marker);
154
104
  if (matches.length === 0) {
@@ -174,10 +124,7 @@ function inspectArtifactStructure(content, schema) {
174
124
  if (marker && !heading && marker.standalone) {
175
125
  const nextHeading = scanned.headings.find((candidate) => candidate.start > marker.start && candidate.level <= 2);
176
126
  const body = stripHtmlComments(content.slice(marker.end, nextHeading?.start ?? content.length)).trim();
177
- if (body && repairCandidates.length === 0) {
178
- repairCandidates.push(createSectionInsertionRepair(content, section, marker, locale));
179
- }
180
- else if (!body) {
127
+ if (!body) {
181
128
  diagnostics.push(diagnostic('ARTIFACT_EMPTY_SECTION', `section '${section.headings.zh}' has no semantic body`, section.id, marker.line));
182
129
  }
183
130
  }
@@ -187,17 +134,6 @@ function inspectArtifactStructure(content, schema) {
187
134
  diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `unknown section marker '${marker.marker}'`, null, marker.line));
188
135
  }
189
136
  }
190
- for (const candidate of repairCandidates) {
191
- if (candidate.kind === 'replace-line') {
192
- diagnostics.push(diagnostic('ARTIFACT_HEADING_TRAILING_PUNCTUATION', `visible required H2 '${candidate.from}' may be normalized to '${candidate.to}'`, candidate.sectionId, candidate.line, true, candidate));
193
- continue;
194
- }
195
- const missing = diagnostics.findIndex((item) => item.code === 'ARTIFACT_MISSING_SECTION' && item.sectionId === candidate.sectionId);
196
- if (missing >= 0) {
197
- const section = schema.sections.find((item) => item.id === candidate.sectionId);
198
- diagnostics.splice(missing, 1, diagnostic('ARTIFACT_MISSING_SECTION', `required section '${section.headings.zh}' is missing and can be inserted at its unique marker`, candidate.sectionId, candidate.line, true, candidate));
199
- }
200
- }
201
137
  const orderedHeadings = schema.sections.flatMap((section) => {
202
138
  const heading = headingsBySection.get(section.id)?.[0];
203
139
  return heading ? [{ section, start: heading.start }] : [];
@@ -212,14 +148,11 @@ function inspectArtifactStructure(content, schema) {
212
148
  if (orderedMarkers.some((item, index) => index > 0 && item.start <= orderedMarkers[index - 1].start)) {
213
149
  diagnostics.push(diagnostic('ARTIFACT_SECTION_ORDER_INVALID', 'section markers are not in schema order', null, null));
214
150
  }
215
- const repair = repairCandidates.length === 1 && (diagnostics.length === 0 ||
216
- (diagnostics.length === 1 && diagnostics[0]?.operation?.kind === repairCandidates[0]?.kind)) ? repairCandidates[0] : null;
217
151
  return {
218
152
  ok: diagnostics.length === 0,
219
153
  family: schema.family,
220
- semanticDigest: canonicalSemanticDigest(content, repair),
221
- diagnostics,
222
- repair
154
+ semanticDigest: canonicalSemanticDigest(content),
155
+ diagnostics
223
156
  };
224
157
  }
225
158
  function inspectArtifactPatterns(content, schema) {
@@ -230,25 +163,22 @@ function inspectArtifactPatterns(content, schema) {
230
163
  ok: diagnostics.length === 0,
231
164
  family: schema.family,
232
165
  semanticDigest: canonicalSemanticDigest(content),
233
- diagnostics,
234
- repair: null
166
+ diagnostics
235
167
  };
236
168
  }
237
169
  function inspectArtifactContract(content, schema) {
238
170
  const structure = inspectArtifactStructure(content, schema);
239
171
  const patterns = inspectArtifactPatterns(content, schema);
240
172
  const diagnostics = [...structure.diagnostics, ...patterns.diagnostics];
241
- const repair = patterns.diagnostics.length === 0 ? structure.repair : null;
242
173
  return {
243
174
  ok: diagnostics.length === 0,
244
175
  family: schema.family,
245
- semanticDigest: canonicalSemanticDigest(content, repair),
246
- diagnostics,
247
- repair
176
+ semanticDigest: canonicalSemanticDigest(content),
177
+ diagnostics
248
178
  };
249
179
  }
250
180
  function resultFailure(code, message) {
251
- return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, operation: null, error: { code, message } };
181
+ return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, error: { code, message } };
252
182
  }
253
183
  function resultNoOp(content) {
254
184
  return {
@@ -256,156 +186,13 @@ function resultNoOp(content) {
256
186
  changed: false,
257
187
  artifactSha256: sha256Content(content),
258
188
  semanticDigest: canonicalSemanticDigest(content),
259
- operation: null,
260
189
  error: null
261
190
  };
262
191
  }
263
- function validateRepairContext(request, content) {
264
- const round = artifactRound(request.family, request.artifact);
265
- if (round === null)
266
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact round is not canonical' };
267
- const contextPattern = /^<!--\s*artifact-context:([^:\s]+):([^:\s]+):(\d+)\s*-->\s*$/;
268
- const contexts = scanVisibleMarkdown(content).lines
269
- .map((line) => line.text.trim().match(contextPattern))
270
- .filter((match) => match !== null);
271
- if (contexts.length !== 1
272
- || contexts[0][1] !== request.taskId
273
- || contexts[0][2] !== request.family
274
- || Number(contexts[0][3]) !== round) {
275
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact context marker does not match the requested task, family, and round' };
276
- }
277
- let taskContent;
278
- const resolved = resolveTaskRef(request.taskId, { repoRoot: request.repoRoot });
279
- if (!resolved.ok)
280
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: resolved.message };
281
- try {
282
- taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
283
- }
284
- catch (error) {
285
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `cannot read task lifecycle context: ${String(error)}` };
286
- }
287
- if (!hasOpenArtifactRound(taskContent, request.family, round)) {
288
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `artifact '${request.artifact}' does not have exactly one matching open started lifecycle event` };
289
- }
290
- let intent;
291
- try {
292
- intent = readArtifactRepairIntent(request.repoRoot, request.taskId, request.family, request.artifact);
293
- }
294
- catch (error) {
295
- return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: String(error) };
296
- }
297
- if (!intent || intent.state !== 'awaiting-repair') {
298
- return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'artifact has no awaiting-repair finalization provenance' };
299
- }
300
- if (intent.artifactSha256 !== request.expectedSha256 || intent.semanticDigest !== request.expectedSemanticDigest) {
301
- return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'repair request does not match the current finalization provenance' };
302
- }
303
- return { ok: true };
304
- }
305
192
  function artifactRound(family, artifact) {
306
193
  const identity = parseArtifactName(artifact);
307
194
  return identity?.family === family ? identity.round : null;
308
195
  }
309
- function validateTarget(taskDir, family, artifact) {
310
- if (path.basename(artifact) !== artifact || artifactRound(family, artifact) === null || !artifact.endsWith('.md'))
311
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be a canonical top-level Markdown file');
312
- if (!/^TASK-\d{8}-\d{6}$/.test(path.basename(path.resolve(taskDir))))
313
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'target directory is not a task directory');
314
- const target = path.join(taskDir, artifact);
315
- if (path.dirname(path.resolve(target)) !== path.resolve(taskDir))
316
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be inside the current task directory');
317
- let stat;
318
- try {
319
- stat = fs.lstatSync(target);
320
- }
321
- catch (error) {
322
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact cannot be read: ${String(error)}`);
323
- }
324
- if (stat.isSymbolicLink() || !stat.isFile())
325
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact is not a regular file');
326
- try {
327
- fs.accessSync(target, fs.constants.R_OK | fs.constants.W_OK);
328
- }
329
- catch (error) {
330
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact is not readable and writable: ${String(error)}`);
331
- }
332
- return { path: target };
333
- }
334
- function applyRepairUnlocked(request) {
335
- const schema = getArtifactSchema(request.family);
336
- if (!schema)
337
- return resultFailure('ARTIFACT_FAMILY_UNKNOWN', `unknown artifact family '${request.family}'`);
338
- const target = validateTarget(request.taskDir, request.family, request.artifact);
339
- if ('status' in target)
340
- return target;
341
- let content;
342
- try {
343
- content = readStableFileSync(target.path, { maxBytes: 1024 * 1024 }).bytes.toString('utf8');
344
- }
345
- catch (error) {
346
- return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', String(error));
347
- }
348
- const actualSha256 = sha256Content(content);
349
- if (actualSha256 !== request.expectedSha256)
350
- return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact SHA-256 does not match the expected repair baseline');
351
- const context = validateRepairContext(request, content);
352
- if (!context.ok)
353
- return resultFailure(context.code, context.message);
354
- const inspection = inspectArtifactStructure(content, schema);
355
- if (!inspection.repair || inspection.diagnostics.length !== 1) {
356
- return resultFailure('ARTIFACT_REPAIR_UNSAFE', inspection.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ') || 'no deterministic structural repair is available');
357
- }
358
- if (inspection.repair.kind !== request.operation.kind
359
- || inspection.repair.sectionId !== request.operation.sectionId
360
- || inspection.repair.from !== request.operation.from
361
- || inspection.repair.to !== request.operation.to
362
- || inspection.repair.start !== request.operation.start
363
- || inspection.repair.end !== request.operation.end) {
364
- return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact structure');
365
- }
366
- if (inspection.semanticDigest !== request.expectedSemanticDigest)
367
- return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact semantic digest does not match the expected repair baseline');
368
- const transformed = applyRepairOperation(content, inspection.repair);
369
- if (transformed === null)
370
- return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact bytes');
371
- if (transformed === content)
372
- return resultFailure('ARTIFACT_REPAIR_NO_PROGRESS', 'repair operation produced no byte change');
373
- const tempPath = path.join(request.taskDir, `.${request.artifact}.repair-${process.pid}-${Date.now()}.tmp`);
374
- let mode = 0o600;
375
- try {
376
- mode = fs.statSync(target.path).mode & 0o777;
377
- fs.writeFileSync(tempPath, transformed, { encoding: 'utf8', mode, flag: 'wx' });
378
- const currentStat = fs.lstatSync(target.path);
379
- if (currentStat.isSymbolicLink() || !currentStat.isFile() || readStableFileSync(target.path, { maxBytes: 1024 * 1024 }).sha256 !== actualSha256) {
380
- fs.unlinkSync(tempPath);
381
- return resultFailure('ARTIFACT_REPAIR_CONFLICT', 'artifact changed or was replaced during repair');
382
- }
383
- fs.renameSync(tempPath, target.path);
384
- }
385
- catch (error) {
386
- try {
387
- fs.unlinkSync(tempPath);
388
- }
389
- catch { /* preserve primary error */ }
390
- return resultFailure('ARTIFACT_REPAIR_WRITE_FAILED', String(error));
391
- }
392
- return {
393
- status: 'applied',
394
- changed: true,
395
- artifactSha256: sha256Content(transformed),
396
- semanticDigest: canonicalSemanticDigest(transformed),
397
- operation: inspection.repair,
398
- error: null
399
- };
400
- }
401
- function applyArtifactRepair(request) {
402
- try {
403
- return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.repair', () => applyRepairUnlocked(request));
404
- }
405
- catch (error) {
406
- return resultFailure('ARTIFACT_REPAIR_LOCK_FAILED', String(error));
407
- }
408
- }
409
196
  function initializeArtifactSkeleton(request) {
410
197
  const schema = getArtifactSchema(request.family);
411
198
  if (!schema)
@@ -461,7 +248,6 @@ function initializeArtifactSkeleton(request) {
461
248
  changed: true,
462
249
  artifactSha256: sha256Content(content),
463
250
  semanticDigest: canonicalSemanticDigest(content),
464
- operation: null,
465
251
  error: null
466
252
  };
467
253
  });
@@ -470,5 +256,5 @@ function initializeArtifactSkeleton(request) {
470
256
  return resultFailure('ARTIFACT_INIT_LOCK_FAILED', String(error));
471
257
  }
472
258
  }
473
- export { applyArtifactRepair, canonicalSemanticDigest, initializeArtifactSkeleton, inspectArtifactContract, inspectArtifactPatterns, inspectArtifactStructure, sha256Content };
259
+ export { canonicalSemanticDigest, initializeArtifactSkeleton, inspectArtifactContract, inspectArtifactPatterns, inspectArtifactStructure, sha256Content };
474
260
  //# sourceMappingURL=artifact-operations.js.map