@fitlab-ai/agent-infra 0.11.0 → 0.11.2
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/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +6 -2
- package/dist/lib/internal/platform-comment.js +59 -0
- package/dist/lib/internal/sandbox-control.js +4 -131
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -0
- package/dist/lib/internal/task-review.js +5 -5
- package/dist/lib/internal/task-validate.js +0 -6
- package/dist/lib/platform/comment-safety.js +7 -1
- package/dist/lib/platform/github-provider.js +13 -3
- package/dist/lib/platform/issue-comments.js +120 -13
- package/dist/lib/platform/issues.js +3 -4
- package/dist/lib/platform/provider-contract.d.ts +2 -0
- package/dist/lib/platform/provider-validation.js +7 -2
- package/dist/lib/platform/verification-sync.js +161 -156
- package/dist/lib/sandbox/control/executor.js +0 -38
- package/dist/lib/sandbox/control/finalization-response.js +10 -2
- package/dist/lib/sandbox/control/task-view.js +3 -1
- package/dist/lib/sandbox/control/workflow-executor.js +32 -41
- package/dist/lib/task/artifact-command.js +6 -6
- package/dist/lib/task/artifact-lifecycle.js +15 -52
- package/dist/lib/task/artifact-operations.js +27 -1
- package/dist/lib/task/capabilities.js +0 -2
- package/dist/lib/task/codex-orchestration.js +10 -208
- package/dist/lib/task/commit-operation.js +3 -18
- package/dist/lib/task/control-authority.js +1 -8
- package/dist/lib/task/delegation-receipts.js +14 -76
- package/dist/lib/task/events.js +147 -278
- package/dist/lib/task/finalization-state.js +2 -1
- package/dist/lib/task/finalization.js +273 -49
- package/dist/lib/task/gate-policy.js +94 -0
- package/dist/lib/task/lifecycle-execution.js +2 -11
- package/dist/lib/task/local-artifact-finalization.js +31 -245
- package/dist/lib/task/orchestration.js +8 -24
- package/dist/lib/task/review-artifacts.js +12 -6
- package/dist/lib/task/review-command.js +0 -1
- package/dist/lib/task/review-finalization.js +47 -218
- package/dist/lib/task/verification-engine.js +28 -17
- package/dist/lib/task/verification.js +25 -9
- package/dist/lib/task/workflow-command.js +4 -1
- package/dist/lib/task/workflow-dispatch.js +55 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +6 -2
- package/lib/internal/platform-comment.ts +38 -0
- package/lib/internal/sandbox-control.ts +3 -145
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -0
- package/lib/internal/task-review.ts +5 -5
- package/lib/internal/task-validate.ts +0 -5
- package/lib/platform/comment-safety.ts +8 -0
- package/lib/platform/github-provider.ts +11 -2
- package/lib/platform/issue-comments.ts +132 -13
- package/lib/platform/issues.ts +3 -4
- package/lib/platform/provider-contract.ts +2 -0
- package/lib/platform/provider-validation.ts +6 -2
- package/lib/platform/verification-sync.ts +178 -179
- package/lib/sandbox/control/executor.ts +0 -44
- package/lib/sandbox/control/finalization-response.ts +10 -2
- package/lib/sandbox/control/task-view.ts +3 -1
- package/lib/sandbox/control/workflow-executor.ts +31 -48
- package/lib/task/artifact-command.ts +6 -7
- package/lib/task/artifact-lifecycle.ts +12 -49
- package/lib/task/artifact-operations.ts +27 -1
- package/lib/task/capabilities.ts +0 -1
- package/lib/task/codex-orchestration.ts +8 -241
- package/lib/task/commit-operation.ts +3 -23
- package/lib/task/control-authority.ts +1 -8
- package/lib/task/delegation-receipts.ts +12 -79
- package/lib/task/events.ts +155 -287
- package/lib/task/finalization-state.ts +2 -1
- package/lib/task/finalization.ts +277 -57
- package/lib/task/gate-policy.ts +133 -0
- package/lib/task/lifecycle-execution.ts +10 -21
- package/lib/task/local-artifact-finalization.ts +29 -285
- package/lib/task/orchestration.ts +12 -41
- package/lib/task/review-artifacts.ts +8 -2
- package/lib/task/review-command.ts +0 -1
- package/lib/task/review-finalization.ts +46 -200
- package/lib/task/verification-engine.ts +24 -14
- package/lib/task/verification-types.ts +7 -0
- package/lib/task/verification.ts +25 -9
- package/lib/task/workflow-command.ts +4 -1
- package/lib/task/workflow-dispatch.ts +46 -3
- package/package.json +2 -2
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +5 -8
- package/templates/.agents/rules/lifecycle-orchestration.en.md +7 -7
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +14 -52
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -52
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/block-task/config/verify.json +1 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -0
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -0
- package/templates/.agents/skills/commit/config/verify.json +1 -5
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +6 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +6 -6
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +3 -14
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +3 -14
- package/templates/.agents/skills/create-pr/config/verify.json +1 -10
- package/templates/.agents/skills/create-task/config/verify.json +1 -4
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-code/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -8
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -8
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +38 -13
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +38 -13
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
|
@@ -357,43 +357,6 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
357
357
|
? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
|
|
358
358
|
: orchestrationFailure(code, message);
|
|
359
359
|
}
|
|
360
|
-
let controllerBinding = null;
|
|
361
|
-
if (request.family === 'task-orchestration') {
|
|
362
|
-
const codexPrepare = operation.family === 'task-orchestration'
|
|
363
|
-
&& operation.intent === 'prepare'
|
|
364
|
-
&& operation.input.client === 'codex';
|
|
365
|
-
if (codexPrepare) {
|
|
366
|
-
if (!request.controllerProof) {
|
|
367
|
-
const authority = options.lifecycleRecoveryAttestation;
|
|
368
|
-
if (!authority || authority.phase !== 'orchestration.prepare') {
|
|
369
|
-
return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED', 'Codex prepare requires a current controller lease proof');
|
|
370
|
-
}
|
|
371
|
-
controllerBinding = {
|
|
372
|
-
instanceDigest: authority.controllerInstanceDigest,
|
|
373
|
-
controlGeneration: authority.controlGeneration
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
try {
|
|
378
|
-
controllerBinding = (options.resolveControllerBinding ?? resolveCodexControllerBinding)({
|
|
379
|
-
manifest,
|
|
380
|
-
manifestPath,
|
|
381
|
-
proof: request.controllerProof,
|
|
382
|
-
buildIdentity: (options.buildIdentity ?? computeLifecycleBuildIdentity)(manifest.repoRoot)
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
catch (error) {
|
|
386
|
-
const code = error instanceof CodexControllerRegistrationError
|
|
387
|
-
? error.code
|
|
388
|
-
: 'CODEX_SANDBOX_CONTROLLER_PROOF_INVALID';
|
|
389
|
-
return orchestrationFailure(code, `${code}: Codex controller proof was rejected`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
else if (request.controllerProof !== null) {
|
|
394
|
-
return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'Controller proof is only accepted for canonical Codex prepare');
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
360
|
const diagnosticLog = (event, fields) => {
|
|
398
361
|
appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
|
|
399
362
|
requestId: request.id,
|
|
@@ -411,7 +374,6 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
411
374
|
manifestPath,
|
|
412
375
|
requestId: request.id,
|
|
413
376
|
diagnosticLog,
|
|
414
|
-
...(controllerBinding ? { controllerBinding } : {}),
|
|
415
377
|
lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
|
|
416
378
|
});
|
|
417
379
|
const format = (value) => {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export function finalizationTerminalResponse(taskId, requestId, receipt) {
|
|
2
2
|
const pendingSteps = [
|
|
3
3
|
receipt.taskComment === 'pending' ? 'task-comment' : null,
|
|
4
|
-
receipt.verification === 'pending' ? 'verification' : null
|
|
4
|
+
receipt.verification === 'pending' ? 'verification' : null,
|
|
5
|
+
receipt.summary === 'pending' ? 'summary' : null,
|
|
6
|
+
receipt.postSummaryVerification === 'pending' ? 'post-summary-verification' : null
|
|
5
7
|
].filter((step) => step !== null);
|
|
6
|
-
const completedSteps = [
|
|
8
|
+
const completedSteps = [
|
|
9
|
+
'lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment',
|
|
10
|
+
receipt.verification === 'pending' ? null : 'verification', receipt.summary === 'pending' ? null : 'summary',
|
|
11
|
+
receipt.postSummaryVerification === 'pending' ? null : 'post-summary-verification'
|
|
12
|
+
]
|
|
7
13
|
.filter((step) => step !== null);
|
|
8
14
|
const warnings = receipt.warnings
|
|
9
15
|
.filter((warning) => warning.status === 'open')
|
|
@@ -13,6 +19,8 @@ export function finalizationTerminalResponse(taskId, requestId, receipt) {
|
|
|
13
19
|
lifecycle: { status: 'no-op', changed: false, error: null },
|
|
14
20
|
taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
|
|
15
21
|
verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
|
|
22
|
+
summary: receipt.summary === 'pending' ? null : { status: 'no-op', changed: false, error: null },
|
|
23
|
+
postSummaryVerification: receipt.postSummaryVerification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
|
|
16
24
|
completedSteps, pendingSteps,
|
|
17
25
|
result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
|
|
18
26
|
? 'completed_with_warnings' : 'completed',
|
|
@@ -13,9 +13,11 @@ function receiptIdentity(value, generation, requestId) {
|
|
|
13
13
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding))
|
|
14
14
|
return null;
|
|
15
15
|
const controlBinding = binding;
|
|
16
|
-
if (receipt.version !==
|
|
16
|
+
if (receipt.version !== 3 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
|
|
17
17
|
|| !Number.isSafeInteger(receipt.revision) || receipt.revision < 0
|
|
18
18
|
|| receipt.lifecycle !== 'done'
|
|
19
|
+
|| !['pending', 'done', 'skipped'].includes(String(receipt.summary))
|
|
20
|
+
|| !['pending', 'done', 'skipped'].includes(String(receipt.postSummaryVerification))
|
|
19
21
|
|| controlBinding.generation !== generation
|
|
20
22
|
|| typeof controlBinding.requestId !== 'string'
|
|
21
23
|
|| !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
1
4
|
import { TASK_WORKFLOW_COMMANDS } from '../../task/workflow-command.js';
|
|
2
5
|
import { parseArtifactCommand, executeArtifactCommand } from '../../task/artifact-command.js';
|
|
3
6
|
import { parseReviewCommand } from '../../task/review-command.js';
|
|
4
7
|
import { prepareLocalArtifact, preflightLocalArtifact, commitLocalArtifactProvenance } from '../../task/local-artifact-finalization.js';
|
|
5
|
-
import { prepareReviewSummaryCandidate,
|
|
6
|
-
import { readArtifactRecoveryIntent } from '../../task/artifact-repair-intent.js';
|
|
8
|
+
import { prepareReviewSummaryCandidate, preflightReviewSummary } from '../../task/review-finalization.js';
|
|
7
9
|
import { withTaskExecutionLock } from '../../task/task-execution-lock.js';
|
|
8
10
|
import { dispatchWorkflowCommand } from '../../task/workflow-dispatch.js';
|
|
9
11
|
import { assertSandboxTaskSource } from '../workspace-view.js';
|
|
@@ -14,20 +16,25 @@ import { parseTaskEventRequest } from '../../internal/task-event.js';
|
|
|
14
16
|
function executionResult(result) {
|
|
15
17
|
return { exitCode: result.status === 'failed' || result.status === 'refused' ? 1 : 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
16
18
|
}
|
|
19
|
+
function replaceCurrentArtifactAtomically(file, content) {
|
|
20
|
+
const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
|
|
21
|
+
try {
|
|
22
|
+
fs.writeFileSync(temporary, content, { encoding: 'utf8', flag: 'wx' });
|
|
23
|
+
fs.renameSync(temporary, file);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
try {
|
|
27
|
+
fs.unlinkSync(temporary);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error.code !== 'ENOENT')
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
17
35
|
/** The broker already authorized this host executor; domain operations own validation. */
|
|
18
36
|
export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAttestation = null, options = {}) {
|
|
19
37
|
let publicationCommitted = false;
|
|
20
|
-
let recoveryObservation = null;
|
|
21
|
-
const observeRecoveryPublication = () => {
|
|
22
|
-
if (!recoveryObservation || recoveryObservation.wasPassed)
|
|
23
|
-
return;
|
|
24
|
-
const intent = readArtifactRecoveryIntent(manifest.repoRoot, request.taskId, recoveryObservation.family, recoveryObservation.artifact);
|
|
25
|
-
publicationCommitted ||= intent?.state === 'preflight-ready'
|
|
26
|
-
|| intent?.state === 'preflight-commit-started'
|
|
27
|
-
|| intent?.state === 'preflight-passed'
|
|
28
|
-
|| intent?.state === 'passed'
|
|
29
|
-
|| intent?.state === 'commit-started';
|
|
30
|
-
};
|
|
31
38
|
let faultTriggered = false;
|
|
32
39
|
const fault = (window) => {
|
|
33
40
|
if (faultTriggered || options.faultWindow !== window)
|
|
@@ -91,46 +98,31 @@ export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAt
|
|
|
91
98
|
const { family } = input;
|
|
92
99
|
if (family !== 'analysis' && family !== 'plan' && family !== 'code')
|
|
93
100
|
throw new Error('ARTIFACT_IDENTITY_INVALID');
|
|
94
|
-
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot,
|
|
101
|
+
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot, lockAlreadyHeld: true };
|
|
95
102
|
if (input.operation === 'preflight') {
|
|
96
|
-
// Preflight only seals a verified generation. It must not publish the
|
|
97
|
-
// formal artifact, set publicationStarted, or append finalizer audit.
|
|
98
103
|
const result = preflightLocalArtifact(local);
|
|
99
|
-
recoveryObservation = { family, artifact: input.artifact, wasPassed: false };
|
|
100
|
-
observeRecoveryPublication();
|
|
101
104
|
return response(result);
|
|
102
105
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
artifact: input.artifact,
|
|
106
|
-
wasPassed: readArtifactRecoveryIntent(manifest.repoRoot, request.taskId, family, input.artifact)?.state === 'passed'
|
|
107
|
-
};
|
|
108
|
-
const prepared = prepareLocalArtifact(local, content, lifecycleRecoveryAttestation ?? undefined);
|
|
109
|
-
observeRecoveryPublication();
|
|
110
|
-
const commitOptions = { afterPublish: () => fault('after-atomic-rename') };
|
|
111
|
-
if (prepared.result.status === 'failed')
|
|
112
|
-
return executionResult(commitLocalArtifactProvenance(prepared, commitOptions));
|
|
113
|
-
result = commitLocalArtifactProvenance(prepared, commitOptions);
|
|
106
|
+
const prepared = prepareLocalArtifact(local, content);
|
|
107
|
+
result = commitLocalArtifactProvenance(prepared);
|
|
114
108
|
}
|
|
115
109
|
else {
|
|
116
110
|
if (input.overrideTicket)
|
|
117
111
|
throw new Error('TASK_WORKFLOW_OVERRIDE_UNSUPPORTED');
|
|
118
112
|
const family = `review-${input.stage}`;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
124
|
-
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot, lockAlreadyHeld: true, startRecovery: true });
|
|
125
|
-
observeRecoveryPublication();
|
|
113
|
+
if (request.operation === 'review-preflight') {
|
|
114
|
+
const result = preflightReviewSummary(input, { repoRoot: manifest.repoRoot, lockAlreadyHeld: true });
|
|
115
|
+
return response(result);
|
|
116
|
+
}
|
|
117
|
+
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot, lockAlreadyHeld: true });
|
|
126
118
|
if (input.dryRun || prepared.result.status === 'planned')
|
|
127
119
|
return executionResult(prepared.result);
|
|
128
|
-
const commitOptions = { afterPublish: () => fault('after-atomic-rename') };
|
|
129
120
|
if (prepared.result.status === 'failed')
|
|
130
|
-
return executionResult(
|
|
131
|
-
|
|
121
|
+
return executionResult(prepared.result);
|
|
122
|
+
if (prepared.result.changed)
|
|
123
|
+
replaceCurrentArtifactAtomically(path.join(taskDir, input.artifact), prepared.content);
|
|
124
|
+
result = prepared.result;
|
|
132
125
|
}
|
|
133
|
-
observeRecoveryPublication();
|
|
134
126
|
publicationCommitted ||= result.changed === true;
|
|
135
127
|
if (result.status !== 'failed')
|
|
136
128
|
appendDiagnosticAudit(manifest, 'task-workflow-artifact-finalized', {
|
|
@@ -142,7 +134,6 @@ export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAt
|
|
|
142
134
|
});
|
|
143
135
|
}
|
|
144
136
|
catch (error) {
|
|
145
|
-
observeRecoveryPublication();
|
|
146
137
|
const message = error instanceof Error ? error.message : String(error);
|
|
147
138
|
const code = error?.code ?? /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_WORKFLOW_REQUEST_INVALID';
|
|
148
139
|
return executionResult({ status: 'failed', changed: publicationCommitted ? null : false, error: { code, message } });
|
|
@@ -15,8 +15,8 @@ export function parseArtifactCommand(args) {
|
|
|
15
15
|
const allowed = {
|
|
16
16
|
inspect: ['--family'],
|
|
17
17
|
init: ['--family', '--artifact', '--locale'],
|
|
18
|
-
'preflight': ['--family', '--artifact'
|
|
19
|
-
'finalize-local': ['--family', '--artifact'
|
|
18
|
+
'preflight': ['--family', '--artifact'],
|
|
19
|
+
'finalize-local': ['--family', '--artifact']
|
|
20
20
|
}[operation];
|
|
21
21
|
for (let index = 2; index < args.length; index += 1) {
|
|
22
22
|
const flag = args[index];
|
|
@@ -38,12 +38,12 @@ export function parseArtifactCommand(args) {
|
|
|
38
38
|
throw new Error("option '--locale' must be 'zh-CN' or 'en'");
|
|
39
39
|
return {
|
|
40
40
|
taskRef, operation: operation, family: fields['--family'],
|
|
41
|
-
artifact: fields['--artifact'] ?? '', locale
|
|
41
|
+
artifact: fields['--artifact'] ?? '', locale
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
/** Resolve authoritative task metadata separately from a trusted candidate directory. */
|
|
45
45
|
export function executeArtifactCommand(command, options = {}) {
|
|
46
|
-
const { taskRef, operation, family, artifact, locale
|
|
46
|
+
const { taskRef, operation, family, artifact, locale } = command;
|
|
47
47
|
const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
|
|
48
48
|
if (operation === 'inspect') {
|
|
49
49
|
const result = resolveArtifactContext(taskRef, family, { repoRoot: options.repoRoot });
|
|
@@ -76,7 +76,7 @@ export function executeArtifactCommand(command, options = {}) {
|
|
|
76
76
|
return fail('ARTIFACT_PAYLOAD_INVALID', "preflight and finalize-local only support 'analysis', 'plan', and 'code'");
|
|
77
77
|
}
|
|
78
78
|
if (operation === 'preflight')
|
|
79
|
-
return { ...preflightLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot
|
|
80
|
-
return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot
|
|
79
|
+
return { ...preflightLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot }) };
|
|
80
|
+
return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot }) };
|
|
81
81
|
}
|
|
82
82
|
//# sourceMappingURL=artifact-command.js.map
|
|
@@ -150,72 +150,35 @@ function inspectArtifactDirectory(taskDir, family, identity = {}) {
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
function resolveReviewedInput(taskDir, review, expectedFamily, diagnostics) {
|
|
153
|
-
let content;
|
|
154
|
-
try {
|
|
155
|
-
content = fs.readFileSync(review.path, 'utf8');
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
diagnostics.push(diagnostic('BROKEN_REFERENCE', review.family, review.name, String(error)));
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
const parsed = parseReviewedInputReference(content, expectedFamily);
|
|
162
|
-
if (!parsed) {
|
|
163
|
-
diagnostics.push(diagnostic('BROKEN_REFERENCE', review.family, review.name, `${review.name} does not reference a canonical ${expectedFamily} artifact`));
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
const abs = path.join(taskDir, parsed.name);
|
|
167
153
|
try {
|
|
154
|
+
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
155
|
+
const receipt = receiptForOutput(taskContent, review.name);
|
|
156
|
+
const expectedEvent = reviewEventName(review.family);
|
|
157
|
+
if (!receipt || receipt.event !== expectedEvent)
|
|
158
|
+
throw new Error(`receipt for ${review.name} is missing or invalid`);
|
|
159
|
+
const input = parseArtifactName(receipt.input);
|
|
160
|
+
if (!input || input.family !== expectedFamily)
|
|
161
|
+
throw new Error(`receipt input '${receipt.input}' is not a canonical ${expectedFamily} artifact`);
|
|
162
|
+
const abs = path.join(taskDir, input.name);
|
|
168
163
|
const stat = fs.lstatSync(abs);
|
|
169
164
|
if (!stat.isFile() || stat.isSymbolicLink())
|
|
170
165
|
throw new Error('referenced input is not a regular file');
|
|
171
|
-
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
172
166
|
const invalidation = parseInvalidationDocument(taskContent);
|
|
173
167
|
if (!invalidation.ok)
|
|
174
168
|
throw new Error(invalidation.message);
|
|
175
|
-
if (isArtifactInvalidated(invalidation.document, expectedFamily,
|
|
176
|
-
throw new Error(`${
|
|
177
|
-
}
|
|
178
|
-
const receipt = receiptForOutput(taskContent, review.name);
|
|
179
|
-
if (!receipt)
|
|
180
|
-
throw new Error(`receipt for ${review.name} is missing`);
|
|
181
|
-
const expectedEvent = reviewEventName(review.family);
|
|
182
|
-
if (receipt.event !== expectedEvent || receipt.input !== parsed.name) {
|
|
183
|
-
throw new Error(`receipt for ${review.name} does not match ${parsed.name}`);
|
|
184
|
-
}
|
|
169
|
+
if (isArtifactInvalidated(invalidation.document, expectedFamily, input.name))
|
|
170
|
+
throw new Error(`${input.name} is invalidated`);
|
|
185
171
|
const actualSha256 = sha256File(abs);
|
|
186
172
|
if (actualSha256 !== receipt.inputSha256) {
|
|
187
|
-
throw new Error(`${
|
|
173
|
+
throw new Error(`${input.name} content digest does not match receipt`);
|
|
188
174
|
}
|
|
189
|
-
return { ...
|
|
175
|
+
return { ...input, path: abs, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
190
176
|
}
|
|
191
177
|
catch (error) {
|
|
192
|
-
diagnostics.push(diagnostic('BROKEN_REFERENCE', review.family, review.name,
|
|
178
|
+
diagnostics.push(diagnostic('BROKEN_REFERENCE', review.family, review.name, String(error)));
|
|
193
179
|
return null;
|
|
194
180
|
}
|
|
195
181
|
}
|
|
196
|
-
function parseReviewedInputReference(content, expectedFamily) {
|
|
197
|
-
const lines = content.split(/\r?\n/);
|
|
198
|
-
const header = lines.findIndex((line) => /\*\*(?:审查输入|Review Input)\*\*[::]/.test(line));
|
|
199
|
-
const referenceBlock = header >= 0 ? lines.slice(header, header + 12).join('\n') : '';
|
|
200
|
-
const candidates = [...referenceBlock.matchAll(/`([^`]+\.md)`/g)].map((match) => match[1]);
|
|
201
|
-
return candidates
|
|
202
|
-
.map((name) => parseArtifactName(name))
|
|
203
|
-
.find((item) => item?.family === expectedFamily) ?? null;
|
|
204
|
-
}
|
|
205
|
-
function parseCodePlanInputReference(content) {
|
|
206
|
-
const lines = content.split(/\r?\n/);
|
|
207
|
-
const sectionStart = lines.findIndex((line) => /^##\s+(?:实现输入|Implementation Input)\s*$/.test(line.trim()));
|
|
208
|
-
if (sectionStart < 0)
|
|
209
|
-
return null;
|
|
210
|
-
const bodyLines = lines.slice(sectionStart + 1);
|
|
211
|
-
const nextSection = bodyLines.findIndex((line) => /^##\s+/.test(line.trim()));
|
|
212
|
-
const body = (nextSection < 0 ? bodyLines : bodyLines.slice(0, nextSection)).join('\n');
|
|
213
|
-
const match = body.match(/\*\*(?:方案输入|Plan Input)\*\*[::]\s*`([^`]+\.md)`/);
|
|
214
|
-
if (!match)
|
|
215
|
-
return null;
|
|
216
|
-
const parsed = parseArtifactName(match[1]);
|
|
217
|
-
return parsed?.family === 'plan' ? { ...parsed, family: 'plan' } : null;
|
|
218
|
-
}
|
|
219
182
|
function reviewEventName(family) {
|
|
220
183
|
if (family === 'review-analysis')
|
|
221
184
|
return 'review-analysis.completed';
|
|
@@ -509,5 +472,5 @@ function buildArtifactLinkSection(content, artifact) {
|
|
|
509
472
|
body = lines.join('\n').replace(/^\n+|\n+$/g, '');
|
|
510
473
|
return { aliases, heading, body };
|
|
511
474
|
}
|
|
512
|
-
export { artifactFamilyCatalog, familySpec, inspectTaskArtifacts, inspectArtifactDirectory, resolveArtifactContext,
|
|
475
|
+
export { artifactFamilyCatalog, familySpec, inspectTaskArtifacts, inspectArtifactDirectory, resolveArtifactContext, resolveCodePlanInput, reviewEventName, assertWritableInventory, validateCompletedArtifact, buildArtifactLinkSection };
|
|
513
476
|
//# sourceMappingURL=artifact-lifecycle.js.map
|
|
@@ -6,6 +6,7 @@ import { parseArtifactName } from './artifact-name.js';
|
|
|
6
6
|
import { scanVisibleMarkdown } from './markdown.js';
|
|
7
7
|
import { getArtifactSchema, renderArtifactSkeleton } from './artifact-schema.js';
|
|
8
8
|
import { withTaskExecutionLock } from './task-execution-lock.js';
|
|
9
|
+
import { buildQualificationAudit, expectedQualificationRelations, parseTaskQualification, renderQualificationAudit } from './qualification-audit.js';
|
|
9
10
|
function sha256Content(content) {
|
|
10
11
|
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
11
12
|
}
|
|
@@ -193,6 +194,27 @@ function artifactRound(family, artifact) {
|
|
|
193
194
|
const identity = parseArtifactName(artifact);
|
|
194
195
|
return identity?.family === family ? identity.round : null;
|
|
195
196
|
}
|
|
197
|
+
function qualificationAuditForInit(request) {
|
|
198
|
+
let taskContent;
|
|
199
|
+
try {
|
|
200
|
+
taskContent = readStableFileSync(path.join(request.taskDir, 'task.md'), { maxBytes: 1024 * 1024 }).bytes.toString('utf8');
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
return { ok: false, code: 'ARTIFACT_INIT_TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error) };
|
|
204
|
+
}
|
|
205
|
+
const qualification = parseTaskQualification(taskContent);
|
|
206
|
+
if (!qualification.ok)
|
|
207
|
+
return qualification;
|
|
208
|
+
if (!qualification.qualification.present)
|
|
209
|
+
return { ok: true, content: '' };
|
|
210
|
+
const relations = expectedQualificationRelations(taskContent, request.family);
|
|
211
|
+
if (!relations.ok)
|
|
212
|
+
return relations;
|
|
213
|
+
const audit = buildQualificationAudit(taskContent, { upstreamRelations: relations.relations ?? [] });
|
|
214
|
+
if (!audit.ok)
|
|
215
|
+
return audit;
|
|
216
|
+
return { ok: true, content: `\n## 资格审计\n\n${renderQualificationAudit(audit.audit)}\n` };
|
|
217
|
+
}
|
|
196
218
|
function initializeArtifactSkeleton(request) {
|
|
197
219
|
const schema = getArtifactSchema(request.family);
|
|
198
220
|
if (!schema)
|
|
@@ -216,12 +238,16 @@ function initializeArtifactSkeleton(request) {
|
|
|
216
238
|
if (error.code !== 'ENOENT')
|
|
217
239
|
return resultFailure('ARTIFACT_INIT_TARGET_INVALID', String(error));
|
|
218
240
|
}
|
|
219
|
-
const
|
|
241
|
+
const skeleton = renderArtifactSkeleton({
|
|
220
242
|
taskId: request.taskId,
|
|
221
243
|
family: request.family,
|
|
222
244
|
artifact: request.artifact,
|
|
223
245
|
...(request.locale ? { locale: request.locale } : {})
|
|
224
246
|
});
|
|
247
|
+
const audit = qualificationAuditForInit(request);
|
|
248
|
+
if (!audit.ok)
|
|
249
|
+
return resultFailure(audit.code, audit.message);
|
|
250
|
+
const content = `${skeleton.trimEnd()}${audit.content}`;
|
|
225
251
|
const tempPath = path.join(request.taskDir, `.${request.artifact}.init-${process.pid}-${Date.now()}.tmp`);
|
|
226
252
|
try {
|
|
227
253
|
fs.writeFileSync(tempPath, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
|
@@ -146,8 +146,6 @@ function qualificationRecoveryAction(facts) {
|
|
|
146
146
|
return QUALIFICATION_RECOVERY_ORDER.find(({ pattern }) => stale.some((name) => pattern.test(name)))?.action ?? null;
|
|
147
147
|
}
|
|
148
148
|
function recommendNext(facts) {
|
|
149
|
-
if (invalidationBlocks(facts.invalidation))
|
|
150
|
-
return { action: null, reasonCode: 'INVALIDATION_INCOMPLETE', evidence: ['reconcile task invalidation before routing'] };
|
|
151
149
|
if (facts.qualificationStale) {
|
|
152
150
|
const action = qualificationRecoveryAction(facts);
|
|
153
151
|
return {
|