@fitlab-ai/agent-infra 0.9.7 → 0.9.9
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +3 -4
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +161 -288
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/pr-summary.js +53 -13
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +145 -5
- package/dist/lib/sandbox/control/lifecycle.js +395 -73
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +279 -17
- package/dist/lib/task/events.js +9 -5
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +199 -719
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +138 -59
- package/dist/lib/task/verification.js +15 -2
- package/dist/lib/task/workflow-warning-intents.js +54 -3
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/git/workflow.ts +11 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +3 -4
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +32 -107
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/pr-summary.ts +65 -14
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +172 -6
- package/lib/sandbox/control/lifecycle.ts +465 -77
- package/lib/sandbox/control/protocol.ts +85 -9
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +73 -43
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +304 -19
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +719 -0
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +239 -922
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +145 -64
- package/lib/task/verification.ts +18 -3
- package/lib/task/workflow-warning-intents.ts +61 -4
- package/lib/task/write.ts +4 -1
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- 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/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- 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 +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -8,6 +8,7 @@ import { finalizeReviewSummaryContent } from './review-artifacts.js';
|
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
9
|
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
10
10
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
11
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
11
12
|
const STAGES = {
|
|
12
13
|
analysis: { family: 'review-analysis', action: 'Review Analysis' },
|
|
13
14
|
plan: { family: 'review-plan', action: 'Review Plan' },
|
|
@@ -60,7 +61,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
60
61
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
61
62
|
if (!resolved.ok)
|
|
62
63
|
return failed(request, resolved.code, resolved.message, resolved.taskId);
|
|
63
|
-
if (resolved.state !== 'active') {
|
|
64
|
+
if (resolved.state !== 'active' && !allowsManualOverride(options.manualOverride, 'review-finalization', 'TASK_STATE_MISMATCH')) {
|
|
64
65
|
return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
65
66
|
}
|
|
66
67
|
const parsedArtifact = parseArtifactName(request.artifact);
|
|
@@ -168,7 +169,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
function finalizeReviewSummary(request, options = {}) {
|
|
171
|
-
if (request.dryRun)
|
|
172
|
+
if (request.dryRun || options.lockAlreadyHeld)
|
|
172
173
|
return finalizeReviewSummaryUnlocked(request, options);
|
|
173
174
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
174
175
|
if (!resolved.ok)
|
|
@@ -343,6 +343,64 @@ function loadShortIdByTaskId(repoRoot) {
|
|
|
343
343
|
}
|
|
344
344
|
return map;
|
|
345
345
|
}
|
|
346
|
+
function inspectShortIdRegistry(repoRoot) {
|
|
347
|
+
const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
|
|
348
|
+
if (!fs.existsSync(registryPath)) {
|
|
349
|
+
return {
|
|
350
|
+
status: 'missing',
|
|
351
|
+
error: {
|
|
352
|
+
code: 'SHORT_ID_REGISTRY_NOT_FOUND',
|
|
353
|
+
message: 'canonical short-id registry is missing'
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
let raw;
|
|
358
|
+
try {
|
|
359
|
+
raw = fs.readFileSync(registryPath, 'utf8');
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return {
|
|
363
|
+
status: 'invalid',
|
|
364
|
+
error: {
|
|
365
|
+
code: 'SHORT_ID_REGISTRY_READ_FAILED',
|
|
366
|
+
message: 'canonical short-id registry is unreadable'
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
let parsed;
|
|
371
|
+
try {
|
|
372
|
+
parsed = JSON.parse(raw);
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return {
|
|
376
|
+
status: 'invalid',
|
|
377
|
+
error: {
|
|
378
|
+
code: 'SHORT_ID_REGISTRY_INVALID_JSON',
|
|
379
|
+
message: 'canonical short-id registry contains invalid JSON'
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
|
|
385
|
+
const shortIds = new Map();
|
|
386
|
+
for (const [key, taskId] of Object.entries(registry.ids))
|
|
387
|
+
shortIds.set(taskId, key);
|
|
388
|
+
return { status: 'valid', shortIds };
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
return {
|
|
392
|
+
status: 'invalid',
|
|
393
|
+
error: {
|
|
394
|
+
code: typeof error.code === 'string'
|
|
395
|
+
? error.code
|
|
396
|
+
: 'SHORT_ID_REGISTRY_INVALID_SCHEMA',
|
|
397
|
+
message: error.code === 'SHORT_ID_REGISTRY_DUPLICATE_TASK'
|
|
398
|
+
? 'canonical short-id registry contains duplicate task entries'
|
|
399
|
+
: 'canonical short-id registry has invalid schema'
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
346
404
|
/**
|
|
347
405
|
* Resolve a branch to its active-task short id (`NN`), or `null` when no
|
|
348
406
|
* active task is bound to that branch.
|
|
@@ -371,5 +429,5 @@ function lookupShortIdByBranch(branch, repoRoot, _opts) {
|
|
|
371
429
|
}
|
|
372
430
|
return matches[0];
|
|
373
431
|
}
|
|
374
|
-
export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
|
|
432
|
+
export { normalizeShortIdInput, isRemovedHashShortIdInput, resolveShortIdReadOnly, lookupShortIdByBranch, loadShortIdByTaskId, inspectShortIdRegistry, configuredShortIdLength, mutateShortIdRegistry, executeShortIdCommand };
|
|
375
433
|
//# sourceMappingURL=short-id.js.map
|
|
@@ -207,6 +207,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
|
|
|
207
207
|
catch (error) {
|
|
208
208
|
if (error instanceof TaskExecutionLockError)
|
|
209
209
|
throw error;
|
|
210
|
+
if (error instanceof Error && error.name === 'OrchestrationStateError')
|
|
211
|
+
throw error;
|
|
210
212
|
throw new TaskExecutionLockError('ORCHESTRATION_LOCK_FAILED', 'lifecycle task lock operation failed', { operation: ownsFixed ? 'callback' : 'acquire', errno: errno(error), key });
|
|
211
213
|
}
|
|
212
214
|
finally {
|
|
@@ -228,5 +230,8 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
|
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
}
|
|
231
|
-
|
|
233
|
+
function withRepositoryMutationLock(repoRoot, callback, options = {}) {
|
|
234
|
+
return withTaskExecutionLock(repoRoot, '__repository_mutation__', 'repository-mutation', callback, options);
|
|
235
|
+
}
|
|
236
|
+
export { TaskExecutionLockError, lockKey, mapLinkError, withRepositoryMutationLock, withTaskExecutionLock };
|
|
232
237
|
//# sourceMappingURL=task-execution-lock.js.map
|
|
@@ -16,6 +16,7 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.
|
|
|
16
16
|
import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
|
|
17
17
|
import { loadVerificationConfig } from "./verification-config.js";
|
|
18
18
|
import { snapshotReview } from "../git/review-snapshot.js";
|
|
19
|
+
import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
19
20
|
const TASK_ENUMS = {
|
|
20
21
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
21
22
|
workflow: ["feature-development", "bug-fix", "refactoring"],
|
|
@@ -104,6 +105,8 @@ function runCheck(type, context, shared) {
|
|
|
104
105
|
return checkReviewFact(context);
|
|
105
106
|
case "post-review-commit":
|
|
106
107
|
return checkPostReviewCommit(context);
|
|
108
|
+
case "required-pr-delivery":
|
|
109
|
+
return checkRequiredPrDelivery(context);
|
|
107
110
|
case "orchestration-state":
|
|
108
111
|
return checkOrchestrationState(context);
|
|
109
112
|
case "orchestration-evidence":
|
|
@@ -120,22 +123,6 @@ function runCheck(type, context, shared) {
|
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
|
-
function isFailClosedLegacyPause(run) {
|
|
124
|
-
if (run.schemaVersion !== 1
|
|
125
|
-
|| run.status !== 'paused'
|
|
126
|
-
|| run.pause?.recoverable !== false
|
|
127
|
-
|| !exactText(run.pause?.message)
|
|
128
|
-
|| !Array.isArray(run.receipts)) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
if (run.pause.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED') {
|
|
132
|
-
return run.receipts.length > 0 && run.pendingDelegation === null;
|
|
133
|
-
}
|
|
134
|
-
if (run.pause.code === 'ORCHESTRATION_DELEGATION_BUSY') {
|
|
135
|
-
return run.pendingDelegation != null && run.pendingDelegation.status !== 'sealed';
|
|
136
|
-
}
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
126
|
function checkOrchestrationState({ taskDir }) {
|
|
140
127
|
const file = path.join(taskDir, 'orchestration.json');
|
|
141
128
|
const stat = safeStat(file);
|
|
@@ -143,16 +130,14 @@ function checkOrchestrationState({ taskDir }) {
|
|
|
143
130
|
return failResult('orchestration-state', 'orchestration.json is missing');
|
|
144
131
|
let run;
|
|
145
132
|
try {
|
|
146
|
-
run =
|
|
133
|
+
run = readRun(taskDir);
|
|
147
134
|
}
|
|
148
135
|
catch (error) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (isFailClosedLegacyPause(run)) {
|
|
152
|
-
return passResult('orchestration-state', 'Legacy orchestration run is safely paused');
|
|
136
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
137
|
+
return failResult('orchestration-state', `Invalid orchestration.json: ${message}`);
|
|
153
138
|
}
|
|
154
|
-
if (!
|
|
155
|
-
return failResult('orchestration-state', `Expected paused or completed run, received '${run
|
|
139
|
+
if (!run || !['paused', 'completed'].includes(run.status)) {
|
|
140
|
+
return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
|
|
156
141
|
}
|
|
157
142
|
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
158
143
|
return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
|
|
@@ -215,13 +200,11 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
215
200
|
return failResult('orchestration-evidence', 'orchestration.json is missing');
|
|
216
201
|
let run;
|
|
217
202
|
try {
|
|
218
|
-
run =
|
|
203
|
+
run = readRun(taskDir);
|
|
219
204
|
}
|
|
220
205
|
catch (error) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (isFailClosedLegacyPause(run)) {
|
|
224
|
-
return passResult('orchestration-evidence', 'Legacy orchestration evidence is preserved in a fail-closed pause');
|
|
206
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
207
|
+
return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
|
|
225
208
|
}
|
|
226
209
|
const policy = run.modelPolicy;
|
|
227
210
|
if (!policy
|
|
@@ -239,18 +222,11 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
239
222
|
return failResult('orchestration-evidence', 'Run model policy source or recovery history is invalid');
|
|
240
223
|
}
|
|
241
224
|
for (const recovery of run.recoveryHistory) {
|
|
242
|
-
const validModelRecovery = recovery.code === 'MODEL_POLICY_SUPPLEMENTED'
|
|
243
|
-
&& recovery.previousSchemaVersion === 1
|
|
244
|
-
&& recovery.receiptCount === 0
|
|
245
|
-
&& recovery.pendingDelegation === false
|
|
246
|
-
&& exactText(recovery.recoveredAt)
|
|
247
|
-
&& recovery.policySource;
|
|
248
225
|
const guards = recovery.guards;
|
|
249
|
-
const
|
|
250
|
-
&& recovery.previousSchemaVersion === 2
|
|
226
|
+
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED'
|
|
251
227
|
&& recovery.previousStatus === 'paused'
|
|
252
228
|
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
253
|
-
&& recovery.client === '
|
|
229
|
+
&& recovery.client === 'claude-code'
|
|
254
230
|
&& recovery.resultingStatus === 'running'
|
|
255
231
|
&& exactText(recovery.recoveredAt)
|
|
256
232
|
&& guards?.stepCount === 0
|
|
@@ -259,9 +235,8 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
259
235
|
&& guards?.receiptCount === 0
|
|
260
236
|
&& guards?.pendingDelegation === false
|
|
261
237
|
&& guards?.commitAuthorizationUnused === true
|
|
262
|
-
&& guards?.completionEvidenceAbsent === true
|
|
263
|
-
|
|
264
|
-
if (!validModelRecovery && !validCapabilityRecovery) {
|
|
238
|
+
&& guards?.completionEvidenceAbsent === true;
|
|
239
|
+
if (!validClaudeCodeRecovery) {
|
|
265
240
|
return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
|
|
266
241
|
}
|
|
267
242
|
}
|
|
@@ -292,35 +267,69 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
292
267
|
}
|
|
293
268
|
const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
294
269
|
if (activated) {
|
|
295
|
-
|
|
296
|
-
|
|
270
|
+
const isClaudeCode = receipt.client === 'claude-code';
|
|
271
|
+
if (!isClaudeCode) {
|
|
272
|
+
if (!exactText(receipt.actualModel)) {
|
|
273
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
|
|
274
|
+
}
|
|
275
|
+
if (!exactText(receipt.actualReasoningEffort)) {
|
|
276
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
|
|
277
|
+
}
|
|
297
278
|
}
|
|
298
|
-
|
|
299
|
-
|
|
279
|
+
// parentId/childId 校验对所有 client 保持统一(不新增分支);spawnMode 检查按 client 判断
|
|
280
|
+
if (!exactText(receipt.parentId) || !exactText(receipt.childId) || receipt.parentId === receipt.childId) {
|
|
281
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid delegation identity`);
|
|
300
282
|
}
|
|
301
|
-
if (!
|
|
283
|
+
if (!isClaudeCode && receipt.spawnMode !== 'fresh') {
|
|
302
284
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
|
|
303
285
|
}
|
|
304
|
-
if (receipt.actualModel
|
|
305
|
-
|
|
286
|
+
if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
|
|
287
|
+
if (!isClaudeCode && !exactText(receipt.modelFallbackReason)) {
|
|
288
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
|
|
289
|
+
}
|
|
306
290
|
}
|
|
307
|
-
if (receipt.actualModel
|
|
291
|
+
if ((receipt.actualModel == null || receipt.actualModel === receipt.requestedModel)
|
|
292
|
+
&& receipt.modelFallbackReason !== null) {
|
|
308
293
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
|
|
309
294
|
}
|
|
310
|
-
if (receipt.actualReasoningEffort !== receipt.requestedReasoningEffort
|
|
311
|
-
&& !exactText(receipt.reasoningEffortFallbackReason)) {
|
|
312
|
-
|
|
295
|
+
if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
296
|
+
if (!isClaudeCode && !exactText(receipt.reasoningEffortFallbackReason)) {
|
|
297
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
|
|
298
|
+
}
|
|
313
299
|
}
|
|
314
|
-
if (receipt.actualReasoningEffort === receipt.requestedReasoningEffort
|
|
300
|
+
if ((receipt.actualReasoningEffort == null || receipt.actualReasoningEffort === receipt.requestedReasoningEffort)
|
|
315
301
|
&& receipt.reasoningEffortFallbackReason !== null) {
|
|
316
302
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
|
|
317
303
|
}
|
|
318
304
|
if (receipt.client === 'codex') {
|
|
305
|
+
const activatedCodex = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
319
306
|
const host = receipt.hostEvidence;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
||
|
|
323
|
-
|
|
307
|
+
const provenance = receipt.lifecycleProvenance;
|
|
308
|
+
if (!provenance
|
|
309
|
+
|| (activatedCodex && (host?.kind !== 'codex-lifecycle-v2'
|
|
310
|
+
|| host.protocolVersion !== provenance.protocolVersion
|
|
311
|
+
|| host.packageVersion !== provenance.packageVersion
|
|
312
|
+
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
313
|
+
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
314
|
+
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
315
|
+
|| host.hookSource !== provenance.hookSource
|
|
316
|
+
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
317
|
+
|| host.hookSourceHash !== provenance.hookSourceHash
|
|
318
|
+
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
319
|
+
|| receipt.parentId !== provenance.capabilitySessionId
|
|
320
|
+
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
321
|
+
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
322
|
+
|| host.controlGeneration !== provenance.controlGeneration
|
|
323
|
+
|| host.spawnToolUseId === provenance.capabilityToolUseId
|
|
324
|
+
|| !exactText(host.spawnToolUseId)
|
|
325
|
+
|| !Number.isFinite(Date.parse(host.spawnObservedAt ?? ''))
|
|
326
|
+
|| !Number.isFinite(Date.parse(receipt.spawnDispatchedAt ?? ''))
|
|
327
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt ?? ''))
|
|
328
|
+
|| Date.parse(host.spawnObservedAt ?? '') < Date.parse(receipt.spawnDispatchedAt ?? '')
|
|
329
|
+
|| Date.parse(host.spawnObservedAt ?? '') > Date.parse(receipt.activationDeadlineAt ?? '')
|
|
330
|
+
|| !exactText(host.hookDefinitionHash)
|
|
331
|
+
|| !Number.isSafeInteger(host.startRevision)
|
|
332
|
+
|| host.startRevision < 1))) {
|
|
324
333
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid Codex start evidence`);
|
|
325
334
|
}
|
|
326
335
|
if (['sealed', 'consumed'].includes(receipt.status) && (!Number.isSafeInteger(host.stopRevision)
|
|
@@ -412,6 +421,60 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
412
421
|
const warningSuffix = warnings.length > 0 ? `; warnings: ${warnings.join("; ")}` : "";
|
|
413
422
|
return passResult("task-meta", `Task metadata valid (${requiredFields.length} required fields checked${warningSuffix})`, warnings);
|
|
414
423
|
}
|
|
424
|
+
function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
|
|
425
|
+
const task = loadTask(taskDir);
|
|
426
|
+
if (!task.ok)
|
|
427
|
+
return failResult('required-pr-delivery', task.message);
|
|
428
|
+
let projectConfig = {};
|
|
429
|
+
try {
|
|
430
|
+
const file = path.join(repositoryRoot, '.agents', '.airc.json');
|
|
431
|
+
if (safeStat(file)?.isFile())
|
|
432
|
+
projectConfig = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return failResult('required-pr-delivery', 'Project configuration is invalid');
|
|
436
|
+
}
|
|
437
|
+
const flow = projectConfig.prFlow;
|
|
438
|
+
const status = String(task.metadata.pr_status || 'pending');
|
|
439
|
+
const prNumber = parsePrNumber(task.metadata.pr_number);
|
|
440
|
+
if (flow === 'disabled')
|
|
441
|
+
return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
|
|
442
|
+
if (flow === 'required' && (status !== 'created' || prNumber === null)) {
|
|
443
|
+
return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
|
|
444
|
+
}
|
|
445
|
+
if (flow !== 'required' && status === 'pending') {
|
|
446
|
+
return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
|
|
447
|
+
}
|
|
448
|
+
if (status === 'created' && prNumber === null) {
|
|
449
|
+
return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
|
|
450
|
+
}
|
|
451
|
+
if (flow === 'required') {
|
|
452
|
+
if (mode === 'gate') {
|
|
453
|
+
return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
|
|
454
|
+
}
|
|
455
|
+
const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
|
|
456
|
+
if (inspected.status === 'blocked') {
|
|
457
|
+
return blockedResult('required-pr-delivery', inspected.error?.message || 'Unable to inspect the required pull request', inspected.error?.code || 'PR_INSPECTION_BLOCKED');
|
|
458
|
+
}
|
|
459
|
+
const pullRequest = inspected.pullRequest;
|
|
460
|
+
const repository = inspected.platform.repository?.toLowerCase() || '';
|
|
461
|
+
const taskBranch = String(task.metadata.branch || '').trim();
|
|
462
|
+
if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
|
|
463
|
+
return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
|
|
464
|
+
}
|
|
465
|
+
if (!repository
|
|
466
|
+
|| pullRequest.repository.toLowerCase() !== repository
|
|
467
|
+
|| pullRequest.base.repository.toLowerCase() !== repository
|
|
468
|
+
|| (taskBranch && pullRequest.head.ref !== taskBranch)) {
|
|
469
|
+
return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
|
|
470
|
+
}
|
|
471
|
+
if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
|
|
472
|
+
return failResult('required-pr-delivery', 'Project policy requires the bound pull request to be merged');
|
|
473
|
+
}
|
|
474
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR #${prNumber})`);
|
|
475
|
+
}
|
|
476
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (${status})`);
|
|
477
|
+
}
|
|
415
478
|
function validateTaskBranch(metadata) {
|
|
416
479
|
if (isBlank(metadata.branch)) {
|
|
417
480
|
return null;
|
|
@@ -774,7 +837,7 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
774
837
|
return passResult("review-ledger", "Disagreement ledger has no entries");
|
|
775
838
|
}
|
|
776
839
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
777
|
-
const maxRounds = Number(resolveReviewSetting(config, "
|
|
840
|
+
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS));
|
|
778
841
|
const problems = [];
|
|
779
842
|
let inScopeCount = 0;
|
|
780
843
|
for (const cells of rows) {
|
|
@@ -1311,7 +1374,14 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1311
1374
|
for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
|
|
1312
1375
|
if (checkConfig === null)
|
|
1313
1376
|
continue;
|
|
1314
|
-
const result = runCheck(type, {
|
|
1377
|
+
const result = runCheck(type, {
|
|
1378
|
+
mode,
|
|
1379
|
+
skillName,
|
|
1380
|
+
taskDir: path.resolve(taskDir),
|
|
1381
|
+
artifactFile,
|
|
1382
|
+
repositoryRoot: repoRoot,
|
|
1383
|
+
config: checkConfig
|
|
1384
|
+
}, shared);
|
|
1315
1385
|
checks.push(result);
|
|
1316
1386
|
if (result.status === "blocked")
|
|
1317
1387
|
break;
|
|
@@ -1325,7 +1395,16 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1325
1395
|
return { skill: skillName, ...failResult(type, `Unknown check type '${type}' for skill '${skillName}'.`) };
|
|
1326
1396
|
if (config === null)
|
|
1327
1397
|
return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
|
|
1328
|
-
return {
|
|
1398
|
+
return {
|
|
1399
|
+
skill: skillName,
|
|
1400
|
+
...runCheck(type, {
|
|
1401
|
+
skillName,
|
|
1402
|
+
taskDir: path.resolve(taskDir),
|
|
1403
|
+
artifactFile,
|
|
1404
|
+
repositoryRoot: repoRoot,
|
|
1405
|
+
config
|
|
1406
|
+
}, shared)
|
|
1407
|
+
};
|
|
1329
1408
|
}
|
|
1330
1409
|
export { verifyInProcess };
|
|
1331
1410
|
//# sourceMappingURL=verification-engine.js.map
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { verifyInProcess } from './verification-engine.js';
|
|
5
5
|
import { parseArtifactName } from './artifact-lifecycle.js';
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
7
8
|
const gate = (skill, expectedState, artifactFamily) => ({
|
|
8
9
|
skill, expectedState, mode: 'gate', ...(artifactFamily ? { artifactFamily } : {})
|
|
9
10
|
});
|
|
@@ -20,7 +21,8 @@ const VERIFICATION_CATALOG = {
|
|
|
20
21
|
'block-task.completed': gate('block-task', 'blocked'),
|
|
21
22
|
'cancel-task.completed': gate('cancel-task', 'completed'),
|
|
22
23
|
'commit.completed': gate('commit', 'active'),
|
|
23
|
-
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['
|
|
24
|
+
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
25
|
+
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
24
26
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
25
27
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
26
28
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -47,7 +49,7 @@ function verifyTaskEvent(request, options = {}) {
|
|
|
47
49
|
if (!resolved.ok)
|
|
48
50
|
return failure(request, resolved.code, resolved.message, { taskId: resolved.taskId, skill: spec.skill, mode: spec.mode });
|
|
49
51
|
const identity = { taskId: resolved.taskId, taskDir: resolved.taskDir, taskState: resolved.state, skill: spec.skill, mode: spec.mode };
|
|
50
|
-
if (resolved.state !== spec.expectedState) {
|
|
52
|
+
if (resolved.state !== spec.expectedState && !allowsManualOverride(options.manualOverride, 'task-verify', 'VERIFY_TASK_STATE_MISMATCH')) {
|
|
51
53
|
return failure(request, 'VERIFY_TASK_STATE_MISMATCH', `event '${request.event}' requires workspace '${spec.expectedState}', received '${resolved.state}'`, identity);
|
|
52
54
|
}
|
|
53
55
|
if (spec.artifactFamily) {
|
|
@@ -66,12 +68,23 @@ function verifyTaskEvent(request, options = {}) {
|
|
|
66
68
|
if (spec.mode === 'gate') {
|
|
67
69
|
const payload = engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
|
|
68
70
|
const status = payload.gate;
|
|
71
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
72
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
|
|
73
|
+
return {
|
|
74
|
+
status: 'pass', changed: false, event: request.event, requestRef: request.taskRef,
|
|
75
|
+
...identity, artifact: request.artifact ?? null, invocations, error: null
|
|
76
|
+
};
|
|
77
|
+
}
|
|
69
78
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
70
79
|
}
|
|
71
80
|
else {
|
|
72
81
|
for (const check of spec.checks ?? []) {
|
|
73
82
|
const payload = engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
|
|
74
83
|
const status = payload.status;
|
|
84
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
85
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
75
88
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
76
89
|
if (status !== 'pass')
|
|
77
90
|
break;
|
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
3
3
|
import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
|
|
4
4
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
5
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
6
|
+
import { retryHintForWarning } from './operation-outcome.js';
|
|
5
7
|
function failed(intent, code, message, taskId = null, entityId = null) {
|
|
6
8
|
return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
|
|
7
9
|
}
|
|
@@ -54,7 +56,8 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
54
56
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
55
57
|
if (!resolved.ok)
|
|
56
58
|
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
57
|
-
|
|
59
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'workflow-warning', 'TASK_STATE_MISMATCH');
|
|
60
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
58
61
|
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
59
62
|
let content;
|
|
60
63
|
let rows;
|
|
@@ -114,7 +117,7 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
114
117
|
after = { ...before, status: intent.status, resolvedAt: metadata.timestamp, resolution };
|
|
115
118
|
}
|
|
116
119
|
const writeResult = writeTask({
|
|
117
|
-
taskRef: intent.taskRef, expectedState: 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
120
|
+
taskRef: intent.taskRef, expectedState: stateOverride ? resolved.state : 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
118
121
|
}, {
|
|
119
122
|
...options,
|
|
120
123
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
@@ -127,5 +130,53 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
127
130
|
entityId: after.id, before, after, warnings: [], operations: writeResult.operations, error: null
|
|
128
131
|
};
|
|
129
132
|
}
|
|
130
|
-
|
|
133
|
+
function projectFinalizationWarning(taskRef, warning, options = {}) {
|
|
134
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
135
|
+
if (!resolved.ok)
|
|
136
|
+
return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
137
|
+
if (resolved.state !== 'completed')
|
|
138
|
+
return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected completed` } };
|
|
139
|
+
let content;
|
|
140
|
+
let rows;
|
|
141
|
+
try {
|
|
142
|
+
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
143
|
+
rows = parseWorkflowWarnings(content);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return { status: 'failed', changed: false, error: { code: 'WARNING_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
147
|
+
}
|
|
148
|
+
const metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
149
|
+
const existing = rows.find((row) => row.step === warning.step && row.code === warning.code && row.target === warning.target);
|
|
150
|
+
const after = existing
|
|
151
|
+
? {
|
|
152
|
+
...existing,
|
|
153
|
+
severity: warning.severity,
|
|
154
|
+
status: warning.status,
|
|
155
|
+
message: warning.message,
|
|
156
|
+
action: retryHintForWarning(warning),
|
|
157
|
+
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
158
|
+
resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
159
|
+
}
|
|
160
|
+
: {
|
|
161
|
+
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
162
|
+
severity: warning.severity, code: warning.code, status: warning.status,
|
|
163
|
+
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
164
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
165
|
+
};
|
|
166
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(after))
|
|
167
|
+
return { status: 'no-op', changed: false, error: null };
|
|
168
|
+
const written = writeTask({
|
|
169
|
+
taskRef: resolved.taskId,
|
|
170
|
+
expectedState: 'completed',
|
|
171
|
+
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
172
|
+
}, {
|
|
173
|
+
...options,
|
|
174
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: 'completed' },
|
|
175
|
+
metadataProvider: () => metadata
|
|
176
|
+
});
|
|
177
|
+
if (written.status === 'failed')
|
|
178
|
+
return { status: 'failed', changed: false, error: { code: written.error.code, message: written.error.message } };
|
|
179
|
+
return { status: written.status === 'applied' ? 'applied' : 'no-op', changed: written.changed, error: null };
|
|
180
|
+
}
|
|
181
|
+
export { applyWorkflowWarningIntent, projectFinalizationWarning };
|
|
131
182
|
//# sourceMappingURL=workflow-warning-intents.js.map
|
package/dist/lib/task/write.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { VERSION } from '../version.js';
|
|
4
4
|
import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
6
7
|
import { mutateTableRow, upsertSection } from './sections.js';
|
|
7
8
|
const DEFAULT_FILE_SYSTEM = {
|
|
8
9
|
readFileSync: (file) => fs.readFileSync(file, 'utf8'),
|
|
@@ -70,7 +71,7 @@ function writeTask(request, options = {}) {
|
|
|
70
71
|
taskMdPath: resolved.taskMdPath,
|
|
71
72
|
actualState: resolved.state
|
|
72
73
|
};
|
|
73
|
-
if (resolved.state !== request.expectedState) {
|
|
74
|
+
if (resolved.state !== request.expectedState && !allowsManualOverride(options.manualOverride, 'task.write', 'TASK_STATE_MISMATCH')) {
|
|
74
75
|
return failure(request, identity, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected ${request.expectedState}`);
|
|
75
76
|
}
|
|
76
77
|
const io = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.9.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.9.9",
|
|
4
|
+
"sourceInputHash": "2f2db695db557531b3139eccd1bd530fbeb7b99409e2d49338f3b4646aac9ef6",
|
|
5
|
+
"internalExecutableBuildHash": "1391ebe2614f3071e7a60920325046a47f1473e7fb23c821327072f947f639d3"
|
|
6
6
|
}
|
|
@@ -16,7 +16,7 @@ const claudeCodeAdapter = defineAgentClientAdapter({
|
|
|
16
16
|
commands: { level: 'integrated' },
|
|
17
17
|
hooks: { level: 'integrated' },
|
|
18
18
|
subagents: { level: 'experimental' },
|
|
19
|
-
orchestration: { level: '
|
|
19
|
+
orchestration: { level: 'experimental' },
|
|
20
20
|
sandbox: { level: 'integrated' },
|
|
21
21
|
verification: { level: 'compatible' }
|
|
22
22
|
},
|
|
@@ -26,8 +26,8 @@ const claudeCodeAdapter = defineAgentClientAdapter({
|
|
|
26
26
|
guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
|
|
27
27
|
},
|
|
28
28
|
delegationEvidence: {
|
|
29
|
-
actualModel: '
|
|
30
|
-
actualReasoningEffort: '
|
|
29
|
+
actualModel: 'host-event',
|
|
30
|
+
actualReasoningEffort: 'spawn-ack'
|
|
31
31
|
},
|
|
32
32
|
project: {
|
|
33
33
|
ownedPathPrefixes: ['.claude/'],
|