@fitlab-ai/agent-infra 0.9.14 → 0.9.15
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/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +3 -1
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +15 -262
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-operation-registry.js +101 -3
- package/dist/lib/internal/task-review.js +8 -53
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +2 -2
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +55 -3
- package/dist/lib/platform/pull-requests.js +2 -105
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +127 -142
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +3 -1514
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +491 -120
- package/dist/lib/sandbox/control/state.js +58 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +23 -69
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +87 -16
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +371 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +120 -9
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +132 -157
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +14 -17
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +125 -193
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/verification-engine.js +116 -323
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +3 -1
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +13 -216
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-operation-registry.ts +111 -3
- package/lib/internal/task-review.ts +6 -51
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +3 -4
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +61 -5
- package/lib/platform/pull-requests.ts +3 -170
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +145 -155
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +3 -1909
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +75 -6
- package/lib/sandbox/control/server.ts +538 -123
- package/lib/sandbox/control/state.ts +78 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +22 -68
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +99 -18
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +502 -5
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +131 -10
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +148 -182
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +19 -19
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +131 -259
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/verification-engine.ts +110 -366
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
package/dist/lib/task/create.js
CHANGED
|
@@ -6,7 +6,7 @@ import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
|
|
|
6
6
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
7
7
|
import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.js';
|
|
8
8
|
import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.js';
|
|
9
|
-
import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS } from './qualification-audit.js';
|
|
9
|
+
import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.js';
|
|
10
10
|
const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'];
|
|
11
11
|
const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'];
|
|
12
12
|
const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'];
|
|
@@ -150,12 +150,64 @@ function renderTable(columns, rows) {
|
|
|
150
150
|
...rows.map((row) => `| ${row.map(tableCell).join(' | ')} |`)
|
|
151
151
|
].join('\n');
|
|
152
152
|
}
|
|
153
|
-
function
|
|
153
|
+
function escapeRegExp(value) {
|
|
154
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
155
|
+
}
|
|
156
|
+
function candidateIdFor(index) {
|
|
157
|
+
let value = index + 1;
|
|
158
|
+
let id = '';
|
|
159
|
+
while (value > 0) {
|
|
160
|
+
const remainder = (value - 1) % 26;
|
|
161
|
+
id = `${String.fromCharCode(65 + remainder)}${id}`;
|
|
162
|
+
value = Math.floor((value - 1) / 26);
|
|
163
|
+
}
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
function appendCanonicalRows(content, heading, columns, rows) {
|
|
154
167
|
if (rows.length === 0)
|
|
155
168
|
return content;
|
|
156
169
|
const table = renderTable(columns, rows);
|
|
157
|
-
const separator =
|
|
158
|
-
|
|
170
|
+
const [header, separator, ...rowLines] = table.split('\n');
|
|
171
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
172
|
+
const lines = content.split(/\r?\n/);
|
|
173
|
+
const headingPattern = new RegExp(`^###\\s+${escapeRegExp(heading)}\\s*$`);
|
|
174
|
+
const sectionStart = lines.findIndex((line) => headingPattern.test(line));
|
|
175
|
+
if (sectionStart < 0)
|
|
176
|
+
throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' is missing`);
|
|
177
|
+
let sectionEnd = lines.length;
|
|
178
|
+
for (let index = sectionStart + 1; index < lines.length; index += 1) {
|
|
179
|
+
if (/^#{2,3}\s+/.test(lines[index])) {
|
|
180
|
+
sectionEnd = index;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const matches = [];
|
|
185
|
+
for (let index = sectionStart + 1; index + 1 < sectionEnd; index += 1) {
|
|
186
|
+
if (lines[index].trim() === header && lines[index + 1].trim() === separator)
|
|
187
|
+
matches.push(index);
|
|
188
|
+
}
|
|
189
|
+
if (matches.length !== 1) {
|
|
190
|
+
throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' must contain exactly one canonical qualification table`);
|
|
191
|
+
}
|
|
192
|
+
lines.splice(matches[0] + 2, 0, ...rowLines);
|
|
193
|
+
return lines.join(eol);
|
|
194
|
+
}
|
|
195
|
+
function validateRenderedQualification(content, candidate) {
|
|
196
|
+
const parsed = parseTaskQualification(content);
|
|
197
|
+
if (!parsed.ok) {
|
|
198
|
+
throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: ${parsed.code}: ${parsed.message}`);
|
|
199
|
+
}
|
|
200
|
+
if (!parsed.qualification.present) {
|
|
201
|
+
throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification contract is missing');
|
|
202
|
+
}
|
|
203
|
+
const expectedConstraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
|
|
204
|
+
const expectedCandidateIds = candidate.taskInput.alternatives.map((_, index) => candidateIdFor(index));
|
|
205
|
+
const actualConstraintIds = parsed.qualification.constraints.map((row) => row.constraintId);
|
|
206
|
+
const actualCandidateIds = parsed.qualification.candidates.map((row) => row.candidateId);
|
|
207
|
+
if (JSON.stringify(actualConstraintIds) !== JSON.stringify(expectedConstraintIds)
|
|
208
|
+
|| JSON.stringify(actualCandidateIds) !== JSON.stringify(expectedCandidateIds)) {
|
|
209
|
+
throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification rows do not match task input');
|
|
210
|
+
}
|
|
159
211
|
}
|
|
160
212
|
function replaceEmptySubsection(content, heading, items) {
|
|
161
213
|
if (items.length === 0)
|
|
@@ -202,11 +254,11 @@ function renderTask(params) {
|
|
|
202
254
|
for (const [heading, key] of sections)
|
|
203
255
|
content = replaceEmptySubsection(content, heading, candidate.taskInput[key]);
|
|
204
256
|
const constraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
|
|
205
|
-
content = appendCanonicalRows(content, CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
|
|
257
|
+
content = appendCanonicalRows(content, '约束', CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
|
|
206
258
|
constraintIds[index], statement, 'assumption', 'create-task', 'taskInput', 'create-task input', '', ''
|
|
207
259
|
]));
|
|
208
|
-
content = appendCanonicalRows(content, CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
|
|
209
|
-
|
|
260
|
+
content = appendCanonicalRows(content, '候选与否决方案', CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
|
|
261
|
+
candidateIdFor(index), statement, 'pending', constraintIds.join(','), 'requires qualification', 'create-task input'
|
|
210
262
|
]));
|
|
211
263
|
content = content.replace('- **关联 Issue**:#XXX', '- **关联 Issue**:N/A');
|
|
212
264
|
content = content.replace('- **关联 PR**:#XXX', '- **关联 PR**:N/A');
|
|
@@ -328,8 +380,6 @@ function createLocalTask(value, options) {
|
|
|
328
380
|
return withCreateLock(repoRoot, workspaceRoot, () => {
|
|
329
381
|
if (fs.existsSync(receipts))
|
|
330
382
|
assertRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
|
|
331
|
-
else
|
|
332
|
-
fs.mkdirSync(receipts, { mode: 0o700 });
|
|
333
383
|
const receiptPath = path.join(receipts, `${keyDigest}.json`);
|
|
334
384
|
if (fs.existsSync(receiptPath)) {
|
|
335
385
|
const stat = fs.lstatSync(receiptPath);
|
|
@@ -345,6 +395,7 @@ function createLocalTask(value, options) {
|
|
|
345
395
|
}
|
|
346
396
|
const recovered = recoverPublishedTask(activeRoot, repoRoot, keyDigest, candidateDigest);
|
|
347
397
|
if (recovered) {
|
|
398
|
+
ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
|
|
348
399
|
writeReceipt(receiptPath, {
|
|
349
400
|
version: 1, candidateDigest, taskId: recovered.taskId, shortId: recovered.shortId, status: 'recovered'
|
|
350
401
|
});
|
|
@@ -366,6 +417,8 @@ function createLocalTask(value, options) {
|
|
|
366
417
|
template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project: projectConfig.project, delivery: projectConfig.delivery, timestamp,
|
|
367
418
|
agentInfraVersion, keyDigest, candidateDigest
|
|
368
419
|
});
|
|
420
|
+
validateRenderedQualification(rendered, candidate);
|
|
421
|
+
ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
|
|
369
422
|
const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
|
|
370
423
|
fs.mkdirSync(temporary, { mode: 0o700 });
|
|
371
424
|
fs.writeFileSync(path.join(temporary, 'task.md'), rendered, { flag: 'wx', mode: 0o600 });
|
|
@@ -1,51 +1,7 @@
|
|
|
1
|
+
import { scanVisibleMarkdown } from './markdown.js';
|
|
1
2
|
const DECISION_ID = /^(AN|PL|CD|HD)-[1-9]\d*(?=$|[\s::])/i;
|
|
2
|
-
const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
|
|
3
|
-
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
4
|
-
const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
5
|
-
const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
|
|
6
3
|
const FIELD = /^\s*[-*]\s+\*\*([^*]+)\*\*\s*[::]\s*/;
|
|
7
4
|
const OPTION_HEADING = /^(?:方案|Option)\s+[A-Z](?:\b|\s*[::])/i;
|
|
8
|
-
function sourceLines(content) {
|
|
9
|
-
const lines = [];
|
|
10
|
-
let start = 0;
|
|
11
|
-
while (start < content.length) {
|
|
12
|
-
const newline = content.indexOf('\n', start);
|
|
13
|
-
const end = newline === -1 ? content.length : newline;
|
|
14
|
-
lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
|
|
15
|
-
start = newline === -1 ? content.length : newline + 1;
|
|
16
|
-
}
|
|
17
|
-
return lines;
|
|
18
|
-
}
|
|
19
|
-
function scanVisibleMarkdown(content) {
|
|
20
|
-
const lines = sourceLines(content);
|
|
21
|
-
const visible = [];
|
|
22
|
-
const headings = [];
|
|
23
|
-
const anchors = [];
|
|
24
|
-
let fence = null;
|
|
25
|
-
for (const line of lines) {
|
|
26
|
-
if (fence) {
|
|
27
|
-
const close = line.text.match(FENCE_CLOSE);
|
|
28
|
-
if (close && close[1][0] === fence.character && close[1].length >= fence.length)
|
|
29
|
-
fence = null;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
const open = line.text.match(FENCE_OPEN);
|
|
33
|
-
const marker = open?.[1];
|
|
34
|
-
const info = open?.[2] ?? '';
|
|
35
|
-
if (marker && !(marker[0] === '`' && info.includes('`'))) {
|
|
36
|
-
fence = { character: marker[0], length: marker.length };
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
visible.push(line);
|
|
40
|
-
const heading = line.text.match(HEADING);
|
|
41
|
-
if (heading)
|
|
42
|
-
headings.push({ ...line, level: heading[1].length, text: heading[2].trim() });
|
|
43
|
-
const anchor = line.text.match(ANCHOR);
|
|
44
|
-
if (anchor)
|
|
45
|
-
anchors.push({ ...line, id: anchor[2] });
|
|
46
|
-
}
|
|
47
|
-
return { lines: visible, headings, anchors };
|
|
48
|
-
}
|
|
49
5
|
function parseDecisionDetailBlocks(content) {
|
|
50
6
|
const scanned = scanVisibleMarkdown(content);
|
|
51
7
|
const details = scanned.headings.filter((heading) => heading.level === 3 && DECISION_ID.test(heading.text));
|
|
@@ -214,5 +170,5 @@ function normalizeDecisionDetailForDisplay(block) {
|
|
|
214
170
|
ordered.push(byKey.get('implementation'));
|
|
215
171
|
return [body.slice(heading.start, heading.end).trim(), ...ordered].join('\n\n').trim();
|
|
216
172
|
}
|
|
217
|
-
export { inspectDecisionDetailDuplicates, normalizeDecisionDetailForDisplay, parseDecisionDetailBlocks, parseDecisionEvidence, resolveDecisionDetail
|
|
173
|
+
export { inspectDecisionDetailDuplicates, normalizeDecisionDetailForDisplay, parseDecisionDetailBlocks, parseDecisionEvidence, resolveDecisionDetail };
|
|
218
174
|
//# sourceMappingURL=decision-details.js.map
|
package/dist/lib/task/events.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
|
|
4
|
-
import { buildArtifactLinkSection,
|
|
4
|
+
import { buildArtifactLinkSection, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
5
|
+
import { artifactName, parseArtifactName } from './artifact-name.js';
|
|
5
6
|
import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
|
|
6
7
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
7
8
|
import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
@@ -15,12 +16,15 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
|
|
|
15
16
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
16
17
|
import { allowsManualOverride } from './guard-override.js';
|
|
17
18
|
import { consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
|
|
19
|
+
import { writeArtifactRepairIntent } from './artifact-repair-intent.js';
|
|
18
20
|
import { buildLifecycleFacts, canStart } from './capabilities.js';
|
|
19
21
|
import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
|
|
20
22
|
import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
|
|
21
23
|
import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.js';
|
|
22
24
|
import { getArtifactSchema } from './artifact-schema.js';
|
|
23
25
|
import { inspectArtifactContract } from './artifact-operations.js';
|
|
26
|
+
import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
|
|
27
|
+
import { readManualValidationCompletion } from './manual-validation-completion.js';
|
|
24
28
|
const eventCatalog = [
|
|
25
29
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
26
30
|
'review-analysis.started', 'review-analysis.completed',
|
|
@@ -46,8 +50,8 @@ const SCHEMAS = {
|
|
|
46
50
|
'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
|
|
47
51
|
'review-code.started': { optional: ['round'] },
|
|
48
52
|
'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
49
|
-
'manual-validation.started': { optional: ['round'] },
|
|
50
|
-
'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
|
|
53
|
+
'manual-validation.started': { optional: ['round', 'transactionId'] },
|
|
54
|
+
'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
|
|
51
55
|
'validation-run.started': { optional: ['round'] },
|
|
52
56
|
'validation-run.completed': { required: ['artifact'], optional: ['round'] }
|
|
53
57
|
};
|
|
@@ -92,7 +96,7 @@ function validateTaskEventRequest(request) {
|
|
|
92
96
|
if (modeRequired.some((key) => request[key] === undefined) || forbidden.some((key) => request[key] !== undefined))
|
|
93
97
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
|
|
94
98
|
}
|
|
95
|
-
if (request.fixFor &&
|
|
99
|
+
if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code')
|
|
96
100
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
|
|
97
101
|
if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput))
|
|
98
102
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
|
|
@@ -104,6 +108,19 @@ function validateTaskEventRequest(request) {
|
|
|
104
108
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
|
|
105
109
|
if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult)))
|
|
106
110
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
|
|
111
|
+
if (request.event === 'manual-validation.completed') {
|
|
112
|
+
for (const [name, value, pattern] of [
|
|
113
|
+
['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
|
|
114
|
+
['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
|
|
115
|
+
['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
|
|
116
|
+
])
|
|
117
|
+
if (!value || !pattern.test(value))
|
|
118
|
+
return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
|
|
119
|
+
}
|
|
120
|
+
if (request.event === 'manual-validation.started' && request.transactionId !== undefined
|
|
121
|
+
&& !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
|
|
122
|
+
return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
|
|
123
|
+
}
|
|
107
124
|
if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
|
|
108
125
|
if (!request.initiator || !request.requestId || !request.reasonCode) {
|
|
109
126
|
return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
|
|
@@ -202,7 +219,9 @@ function identity(request) {
|
|
|
202
219
|
if (family === 'manual-validation') {
|
|
203
220
|
return {
|
|
204
221
|
family, phase, action: spec.label,
|
|
205
|
-
note: phase === 'started'
|
|
222
|
+
note: phase === 'started'
|
|
223
|
+
? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
|
|
224
|
+
: `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
|
|
206
225
|
target: null
|
|
207
226
|
};
|
|
208
227
|
}
|
|
@@ -257,6 +276,24 @@ function normalizeStarted(request, repoRoot) {
|
|
|
257
276
|
return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with artifact context` }, context };
|
|
258
277
|
return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
|
|
259
278
|
}
|
|
279
|
+
function validateManualValidationCompletion(taskDir, taskId, request, artifactPath) {
|
|
280
|
+
const completion = readManualValidationCompletion(taskDir, {
|
|
281
|
+
transactionId: request.transactionId,
|
|
282
|
+
taskId,
|
|
283
|
+
prHeadSha: request.prHeadSha,
|
|
284
|
+
artifact: request.artifact,
|
|
285
|
+
receiptDigest: request.receiptDigest
|
|
286
|
+
});
|
|
287
|
+
if (!completion.ok)
|
|
288
|
+
return completion.error;
|
|
289
|
+
const { receipt, transaction } = completion.value;
|
|
290
|
+
if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
|
|
291
|
+
return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
|
|
292
|
+
}
|
|
293
|
+
if (sha256File(artifactPath) !== receipt.artifactSha256)
|
|
294
|
+
return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
260
297
|
function openStartedIdentity(rows, family) {
|
|
261
298
|
const spec = FAMILY[family];
|
|
262
299
|
if (family === 'manual-validation') {
|
|
@@ -623,6 +660,11 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
623
660
|
if (openIdentity) {
|
|
624
661
|
if (openIdentity.row.agent !== request.agent)
|
|
625
662
|
return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
663
|
+
if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
|
|
664
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
|
|
665
|
+
if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId)
|
|
666
|
+
return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
667
|
+
}
|
|
626
668
|
if (request.round !== undefined && request.round !== openIdentity.round)
|
|
627
669
|
return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
628
670
|
if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor)
|
|
@@ -644,6 +686,25 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
644
686
|
normalized = { ...request, round: identity.round };
|
|
645
687
|
}
|
|
646
688
|
const eventIdentity = identity(normalized);
|
|
689
|
+
const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
|
|
690
|
+
if (lifecycleAuthority) {
|
|
691
|
+
try {
|
|
692
|
+
validateLifecycleRecoveryAttestation(lifecycleAuthority);
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
696
|
+
}
|
|
697
|
+
const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
698
|
+
if (eventIdentity.phase !== 'completed'
|
|
699
|
+
|| lifecycleAuthority.phase !== 'task-event.completed'
|
|
700
|
+
|| lifecycleAuthority.taskId !== resolved.taskId
|
|
701
|
+
|| lifecycleAuthority.family !== expectedFamily
|
|
702
|
+
|| lifecycleAuthority.artifact !== normalized.artifact
|
|
703
|
+
|| lifecycleAuthority.round !== normalized.round
|
|
704
|
+
|| (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
|
|
705
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
706
|
+
}
|
|
707
|
+
}
|
|
647
708
|
const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
|
|
648
709
|
const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
|
|
649
710
|
const manual = eventIdentity.family === 'manual-validation';
|
|
@@ -657,6 +718,12 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
657
718
|
if (eventIdentity.phase === 'started' && row) {
|
|
658
719
|
if (row.agent !== normalized.agent)
|
|
659
720
|
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
721
|
+
if (manual && normalized.transactionId !== undefined) {
|
|
722
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
|
|
723
|
+
if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
|
|
724
|
+
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
660
727
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
|
|
661
728
|
}
|
|
662
729
|
if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0)
|
|
@@ -673,6 +740,11 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
673
740
|
if (!validated.ok)
|
|
674
741
|
return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
675
742
|
completedArtifact = validated.artifact;
|
|
743
|
+
if (eventIdentity.family === 'manual-validation') {
|
|
744
|
+
const validationError = validateManualValidationCompletion(resolved.taskDir, resolved.taskId, normalized, completedArtifact.path);
|
|
745
|
+
if (validationError)
|
|
746
|
+
return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
747
|
+
}
|
|
676
748
|
if (eventIdentity.family.startsWith('review-')) {
|
|
677
749
|
let reviewContent;
|
|
678
750
|
try {
|
|
@@ -743,7 +815,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
743
815
|
message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
744
816
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
745
817
|
}
|
|
746
|
-
if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
|
|
818
|
+
if (!localFinalizationIntent || !['passed', 'consumed', 'finalize-ready', 'commit-started'].includes(localFinalizationIntent.state)) {
|
|
747
819
|
return failed(normalized, {
|
|
748
820
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
749
821
|
message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
|
|
@@ -755,6 +827,18 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
755
827
|
message: `local finalizer provenance does not match ${completedArtifact.name}`
|
|
756
828
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
757
829
|
}
|
|
830
|
+
if (localFinalizationIntent.recoveryOperationId !== null) {
|
|
831
|
+
if (!lifecycleAuthority
|
|
832
|
+
|| localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
|
|
833
|
+
|| localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
|
|
834
|
+
|| (localFinalizationIntent.phase !== 'artifact.finalize-local'
|
|
835
|
+
&& localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority))) {
|
|
836
|
+
return failed(normalized, {
|
|
837
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
838
|
+
message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
|
|
839
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
840
|
+
}
|
|
841
|
+
}
|
|
758
842
|
}
|
|
759
843
|
}
|
|
760
844
|
if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
|
|
@@ -914,14 +998,28 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
914
998
|
});
|
|
915
999
|
}
|
|
916
1000
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
917
|
-
if (!normalized.dryRun && localFinalizationIntent && completedArtifact
|
|
1001
|
+
if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
|
|
918
1002
|
try {
|
|
919
|
-
|
|
1003
|
+
if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
1004
|
+
const expectedIntent = localFinalizationIntent;
|
|
1005
|
+
localFinalizationIntent = {
|
|
1006
|
+
...localFinalizationIntent,
|
|
1007
|
+
state: 'commit-started',
|
|
1008
|
+
phase: 'task-event.completed',
|
|
1009
|
+
authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
|
|
1010
|
+
requestId: lifecycleAuthority.lifecycleRequestId,
|
|
1011
|
+
updatedAt: Date.now()
|
|
1012
|
+
};
|
|
1013
|
+
writeArtifactRepairIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
|
|
1014
|
+
}
|
|
1015
|
+
else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
1016
|
+
localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
|
|
1017
|
+
}
|
|
920
1018
|
}
|
|
921
1019
|
catch (error) {
|
|
922
1020
|
return failed(normalized, {
|
|
923
1021
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
924
|
-
message: `local finalizer provenance could not be
|
|
1022
|
+
message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
925
1023
|
}, {
|
|
926
1024
|
taskId: resolved.taskId,
|
|
927
1025
|
taskMdPath: resolved.taskMdPath,
|
|
@@ -938,6 +1036,19 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
938
1036
|
const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
|
|
939
1037
|
if (result.status === 'failed')
|
|
940
1038
|
return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
1039
|
+
if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
|
|
1040
|
+
try {
|
|
1041
|
+
if (!options.deferLifecycleRecoveryConsumption)
|
|
1042
|
+
consumeLifecycleRecoveryAttestation(lifecycleAuthority);
|
|
1043
|
+
writeArtifactRepairIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
return failed(normalized, {
|
|
1047
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
1048
|
+
message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
|
|
1049
|
+
}, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
941
1052
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
942
1053
|
try {
|
|
943
1054
|
(options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function taskFinalizationReceiptState(receipt) {
|
|
2
|
+
if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
|
|
3
|
+
|| receipt.verification === 'pending' || receipt.warningProjection !== 'done')
|
|
4
|
+
return 'pending';
|
|
5
|
+
if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open'))
|
|
6
|
+
return 'unresolved';
|
|
7
|
+
return 'complete';
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=finalization-state.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
4
5
|
import { locateActivityLog } from './activity-log.js';
|
|
5
6
|
import { applyPrReviewActivityIntent } from './activity-intent.js';
|
|
6
7
|
import { applyHumanDecision } from './decision-intents.js';
|
|
@@ -577,12 +578,8 @@ function latestArtifact(taskDir, family) {
|
|
|
577
578
|
catch {
|
|
578
579
|
return undefined;
|
|
579
580
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const round = (name) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
|
|
583
|
-
return round(b) - round(a);
|
|
584
|
-
});
|
|
585
|
-
return candidates[0];
|
|
581
|
+
return names.map(parseArtifactName).filter((identity) => identity !== null)
|
|
582
|
+
.filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
|
|
586
583
|
}
|
|
587
584
|
async function probeVerificationEngineFailure(policy, resolved) {
|
|
588
585
|
const candidates = [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
1
2
|
import { parseTable } from './sections.js';
|
|
2
3
|
const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'];
|
|
3
4
|
const COLUMNS = [
|
|
@@ -5,7 +6,6 @@ const COLUMNS = [
|
|
|
5
6
|
'decided_at', 'status', 'consumed_by'
|
|
6
7
|
];
|
|
7
8
|
const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
|
|
8
|
-
const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
|
|
9
9
|
function validateInput(row) {
|
|
10
10
|
if (!/^II-[1-9]\d*$/.test(row.id))
|
|
11
11
|
throw new Error(`implementation input id '${row.id}' is invalid`);
|
|
@@ -17,7 +17,7 @@ function validateInput(row) {
|
|
|
17
17
|
const valid = (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
|
|
18
18
|
(hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
|
|
19
19
|
(hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
|
|
20
|
-
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' &&
|
|
20
|
+
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
|
|
21
21
|
if (!valid)
|
|
22
22
|
throw new Error(`implementation input ${row.id} has an invalid state combination`);
|
|
23
23
|
}
|
|
@@ -122,7 +122,7 @@ function selectPendingImplementationInput(rows) {
|
|
|
122
122
|
})[0] ?? null;
|
|
123
123
|
}
|
|
124
124
|
function consumeImplementationInput(rows, id, artifact) {
|
|
125
|
-
if (
|
|
125
|
+
if (parseArtifactName(artifact)?.family !== 'code')
|
|
126
126
|
throw new Error(`implementation input artifact '${artifact}' is invalid`);
|
|
127
127
|
let found = false;
|
|
128
128
|
const next = rows.map((row) => {
|