@fitlab-ai/agent-infra 0.9.14 → 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.
- 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 +9 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +39 -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 +4 -2
- 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 +331 -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 +16 -263
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +101 -4
- package/dist/lib/internal/task-review.js +11 -56
- 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 +18 -12
- package/dist/lib/platform/issues.js +2 -180
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +117 -33
- package/dist/lib/platform/pull-requests.js +5 -108
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +132 -143
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +19 -1511
- 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 +518 -127
- package/dist/lib/sandbox/control/state.js +64 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +89 -0
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +822 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- 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 +79 -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 +29 -289
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +134 -29
- 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 +33 -20
- package/dist/lib/task/control-authority.js +403 -6
- package/dist/lib/task/control-recovery.js +140 -0
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +275 -22
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +8 -14
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +441 -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-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +31 -4
- package/dist/lib/task/local-artifact-finalization.js +197 -197
- 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 +105 -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 +66 -19
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +26 -0
- package/dist/lib/task/review-finalization.js +175 -216
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +119 -337
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +16 -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/lib/version.js +1 -11
- 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 +10 -4
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +30 -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 +4 -2
- 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 +315 -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 +14 -217
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +111 -4
- package/lib/internal/task-review.ts +9 -54
- 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 +21 -14
- package/lib/platform/issues.ts +5 -246
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +123 -35
- package/lib/platform/pull-requests.ts +6 -173
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +150 -156
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +21 -1895
- 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 +80 -6
- package/lib/sandbox/control/server.ts +567 -132
- package/lib/sandbox/control/state.ts +86 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +92 -0
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +1142 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-identity.ts +19 -3
- 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 +85 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +34 -310
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +162 -33
- 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 +37 -20
- package/lib/task/control-authority.ts +540 -10
- package/lib/task/control-recovery.ts +186 -0
- package/lib/task/create-service.ts +1 -10
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +309 -24
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +8 -16
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +493 -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-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +30 -5
- package/lib/task/local-artifact-finalization.ts +244 -234
- 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 +156 -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 +84 -19
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +24 -0
- package/lib/task/review-finalization.ts +183 -292
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +113 -383
- package/lib/task/verification-types.ts +15 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +17 -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/lib/version.ts +1 -15
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- 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/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
- 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 +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +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/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- 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 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- 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 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -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/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- 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/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- 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
|
@@ -1,43 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { readStableFileSync } from '../host-control/secure-fs.js';
|
|
5
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
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
9
|
function sha256Content(content) {
|
|
10
10
|
return createHash('sha256').update(content, 'utf8').digest('hex');
|
|
11
11
|
}
|
|
12
|
-
function sourceLines(content) {
|
|
13
|
-
const lines = [];
|
|
14
|
-
let start = 0;
|
|
15
|
-
while (start < content.length) {
|
|
16
|
-
const newline = content.indexOf('\n', start);
|
|
17
|
-
const end = newline === -1 ? content.length : newline;
|
|
18
|
-
lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
|
|
19
|
-
start = newline === -1 ? content.length : newline + 1;
|
|
20
|
-
}
|
|
21
|
-
if (content.length === 0)
|
|
22
|
-
lines.push({ start: 0, end: 0, text: '' });
|
|
23
|
-
return lines;
|
|
24
|
-
}
|
|
25
|
-
function hasUnclosedFence(content) {
|
|
26
|
-
let fence = null;
|
|
27
|
-
for (const line of sourceLines(content)) {
|
|
28
|
-
if (fence) {
|
|
29
|
-
const close = line.text.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
|
|
30
|
-
if (close && close[1][0] === fence.character && close[1].length >= fence.length)
|
|
31
|
-
fence = null;
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
const open = line.text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
35
|
-
if (open && !(open[1][0] === '`' && open[2].includes('`'))) {
|
|
36
|
-
fence = { character: open[1][0], length: open[1].length };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return fence !== null;
|
|
40
|
-
}
|
|
41
12
|
function lineNumber(content, offset) {
|
|
42
13
|
return content.slice(0, offset).split('\n').length;
|
|
43
14
|
}
|
|
@@ -47,9 +18,9 @@ function markerPattern(marker) {
|
|
|
47
18
|
function escapeRegExp(value) {
|
|
48
19
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
49
20
|
}
|
|
50
|
-
function markerEntries(content) {
|
|
21
|
+
function markerEntries(content, scanned) {
|
|
51
22
|
const entries = [];
|
|
52
|
-
for (const line of
|
|
23
|
+
for (const line of scanned.lines) {
|
|
53
24
|
const match = line.text.match(/<!--\s*(artifact-section:[^\s>]+)\s*-->/g);
|
|
54
25
|
if (!match)
|
|
55
26
|
continue;
|
|
@@ -67,10 +38,9 @@ function markerEntries(content) {
|
|
|
67
38
|
}
|
|
68
39
|
return entries;
|
|
69
40
|
}
|
|
70
|
-
function sectionBodyBounds(content, heading) {
|
|
71
|
-
const next =
|
|
72
|
-
|
|
73
|
-
return { start: headingEnd, end: next?.start ?? content.length };
|
|
41
|
+
function sectionBodyBounds(content, heading, scanned) {
|
|
42
|
+
const next = scanned.headings.find((candidate) => candidate.start > heading.start && candidate.level <= 2);
|
|
43
|
+
return { start: heading.end, end: next?.start ?? content.length };
|
|
74
44
|
}
|
|
75
45
|
function stripHtmlComments(content) {
|
|
76
46
|
let result = '';
|
|
@@ -87,95 +57,48 @@ function stripHtmlComments(content) {
|
|
|
87
57
|
}
|
|
88
58
|
return result;
|
|
89
59
|
}
|
|
90
|
-
function diagnostic(code, message, sectionId, line
|
|
91
|
-
return {
|
|
92
|
-
code, message, sectionId, line, repairable,
|
|
93
|
-
...(operation ? { operation } : {})
|
|
94
|
-
};
|
|
60
|
+
function diagnostic(code, message, sectionId, line) {
|
|
61
|
+
return { code, message, sectionId, line };
|
|
95
62
|
}
|
|
96
63
|
function sectionHeadings(schema, section) {
|
|
97
64
|
return [section.headings.zh, section.headings.en];
|
|
98
65
|
}
|
|
99
|
-
function
|
|
100
|
-
const to = heading.text.slice(0, -1);
|
|
101
|
-
return {
|
|
102
|
-
kind: 'replace-line',
|
|
103
|
-
sectionId: section.id,
|
|
104
|
-
line: lineNumber(content, heading.start),
|
|
105
|
-
from: heading.text,
|
|
106
|
-
to,
|
|
107
|
-
start: heading.start,
|
|
108
|
-
end: heading.end
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function createSectionInsertionRepair(content, section, marker, locale) {
|
|
112
|
-
return {
|
|
113
|
-
kind: 'insert-section',
|
|
114
|
-
sectionId: section.id,
|
|
115
|
-
line: marker.line,
|
|
116
|
-
from: '',
|
|
117
|
-
to: `## ${section.headings[locale === 'en' ? 'en' : 'zh']}\n`,
|
|
118
|
-
start: marker.start,
|
|
119
|
-
end: marker.start
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function applyRepairOperation(content, operation) {
|
|
123
|
-
if (operation.start < 0 || operation.end < operation.start || operation.end > content.length)
|
|
124
|
-
return null;
|
|
125
|
-
if (operation.kind === 'insert-section') {
|
|
126
|
-
if (operation.from !== '' || operation.start !== operation.end || !operation.to)
|
|
127
|
-
return null;
|
|
128
|
-
return `${content.slice(0, operation.start)}${operation.to}${content.slice(operation.end)}`;
|
|
129
|
-
}
|
|
130
|
-
const raw = content.slice(operation.start, operation.end);
|
|
131
|
-
const offset = raw.indexOf(operation.from);
|
|
132
|
-
if (!operation.from || offset < 0 || raw.indexOf(operation.from, offset + operation.from.length) >= 0)
|
|
133
|
-
return null;
|
|
134
|
-
return `${content.slice(0, operation.start)}${raw.slice(0, offset)}${operation.to}${raw.slice(offset + operation.from.length)}${content.slice(operation.end)}`;
|
|
135
|
-
}
|
|
136
|
-
function canonicalContent(content, operation = null) {
|
|
66
|
+
function canonicalContent(content) {
|
|
137
67
|
let normalized = content;
|
|
138
|
-
if (operation) {
|
|
139
|
-
normalized = applyRepairOperation(normalized, operation) ?? normalized;
|
|
140
|
-
}
|
|
141
68
|
normalized = normalized.replace(/\r\n/g, '\n');
|
|
142
69
|
normalized = normalized.replace(/^<!--\s*artifact-context:[^\n]+-->\s*\n?/gm, '');
|
|
143
70
|
normalized = normalized.replace(/^\s*<!--\s*artifact-section:[^\n]+-->\s*\n?/gm, '');
|
|
144
71
|
normalized = normalized.replace(/^\s*<!--\s*artifact-slot:empty\s*-->\s*\n?/gm, '');
|
|
145
72
|
return normalized;
|
|
146
73
|
}
|
|
147
|
-
function canonicalSemanticDigest(content
|
|
148
|
-
return sha256Content(canonicalContent(content
|
|
74
|
+
function canonicalSemanticDigest(content) {
|
|
75
|
+
return sha256Content(canonicalContent(content));
|
|
149
76
|
}
|
|
150
77
|
function inspectArtifactStructure(content, schema) {
|
|
151
78
|
const diagnostics = [];
|
|
152
79
|
const scanned = scanVisibleMarkdown(content);
|
|
153
|
-
const repairCandidates = [];
|
|
154
80
|
if (!content.trim())
|
|
155
81
|
diagnostics.push(diagnostic('ARTIFACT_EMPTY', 'artifact is empty', null, null));
|
|
156
|
-
if (hasUnclosedFence
|
|
82
|
+
if (scanned.hasUnclosedFence)
|
|
157
83
|
diagnostics.push(diagnostic('ARTIFACT_UNCLOSED_FENCE', 'artifact contains an unclosed Markdown fence', null, null));
|
|
158
84
|
const headingsBySection = new Map();
|
|
159
85
|
for (const section of schema.sections) {
|
|
160
86
|
const aliases = sectionHeadings(schema, section);
|
|
161
87
|
const exact = scanned.headings.filter((heading) => heading.level === 2 && aliases.includes(heading.text));
|
|
162
88
|
const punctuated = scanned.headings.filter((heading) => heading.level === 2 && aliases.some((alias) => heading.text === `${alias}:` || heading.text === `${alias}:`));
|
|
163
|
-
headingsBySection.set(section.id, exact.length
|
|
89
|
+
headingsBySection.set(section.id, exact.length > 0 ? exact : punctuated);
|
|
164
90
|
if (exact.length > 1 || punctuated.length > 0 && exact.length > 0 || punctuated.length > 1) {
|
|
165
91
|
const first = exact[1] ?? punctuated[0] ?? exact[0];
|
|
166
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));
|
|
167
93
|
}
|
|
168
94
|
else if (exact.length === 0 && punctuated.length === 1) {
|
|
169
|
-
|
|
95
|
+
diagnostics.push(diagnostic('ARTIFACT_HEADING_TRAILING_PUNCTUATION', `visible required H2 '${punctuated[0].text}' has trailing punctuation`, section.id, lineNumber(content, punctuated[0].start)));
|
|
170
96
|
}
|
|
171
97
|
else if (exact.length === 0) {
|
|
172
98
|
diagnostics.push(diagnostic('ARTIFACT_MISSING_SECTION', `required section '${aliases[0]}' is missing`, section.id, null));
|
|
173
99
|
}
|
|
174
100
|
}
|
|
175
|
-
const markers = markerEntries(content);
|
|
176
|
-
const englishHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.en === heading.text)).length;
|
|
177
|
-
const chineseHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.zh === heading.text)).length;
|
|
178
|
-
const locale = englishHeadings > chineseHeadings ? 'en' : 'zh-CN';
|
|
101
|
+
const markers = markerEntries(content, scanned);
|
|
179
102
|
for (const section of schema.sections) {
|
|
180
103
|
const matches = markers.filter((entry) => entry.marker === section.marker);
|
|
181
104
|
if (matches.length === 0) {
|
|
@@ -190,7 +113,7 @@ function inspectArtifactStructure(content, schema) {
|
|
|
190
113
|
diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' must occupy its own visible line`, section.id, marker.line));
|
|
191
114
|
}
|
|
192
115
|
if (marker && heading) {
|
|
193
|
-
const bounds = sectionBodyBounds(content, heading);
|
|
116
|
+
const bounds = sectionBodyBounds(content, heading, scanned);
|
|
194
117
|
if (marker.start < bounds.start || marker.start >= bounds.end) {
|
|
195
118
|
diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' is outside its section`, section.id, marker.line));
|
|
196
119
|
}
|
|
@@ -201,10 +124,7 @@ function inspectArtifactStructure(content, schema) {
|
|
|
201
124
|
if (marker && !heading && marker.standalone) {
|
|
202
125
|
const nextHeading = scanned.headings.find((candidate) => candidate.start > marker.start && candidate.level <= 2);
|
|
203
126
|
const body = stripHtmlComments(content.slice(marker.end, nextHeading?.start ?? content.length)).trim();
|
|
204
|
-
if (body
|
|
205
|
-
repairCandidates.push(createSectionInsertionRepair(content, section, marker, locale));
|
|
206
|
-
}
|
|
207
|
-
else if (!body) {
|
|
127
|
+
if (!body) {
|
|
208
128
|
diagnostics.push(diagnostic('ARTIFACT_EMPTY_SECTION', `section '${section.headings.zh}' has no semantic body`, section.id, marker.line));
|
|
209
129
|
}
|
|
210
130
|
}
|
|
@@ -214,17 +134,6 @@ function inspectArtifactStructure(content, schema) {
|
|
|
214
134
|
diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `unknown section marker '${marker.marker}'`, null, marker.line));
|
|
215
135
|
}
|
|
216
136
|
}
|
|
217
|
-
for (const candidate of repairCandidates) {
|
|
218
|
-
if (candidate.kind === 'replace-line') {
|
|
219
|
-
diagnostics.push(diagnostic('ARTIFACT_HEADING_TRAILING_PUNCTUATION', `visible required H2 '${candidate.from}' may be normalized to '${candidate.to}'`, candidate.sectionId, candidate.line, true, candidate));
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
const missing = diagnostics.findIndex((item) => item.code === 'ARTIFACT_MISSING_SECTION' && item.sectionId === candidate.sectionId);
|
|
223
|
-
if (missing >= 0) {
|
|
224
|
-
const section = schema.sections.find((item) => item.id === candidate.sectionId);
|
|
225
|
-
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));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
137
|
const orderedHeadings = schema.sections.flatMap((section) => {
|
|
229
138
|
const heading = headingsBySection.get(section.id)?.[0];
|
|
230
139
|
return heading ? [{ section, start: heading.start }] : [];
|
|
@@ -239,14 +148,11 @@ function inspectArtifactStructure(content, schema) {
|
|
|
239
148
|
if (orderedMarkers.some((item, index) => index > 0 && item.start <= orderedMarkers[index - 1].start)) {
|
|
240
149
|
diagnostics.push(diagnostic('ARTIFACT_SECTION_ORDER_INVALID', 'section markers are not in schema order', null, null));
|
|
241
150
|
}
|
|
242
|
-
const repair = repairCandidates.length === 1 && (diagnostics.length === 0 ||
|
|
243
|
-
(diagnostics.length === 1 && diagnostics[0]?.operation?.kind === repairCandidates[0]?.kind)) ? repairCandidates[0] : null;
|
|
244
151
|
return {
|
|
245
152
|
ok: diagnostics.length === 0,
|
|
246
153
|
family: schema.family,
|
|
247
|
-
semanticDigest: canonicalSemanticDigest(content
|
|
248
|
-
diagnostics
|
|
249
|
-
repair
|
|
154
|
+
semanticDigest: canonicalSemanticDigest(content),
|
|
155
|
+
diagnostics
|
|
250
156
|
};
|
|
251
157
|
}
|
|
252
158
|
function inspectArtifactPatterns(content, schema) {
|
|
@@ -257,25 +163,22 @@ function inspectArtifactPatterns(content, schema) {
|
|
|
257
163
|
ok: diagnostics.length === 0,
|
|
258
164
|
family: schema.family,
|
|
259
165
|
semanticDigest: canonicalSemanticDigest(content),
|
|
260
|
-
diagnostics
|
|
261
|
-
repair: null
|
|
166
|
+
diagnostics
|
|
262
167
|
};
|
|
263
168
|
}
|
|
264
169
|
function inspectArtifactContract(content, schema) {
|
|
265
170
|
const structure = inspectArtifactStructure(content, schema);
|
|
266
171
|
const patterns = inspectArtifactPatterns(content, schema);
|
|
267
172
|
const diagnostics = [...structure.diagnostics, ...patterns.diagnostics];
|
|
268
|
-
const repair = patterns.diagnostics.length === 0 ? structure.repair : null;
|
|
269
173
|
return {
|
|
270
174
|
ok: diagnostics.length === 0,
|
|
271
175
|
family: schema.family,
|
|
272
|
-
semanticDigest: canonicalSemanticDigest(content
|
|
273
|
-
diagnostics
|
|
274
|
-
repair
|
|
176
|
+
semanticDigest: canonicalSemanticDigest(content),
|
|
177
|
+
diagnostics
|
|
275
178
|
};
|
|
276
179
|
}
|
|
277
180
|
function resultFailure(code, message) {
|
|
278
|
-
return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null,
|
|
181
|
+
return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, error: { code, message } };
|
|
279
182
|
}
|
|
280
183
|
function resultNoOp(content) {
|
|
281
184
|
return {
|
|
@@ -283,174 +186,12 @@ function resultNoOp(content) {
|
|
|
283
186
|
changed: false,
|
|
284
187
|
artifactSha256: sha256Content(content),
|
|
285
188
|
semanticDigest: canonicalSemanticDigest(content),
|
|
286
|
-
operation: null,
|
|
287
189
|
error: null
|
|
288
190
|
};
|
|
289
191
|
}
|
|
290
|
-
function validateRepairContext(request, content) {
|
|
291
|
-
const round = artifactRound(request.family, request.artifact);
|
|
292
|
-
if (round === null)
|
|
293
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact round is not canonical' };
|
|
294
|
-
const contextPattern = /^<!--\s*artifact-context:([^:\s]+):([^:\s]+):(\d+)\s*-->\s*$/;
|
|
295
|
-
const contexts = scanVisibleMarkdown(content).lines
|
|
296
|
-
.map((line) => line.text.trim().match(contextPattern))
|
|
297
|
-
.filter((match) => match !== null);
|
|
298
|
-
if (contexts.length !== 1
|
|
299
|
-
|| contexts[0][1] !== request.taskId
|
|
300
|
-
|| contexts[0][2] !== request.family
|
|
301
|
-
|| Number(contexts[0][3]) !== round) {
|
|
302
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact context marker does not match the requested task, family, and round' };
|
|
303
|
-
}
|
|
304
|
-
let taskContent;
|
|
305
|
-
try {
|
|
306
|
-
taskContent = fs.readFileSync(path.join(request.taskDir, 'task.md'), 'utf8');
|
|
307
|
-
}
|
|
308
|
-
catch (error) {
|
|
309
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `cannot read task lifecycle context: ${String(error)}` };
|
|
310
|
-
}
|
|
311
|
-
const activity = locateActivityLog(taskContent);
|
|
312
|
-
if (!activity)
|
|
313
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'task has no unique Activity Log section' };
|
|
314
|
-
const label = {
|
|
315
|
-
analysis: 'Analyze Task',
|
|
316
|
-
'review-analysis': 'Review Analysis',
|
|
317
|
-
plan: 'Plan Task',
|
|
318
|
-
'review-plan': 'Review Plan',
|
|
319
|
-
code: 'Code Task',
|
|
320
|
-
'review-code': 'Review Code'
|
|
321
|
-
};
|
|
322
|
-
const escaped = escapeRegExp(label[request.family]);
|
|
323
|
-
const qualifier = request.family === 'code'
|
|
324
|
-
? '(?:, (?:fix for review-code(?:-r(?:[2-9]|[1-9]\\d+))?\\.md|decision II-[1-9]\\d+))?'
|
|
325
|
-
: '';
|
|
326
|
-
const expected = new RegExp(`^${escaped} \\(Round ${round}${qualifier}\\)$`);
|
|
327
|
-
const open = startedBackedRows(pairEntries(activity.entries)).filter((row) => expected.test(row.step) && !row.done);
|
|
328
|
-
if (open.length !== 1) {
|
|
329
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `artifact '${request.artifact}' does not have exactly one matching open started lifecycle event` };
|
|
330
|
-
}
|
|
331
|
-
let intent;
|
|
332
|
-
try {
|
|
333
|
-
intent = readArtifactRepairIntent(request.repoRoot, request.taskId, request.family, request.artifact);
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: String(error) };
|
|
337
|
-
}
|
|
338
|
-
if (!intent || intent.state !== 'awaiting-repair') {
|
|
339
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'artifact has no awaiting-repair finalization provenance' };
|
|
340
|
-
}
|
|
341
|
-
if (intent.artifactSha256 !== request.expectedSha256 || intent.semanticDigest !== request.expectedSemanticDigest) {
|
|
342
|
-
return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'repair request does not match the current finalization provenance' };
|
|
343
|
-
}
|
|
344
|
-
return { ok: true };
|
|
345
|
-
}
|
|
346
192
|
function artifactRound(family, artifact) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const match = artifact.match(new RegExp(`^${escapeRegExp(family)}-r([2-9]|[1-9]\\d+)\\.md$`));
|
|
350
|
-
if (!match)
|
|
351
|
-
return null;
|
|
352
|
-
const round = Number(match[1]);
|
|
353
|
-
return Number.isSafeInteger(round) ? round : null;
|
|
354
|
-
}
|
|
355
|
-
function validateTarget(taskDir, family, artifact) {
|
|
356
|
-
if (path.basename(artifact) !== artifact || artifactRound(family, artifact) === null || !artifact.endsWith('.md'))
|
|
357
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be a canonical top-level Markdown file');
|
|
358
|
-
if (!/^TASK-\d{8}-\d{6}$/.test(path.basename(path.resolve(taskDir))))
|
|
359
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'target directory is not a task directory');
|
|
360
|
-
const target = path.join(taskDir, artifact);
|
|
361
|
-
if (path.dirname(path.resolve(target)) !== path.resolve(taskDir))
|
|
362
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be inside the current task directory');
|
|
363
|
-
let stat;
|
|
364
|
-
try {
|
|
365
|
-
stat = fs.lstatSync(target);
|
|
366
|
-
}
|
|
367
|
-
catch (error) {
|
|
368
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact cannot be read: ${String(error)}`);
|
|
369
|
-
}
|
|
370
|
-
if (stat.isSymbolicLink() || !stat.isFile())
|
|
371
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact is not a regular file');
|
|
372
|
-
try {
|
|
373
|
-
fs.accessSync(target, fs.constants.R_OK | fs.constants.W_OK);
|
|
374
|
-
}
|
|
375
|
-
catch (error) {
|
|
376
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact is not readable and writable: ${String(error)}`);
|
|
377
|
-
}
|
|
378
|
-
return { path: target };
|
|
379
|
-
}
|
|
380
|
-
function applyRepairUnlocked(request) {
|
|
381
|
-
const schema = getArtifactSchema(request.family);
|
|
382
|
-
if (!schema)
|
|
383
|
-
return resultFailure('ARTIFACT_FAMILY_UNKNOWN', `unknown artifact family '${request.family}'`);
|
|
384
|
-
const target = validateTarget(request.taskDir, request.family, request.artifact);
|
|
385
|
-
if ('status' in target)
|
|
386
|
-
return target;
|
|
387
|
-
let content;
|
|
388
|
-
try {
|
|
389
|
-
content = fs.readFileSync(target.path, 'utf8');
|
|
390
|
-
}
|
|
391
|
-
catch (error) {
|
|
392
|
-
return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', String(error));
|
|
393
|
-
}
|
|
394
|
-
const actualSha256 = sha256Content(content);
|
|
395
|
-
if (actualSha256 !== request.expectedSha256)
|
|
396
|
-
return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact SHA-256 does not match the expected repair baseline');
|
|
397
|
-
const context = validateRepairContext(request, content);
|
|
398
|
-
if (!context.ok)
|
|
399
|
-
return resultFailure(context.code, context.message);
|
|
400
|
-
const inspection = inspectArtifactStructure(content, schema);
|
|
401
|
-
if (!inspection.repair || inspection.diagnostics.length !== 1) {
|
|
402
|
-
return resultFailure('ARTIFACT_REPAIR_UNSAFE', inspection.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ') || 'no deterministic structural repair is available');
|
|
403
|
-
}
|
|
404
|
-
if (inspection.repair.kind !== request.operation.kind
|
|
405
|
-
|| inspection.repair.sectionId !== request.operation.sectionId
|
|
406
|
-
|| inspection.repair.from !== request.operation.from
|
|
407
|
-
|| inspection.repair.to !== request.operation.to
|
|
408
|
-
|| inspection.repair.start !== request.operation.start
|
|
409
|
-
|| inspection.repair.end !== request.operation.end) {
|
|
410
|
-
return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact structure');
|
|
411
|
-
}
|
|
412
|
-
if (inspection.semanticDigest !== request.expectedSemanticDigest)
|
|
413
|
-
return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact semantic digest does not match the expected repair baseline');
|
|
414
|
-
const transformed = applyRepairOperation(content, inspection.repair);
|
|
415
|
-
if (transformed === null)
|
|
416
|
-
return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact bytes');
|
|
417
|
-
if (transformed === content)
|
|
418
|
-
return resultFailure('ARTIFACT_REPAIR_NO_PROGRESS', 'repair operation produced no byte change');
|
|
419
|
-
const tempPath = path.join(request.taskDir, `.${request.artifact}.repair-${process.pid}-${Date.now()}.tmp`);
|
|
420
|
-
let mode = 0o600;
|
|
421
|
-
try {
|
|
422
|
-
mode = fs.statSync(target.path).mode & 0o777;
|
|
423
|
-
fs.writeFileSync(tempPath, transformed, { encoding: 'utf8', mode, flag: 'wx' });
|
|
424
|
-
const currentStat = fs.lstatSync(target.path);
|
|
425
|
-
if (currentStat.isSymbolicLink() || !currentStat.isFile() || sha256Content(fs.readFileSync(target.path, 'utf8')) !== actualSha256) {
|
|
426
|
-
fs.unlinkSync(tempPath);
|
|
427
|
-
return resultFailure('ARTIFACT_REPAIR_CONFLICT', 'artifact changed or was replaced during repair');
|
|
428
|
-
}
|
|
429
|
-
fs.renameSync(tempPath, target.path);
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
try {
|
|
433
|
-
fs.unlinkSync(tempPath);
|
|
434
|
-
}
|
|
435
|
-
catch { /* preserve primary error */ }
|
|
436
|
-
return resultFailure('ARTIFACT_REPAIR_WRITE_FAILED', String(error));
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
status: 'applied',
|
|
440
|
-
changed: true,
|
|
441
|
-
artifactSha256: sha256Content(transformed),
|
|
442
|
-
semanticDigest: canonicalSemanticDigest(transformed),
|
|
443
|
-
operation: inspection.repair,
|
|
444
|
-
error: null
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
function applyArtifactRepair(request) {
|
|
448
|
-
try {
|
|
449
|
-
return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.repair', () => applyRepairUnlocked(request));
|
|
450
|
-
}
|
|
451
|
-
catch (error) {
|
|
452
|
-
return resultFailure('ARTIFACT_REPAIR_LOCK_FAILED', String(error));
|
|
453
|
-
}
|
|
193
|
+
const identity = parseArtifactName(artifact);
|
|
194
|
+
return identity?.family === family ? identity.round : null;
|
|
454
195
|
}
|
|
455
196
|
function initializeArtifactSkeleton(request) {
|
|
456
197
|
const schema = getArtifactSchema(request.family);
|
|
@@ -469,7 +210,7 @@ function initializeArtifactSkeleton(request) {
|
|
|
469
210
|
const existing = fs.lstatSync(target);
|
|
470
211
|
if (existing.isSymbolicLink() || !existing.isFile())
|
|
471
212
|
return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact target is not a regular file');
|
|
472
|
-
return resultNoOp(
|
|
213
|
+
return resultNoOp(readStableFileSync(target, { maxBytes: 1024 * 1024 }).bytes.toString('utf8'));
|
|
473
214
|
}
|
|
474
215
|
catch (error) {
|
|
475
216
|
if (error.code !== 'ENOENT')
|
|
@@ -507,7 +248,6 @@ function initializeArtifactSkeleton(request) {
|
|
|
507
248
|
changed: true,
|
|
508
249
|
artifactSha256: sha256Content(content),
|
|
509
250
|
semanticDigest: canonicalSemanticDigest(content),
|
|
510
|
-
operation: null,
|
|
511
251
|
error: null
|
|
512
252
|
};
|
|
513
253
|
});
|
|
@@ -516,5 +256,5 @@ function initializeArtifactSkeleton(request) {
|
|
|
516
256
|
return resultFailure('ARTIFACT_INIT_LOCK_FAILED', String(error));
|
|
517
257
|
}
|
|
518
258
|
}
|
|
519
|
-
export {
|
|
259
|
+
export { canonicalSemanticDigest, initializeArtifactSkeleton, inspectArtifactContract, inspectArtifactPatterns, inspectArtifactStructure, sha256Content };
|
|
520
260
|
//# sourceMappingURL=artifact-operations.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
3
4
|
import { extractSection, findSectionHeading, parseTable } from './sections.js';
|
|
4
5
|
const RECEIPT_SECTION_ALIASES = ['产物生命周期收据', 'Artifact Lifecycle Receipts'];
|
|
5
6
|
const RECEIPT_COLUMNS = ['event', 'output', 'input', 'input_sha256', 'completed_at'];
|
|
@@ -10,7 +11,6 @@ const RECEIPT_EVENTS = new Set([
|
|
|
10
11
|
'code.completed'
|
|
11
12
|
]);
|
|
12
13
|
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
13
|
-
const ARTIFACT_NAME_RE = /^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|validation-run|pr-review)(?:-r([2-9]|[1-9]\d+))?\.md$/;
|
|
14
14
|
const RECEIPT_SHAPES = {
|
|
15
15
|
'review-analysis.completed': { output: 'review-analysis', input: 'analysis' },
|
|
16
16
|
'review-plan.completed': { output: 'review-plan', input: 'plan' },
|
|
@@ -30,21 +30,9 @@ function sha256Bytes(value) {
|
|
|
30
30
|
function sha256File(filePath) {
|
|
31
31
|
return sha256Bytes(fs.readFileSync(filePath));
|
|
32
32
|
}
|
|
33
|
-
function parseReceiptArtifactName(name) {
|
|
34
|
-
const match = ARTIFACT_NAME_RE.exec(name);
|
|
35
|
-
if (!match)
|
|
36
|
-
return null;
|
|
37
|
-
const roundText = match[2];
|
|
38
|
-
if (roundText !== undefined) {
|
|
39
|
-
const round = Number(roundText);
|
|
40
|
-
if (!Number.isSafeInteger(round) || String(round) !== roundText)
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
return { family: match[1] };
|
|
44
|
-
}
|
|
45
33
|
function validateReceiptShape(event, output, input) {
|
|
46
|
-
const outputIdentity =
|
|
47
|
-
const inputIdentity =
|
|
34
|
+
const outputIdentity = parseArtifactName(output);
|
|
35
|
+
const inputIdentity = parseArtifactName(input);
|
|
48
36
|
if (!outputIdentity || !inputIdentity)
|
|
49
37
|
throw new ArtifactReceiptError(`receipt artifact identity is invalid: ${output} -> ${input}`);
|
|
50
38
|
const shape = RECEIPT_SHAPES[event];
|