@fitlab-ai/agent-infra 0.11.11 → 0.11.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/internal-cli.js +2 -2
- package/dist/lib/agent-clients/adapter.js +7 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
- package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
- package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
- package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
- package/dist/lib/agent-clients/adapters/codex.js +40 -1
- package/dist/lib/agent-clients/adapters/index.js +2 -10
- package/dist/lib/agent-clients/tokens.js +3 -2
- package/dist/lib/internal/agent-client.js +132 -72
- package/dist/lib/internal/cli-route-inventory.js +2 -2
- package/dist/lib/internal/sandbox-control.js +1 -81
- package/dist/lib/internal/task-finalization.js +26 -19
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -1
- package/dist/lib/internal/task-validate.js +1 -1
- package/dist/lib/platform/github-client.js +33 -3
- package/dist/lib/platform/issues.js +18 -6
- package/dist/lib/runtime/agent-runtime.js +6 -29
- package/dist/lib/sandbox/commands/control-status.js +1 -0
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/ls.js +4 -5
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/control/agent-operation.js +34 -0
- package/dist/lib/sandbox/control/client.js +14 -201
- package/dist/lib/sandbox/control/executor.js +12 -125
- package/dist/lib/sandbox/control/lifecycle.js +66 -4
- package/dist/lib/sandbox/control/protocol.js +15 -54
- package/dist/lib/sandbox/control/server.js +216 -281
- package/dist/lib/sandbox/control/task-view.js +0 -13
- package/dist/lib/sandbox/mounts.js +9 -1
- package/dist/lib/sandbox/recovery.js +31 -10
- package/dist/lib/sandbox/removal.js +28 -5
- package/dist/lib/sandbox/workspace-identity.js +18 -6
- package/dist/lib/sandbox/workspace-view.js +56 -19
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/control-authority.js +56 -264
- package/dist/lib/task/control-recovery.js +2 -3
- package/dist/lib/task/create.js +2 -2
- package/dist/lib/task/delegation-receipts.js +74 -161
- package/dist/lib/task/events.js +88 -44
- package/dist/lib/task/finalization.js +35 -6
- package/dist/lib/task/lifecycle.js +274 -115
- package/dist/lib/task/orchestration.js +201 -194
- package/dist/lib/task/runtime-paths.js +10 -0
- package/dist/lib/task/short-id.js +37 -27
- package/dist/lib/task/verification-engine.js +32 -26
- package/dist/lib/task/workspace-snapshot.js +1 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +39 -0
- package/lib/agent-clients/adapters/claude-code.ts +5 -1
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
- package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
- package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
- package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
- package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
- package/lib/agent-clients/adapters/codex.ts +44 -1
- package/lib/agent-clients/adapters/index.ts +7 -20
- package/lib/agent-clients/tokens.ts +4 -3
- package/lib/internal/agent-client.ts +142 -73
- package/lib/internal/cli-route-inventory.ts +2 -2
- package/lib/internal/sandbox-control.ts +0 -97
- package/lib/internal/task-finalization.ts +30 -18
- package/lib/internal/task-lifecycle.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -1
- package/lib/internal/task-validate.ts +1 -1
- package/lib/platform/github-client.ts +34 -3
- package/lib/platform/issues.ts +18 -6
- package/lib/runtime/agent-runtime.ts +6 -41
- package/lib/sandbox/commands/control-status.ts +1 -0
- package/lib/sandbox/commands/create.ts +7 -1
- package/lib/sandbox/commands/ls.ts +4 -5
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/control/agent-operation.ts +49 -0
- package/lib/sandbox/control/client.ts +20 -254
- package/lib/sandbox/control/executor.ts +13 -136
- package/lib/sandbox/control/lifecycle.ts +67 -4
- package/lib/sandbox/control/protocol.ts +22 -66
- package/lib/sandbox/control/server.ts +21 -95
- package/lib/sandbox/control/task-view.ts +0 -19
- package/lib/sandbox/mounts.ts +10 -1
- package/lib/sandbox/recovery.ts +38 -17
- package/lib/sandbox/removal.ts +33 -7
- package/lib/sandbox/workspace-identity.ts +16 -6
- package/lib/sandbox/workspace-view.ts +50 -18
- package/lib/task/commands/ls.ts +3 -3
- package/lib/task/control-authority.ts +66 -378
- package/lib/task/control-recovery.ts +3 -4
- package/lib/task/create.ts +3 -3
- package/lib/task/delegation-receipts.ts +87 -231
- package/lib/task/events.ts +100 -42
- package/lib/task/finalization.ts +37 -7
- package/lib/task/lifecycle.ts +257 -109
- package/lib/task/orchestration.ts +260 -237
- package/lib/task/runtime-paths.ts +12 -0
- package/lib/task/short-id.ts +33 -40
- package/lib/task/verification-engine.ts +30 -25
- package/lib/task/workspace-snapshot.ts +1 -1
- package/package.json +1 -1
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/next-step-output.en.md +6 -19
- package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
- package/templates/.agents/rules/strategy-pattern.en.md +25 -0
- package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
- package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
- package/dist/lib/internal/codex-lifecycle.js +0 -385
- package/dist/lib/task/codex-orchestration.js +0 -206
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
- package/lib/internal/codex-lifecycle.ts +0 -390
- package/lib/task/codex-orchestration.ts +0 -276
package/lib/task/events.ts
CHANGED
|
@@ -45,12 +45,6 @@ import { getArtifactSchema } from './artifact-schema.ts';
|
|
|
45
45
|
import { canonicalSemanticDigest, inspectArtifactContract } from './artifact-operations.ts';
|
|
46
46
|
import { inspectReviewIdentity } from './review-identity.ts';
|
|
47
47
|
import { parseCompletionFacts, type CompletionFact } from './completion-facts.ts';
|
|
48
|
-
import {
|
|
49
|
-
consumeLifecycleRecoveryAttestation,
|
|
50
|
-
lifecycleRecoveryAttestationDigest,
|
|
51
|
-
validateLifecycleRecoveryAttestation,
|
|
52
|
-
type LifecycleRecoveryAttestationV1
|
|
53
|
-
} from './control-authority.ts';
|
|
54
48
|
import { readManualValidationCompletion } from './manual-validation-completion.ts';
|
|
55
49
|
import { parseLifecyclePathDecision } from './lifecycle-path.ts';
|
|
56
50
|
|
|
@@ -93,8 +87,6 @@ type TaskEventError = { code: TaskEventErrorCode; message: string };
|
|
|
93
87
|
type TaskEventOptions = TaskWriteOptions & {
|
|
94
88
|
commitOrchestrationCompletion?: (plan: OrchestrationStageCompletion) => void;
|
|
95
89
|
lockAlreadyHeld?: boolean;
|
|
96
|
-
lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
|
|
97
|
-
deferLifecycleRecoveryConsumption?: boolean;
|
|
98
90
|
};
|
|
99
91
|
type TaskEventResult = {
|
|
100
92
|
status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
|
|
@@ -395,6 +387,78 @@ function openStartedIdentity(rows: ReturnType<typeof pairEntries>, family: Event
|
|
|
395
387
|
return matches.length === 1 ? matches[0] : matches.length > 1 ? { conflict: true as const } : null;
|
|
396
388
|
}
|
|
397
389
|
|
|
390
|
+
function repairMissingCodeFixQualifier(
|
|
391
|
+
request: TaskEventRequest,
|
|
392
|
+
family: EventFamily,
|
|
393
|
+
open: { row: { step: string }; round: number; fixFor?: string; implementationInput?: string },
|
|
394
|
+
repoRoot: string
|
|
395
|
+
) {
|
|
396
|
+
if (family !== 'code' || open.fixFor !== undefined || !request.fixFor || request.implementationInput !== undefined) {
|
|
397
|
+
return {
|
|
398
|
+
error: {
|
|
399
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
400
|
+
message: `open code start cannot be repaired (family=${family}, openFixFor=${open.fixFor ?? 'none'}, requestedFixFor=${request.fixFor ?? 'none'}, implementationInput=${request.implementationInput ?? 'none'})`
|
|
401
|
+
} as TaskEventError,
|
|
402
|
+
context: null
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const repair = normalizeStarted(request, repoRoot);
|
|
406
|
+
if ('error' in repair) return { error: repair.error, context: repair.context };
|
|
407
|
+
if (repair.request.round !== open.round || repair.request.fixFor !== request.fixFor || repair.request.implementationInput !== undefined) {
|
|
408
|
+
return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: 'current code artifact context does not match the open round and requested fix input' } as TaskEventError, context: repair.context };
|
|
409
|
+
}
|
|
410
|
+
return repair;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function resolveOpenStartedEvent(
|
|
414
|
+
request: TaskEventRequest,
|
|
415
|
+
family: EventFamily,
|
|
416
|
+
open: { row: { step: string; agent: string; started: string; note: string }; round: number; fixFor?: string; implementationInput?: string },
|
|
417
|
+
repoRoot: string
|
|
418
|
+
) {
|
|
419
|
+
if (open.row.agent !== request.agent) return { status: 'error' as const, error: { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' } as TaskEventError };
|
|
420
|
+
if (family === 'manual-validation' && request.transactionId !== undefined) {
|
|
421
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(open.row.note)?.[1];
|
|
422
|
+
if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId) {
|
|
423
|
+
return { status: 'error' as const, error: { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' } as TaskEventError };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (request.round !== undefined && request.round !== open.round) {
|
|
427
|
+
return { status: 'error' as const, error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${open.round}` } as TaskEventError };
|
|
428
|
+
}
|
|
429
|
+
if (request.fixFor !== undefined && request.fixFor !== open.fixFor) {
|
|
430
|
+
const repair = repairMissingCodeFixQualifier(request, family, open, repoRoot);
|
|
431
|
+
if (!repair) return { status: 'error' as const, error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with open event` } as TaskEventError };
|
|
432
|
+
if ('error' in repair) return { status: 'error' as const, error: repair.error, context: repair.context };
|
|
433
|
+
return { status: 'repair' as const, repair: repair.request, context: repair.context, superseded: { action: open.row.step } };
|
|
434
|
+
}
|
|
435
|
+
if (request.implementationInput !== open.implementationInput) {
|
|
436
|
+
return { status: 'error' as const, error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with open event` } as TaskEventError };
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
status: 'replay' as const,
|
|
440
|
+
replay: {
|
|
441
|
+
...request,
|
|
442
|
+
round: open.round,
|
|
443
|
+
artifact: artifactName(FAMILY[family].artifact, open.round),
|
|
444
|
+
fixFor: open.fixFor,
|
|
445
|
+
implementationInput: open.implementationInput
|
|
446
|
+
},
|
|
447
|
+
startedAt: open.row.started
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function hasOtherOpenStarted(rows: ReturnType<typeof pairEntries>, allowedAction: string): boolean {
|
|
452
|
+
return rows.some((row) => row.started && !row.done && row.step !== allowedAction);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function hasConflictingOpenStarted(
|
|
456
|
+
rows: ReturnType<typeof pairEntries>,
|
|
457
|
+
recovery: { action: string } | null
|
|
458
|
+
): boolean {
|
|
459
|
+
return recovery ? hasOtherOpenStarted(rows, recovery.action) : rows.some((row) => row.started && !row.done);
|
|
460
|
+
}
|
|
461
|
+
|
|
398
462
|
function reviewInputFamily(family: EventFamily): ArtifactFamily {
|
|
399
463
|
return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
|
|
400
464
|
}
|
|
@@ -581,53 +645,35 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
581
645
|
const rows = startedBackedRows(pairEntries(section.entries));
|
|
582
646
|
let normalized = request;
|
|
583
647
|
let artifactContext: ArtifactContextResult | null = null;
|
|
648
|
+
let supersededOpenStart: { action: string } | null = null;
|
|
584
649
|
if (initialParts.phase === 'started') {
|
|
585
650
|
const openIdentity = openStartedIdentity(rows, initialParts.family);
|
|
586
651
|
if (openIdentity && 'conflict' in openIdentity) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'artifact family has more than one open started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
587
652
|
if (openIdentity) {
|
|
588
|
-
|
|
589
|
-
if (
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
595
|
-
if (request.implementationInput !== openIdentity.implementationInput) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
596
|
-
normalized = { ...request, round: openIdentity.round, artifact: artifactName(FAMILY[initialParts.family].artifact, openIdentity.round), fixFor: openIdentity.fixFor, implementationInput: openIdentity.implementationInput };
|
|
597
|
-
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '', identity(normalized), openIdentity.row.started, frontmatter, null);
|
|
653
|
+
const resolvedStart = resolveOpenStartedEvent(request, initialParts.family, openIdentity, resolved.repoRoot);
|
|
654
|
+
if (resolvedStart.status === 'error') return failed(request, resolvedStart.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
655
|
+
if (resolvedStart.status === 'replay') return successNoOp(resolvedStart.replay, resolved.taskId, resolved.taskMdPath, typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '', identity(resolvedStart.replay), resolvedStart.startedAt, frontmatter, null);
|
|
656
|
+
normalized = resolvedStart.repair;
|
|
657
|
+
artifactContext = resolvedStart.context;
|
|
658
|
+
supersededOpenStart = resolvedStart.superseded;
|
|
598
659
|
}
|
|
599
|
-
if (rows
|
|
660
|
+
if (hasConflictingOpenStarted(rows, supersededOpenStart)) {
|
|
600
661
|
return failed(request, {
|
|
601
662
|
code: 'EVENT_TRANSITION_INVALID',
|
|
602
663
|
message: 'EXECUTION_BUSY: another lifecycle execution is open'
|
|
603
664
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
604
665
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
666
|
+
if (!supersededOpenStart) {
|
|
667
|
+
const result = normalizeStarted(request, resolved.repoRoot);
|
|
668
|
+
artifactContext = result.context;
|
|
669
|
+
if ('error' in result) return failed(request, result.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, artifactContext });
|
|
670
|
+
normalized = result.request;
|
|
671
|
+
}
|
|
609
672
|
} else if (initialParts.phase === 'completed') {
|
|
610
673
|
const identity = request.artifact ? parseArtifactName(request.artifact) : null;
|
|
611
674
|
if (identity?.family === FAMILY[initialParts.family].artifact) normalized = { ...request, round: identity.round };
|
|
612
675
|
}
|
|
613
676
|
const eventIdentity = identity(normalized);
|
|
614
|
-
const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
|
|
615
|
-
if (lifecycleAuthority) {
|
|
616
|
-
try { validateLifecycleRecoveryAttestation(lifecycleAuthority); }
|
|
617
|
-
catch (error) {
|
|
618
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
619
|
-
}
|
|
620
|
-
const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
621
|
-
if (eventIdentity.phase !== 'completed'
|
|
622
|
-
|| lifecycleAuthority.phase !== 'task-event.completed'
|
|
623
|
-
|| lifecycleAuthority.taskId !== resolved.taskId
|
|
624
|
-
|| lifecycleAuthority.family !== expectedFamily
|
|
625
|
-
|| lifecycleAuthority.artifact !== normalized.artifact
|
|
626
|
-
|| lifecycleAuthority.round !== normalized.round
|
|
627
|
-
|| (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
|
|
628
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
677
|
const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
|
|
632
678
|
const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
|
|
633
679
|
const manual = eventIdentity.family === 'manual-validation';
|
|
@@ -716,7 +762,8 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
716
762
|
const capability = canStart(lifecycleAction(eventIdentity.family), facts.facts, trigger);
|
|
717
763
|
const safetyFailure = capability.reasonCode === 'TASK_NOT_ACTIVE'
|
|
718
764
|
|| capability.reasonCode === 'LIFECYCLE_EXECUTION_OPEN';
|
|
719
|
-
if (!capability.allowed && (normalized.initiator === 'orchestrator' || safetyFailure)
|
|
765
|
+
if (!capability.allowed && (normalized.initiator === 'orchestrator' || safetyFailure)
|
|
766
|
+
&& !(supersededOpenStart && capability.reasonCode === 'LIFECYCLE_EXECUTION_OPEN')) {
|
|
720
767
|
return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${capability.reasonCode}: ${capability.evidence.join(', ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
721
768
|
}
|
|
722
769
|
}
|
|
@@ -792,7 +839,18 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
792
839
|
}
|
|
793
840
|
const step = eventIdentity.phase === 'started' || eventIdentity.target === null ? currentStep : eventIdentity.target;
|
|
794
841
|
const logStep = eventIdentity.phase === 'started' ? `${eventIdentity.action} [started]` : eventIdentity.action;
|
|
795
|
-
const
|
|
842
|
+
const supersededBody = supersededOpenStart
|
|
843
|
+
? appendActivityEntry(section, {
|
|
844
|
+
time: metadata.timestamp,
|
|
845
|
+
step: `${supersededOpenStart.action} [aborted]`,
|
|
846
|
+
agent: normalized.agent,
|
|
847
|
+
note: `superseded by ${eventIdentity.action}`
|
|
848
|
+
})
|
|
849
|
+
: section.body;
|
|
850
|
+
const body = appendActivityEntry(
|
|
851
|
+
supersededOpenStart ? { ...section, body: supersededBody } : section,
|
|
852
|
+
{ time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note }
|
|
853
|
+
);
|
|
796
854
|
const frontmatterSet: Record<string, string> = { current_step: step, assigned_to: normalized.agent };
|
|
797
855
|
if (currentFact) frontmatterSet.completion_facts = JSON.stringify(replaceCompletionFact(parseCompletionFacts(frontmatter.completion_facts), currentFact));
|
|
798
856
|
if (completedArtifact && ['analyze', 'plan', 'code'].includes(eventIdentity.family)) {
|
package/lib/task/finalization.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
|
14
14
|
import { recoverPlatformOperations } from './platform-operation-recovery.ts';
|
|
15
15
|
import {
|
|
16
16
|
applyTaskLifecycle,
|
|
17
|
+
applyTaskLifecycleWithinTaskLock,
|
|
17
18
|
inspectTaskLifecycleProgress,
|
|
18
19
|
type TaskLifecycleOptions,
|
|
19
20
|
type TaskLifecycleRequest,
|
|
@@ -78,6 +79,7 @@ type TaskFinalizationOptions = Readonly<{
|
|
|
78
79
|
issueInspect?: typeof inspectPlatformIssue;
|
|
79
80
|
verify?: typeof verifyTaskEvent;
|
|
80
81
|
preflight?: typeof verifyTaskEvent;
|
|
82
|
+
beforeLifecycle?: (taskId: string) => Promise<void>;
|
|
81
83
|
}>;
|
|
82
84
|
|
|
83
85
|
type TaskFinalizationReceipt = Readonly<{
|
|
@@ -865,7 +867,9 @@ async function prepareUnderLock(
|
|
|
865
867
|
options: TaskFinalizationOptions
|
|
866
868
|
): Promise<TaskFinalizationResult> {
|
|
867
869
|
const repoRoot = path.resolve(options.repoRoot);
|
|
868
|
-
const lifecycle = options.lifecycle ??
|
|
870
|
+
const lifecycle = options.lifecycle ?? ((lifecycleRequest, lifecycleOptions) => (
|
|
871
|
+
applyTaskLifecycleWithinTaskLock(lifecycleRequest, lifecycleOptions ?? { repoRoot }, taskId)
|
|
872
|
+
));
|
|
869
873
|
const backfill = options.backfill ?? backfillCompletionComments;
|
|
870
874
|
const commentSync = options.commentSync ?? syncPlatformComment;
|
|
871
875
|
const issueSync = options.issueSync ?? syncPlatformIssue;
|
|
@@ -929,6 +933,8 @@ async function prepareUnderLock(
|
|
|
929
933
|
['requirements', 'issue-type', 'fields'].includes(operation.name)
|
|
930
934
|
&& operation.status !== 'applied' && operation.status !== 'no-op'
|
|
931
935
|
&& !(operation.name === 'requirements' && operation.status === 'skipped' && operation.reasonCode === 'NO_REQUIREMENTS_ANCHOR')
|
|
936
|
+
&& !(!synced.capabilities.push && ['issue-type', 'fields'].includes(operation.name)
|
|
937
|
+
&& operation.status === 'skipped' && operation.reasonCode === 'PUSH_REQUIRED')
|
|
932
938
|
);
|
|
933
939
|
if ((synced.status !== 'applied' && synced.status !== 'no-op') || synced.error || badOperation) {
|
|
934
940
|
const error = synced.error ?? {
|
|
@@ -938,6 +944,16 @@ async function prepareUnderLock(
|
|
|
938
944
|
};
|
|
939
945
|
return failed(taskId, errorOf(error, 'FINALIZATION_ISSUE_METADATA_FAILED', true));
|
|
940
946
|
}
|
|
947
|
+
if (!synced.capabilities.push) {
|
|
948
|
+
const missingCapabilitySkip = ['issue-type', 'fields'].find((name) => !issueOperations?.some((operation) =>
|
|
949
|
+
operation.name === name && operation.status === 'skipped' && operation.reasonCode === 'PUSH_REQUIRED'
|
|
950
|
+
));
|
|
951
|
+
if (missingCapabilitySkip) return failed(taskId, {
|
|
952
|
+
code: 'FINALIZATION_ISSUE_METADATA_UNCONFIRMED',
|
|
953
|
+
message: `Issue metadata operation '${missingCapabilitySkip}' did not report the required PUSH_REQUIRED capability skip`,
|
|
954
|
+
retryable: true
|
|
955
|
+
});
|
|
956
|
+
}
|
|
941
957
|
let inspected: IssueResult;
|
|
942
958
|
try { inspected = await issueInspect(taskId, { cwd: repoRoot }); }
|
|
943
959
|
catch (error) { return failed(taskId, errorOf(error, 'FINALIZATION_ISSUE_INSPECT_FAILED', true)); }
|
|
@@ -949,7 +965,8 @@ async function prepareUnderLock(
|
|
|
949
965
|
retryable: true
|
|
950
966
|
});
|
|
951
967
|
}
|
|
952
|
-
|
|
968
|
+
const rereadNames = synced.capabilities.push ? ['requirements', 'issue-type', 'fields'] : ['requirements'];
|
|
969
|
+
for (const name of rereadNames) {
|
|
953
970
|
const operation = issueOperations?.find((candidate) => candidate.name === name);
|
|
954
971
|
if (!operation) return failed(taskId, {
|
|
955
972
|
code: 'FINALIZATION_ISSUE_METADATA_UNCONFIRMED',
|
|
@@ -1189,11 +1206,11 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
|
|
|
1189
1206
|
retryable: false
|
|
1190
1207
|
});
|
|
1191
1208
|
receipt = updateReceipt(repoRoot, receipt, { verification: 'pending' });
|
|
1192
|
-
const
|
|
1193
|
-
const
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1209
|
+
const lifecycleRequest = { taskRef: resolved.taskId, intent: 'complete' as const, agent: request.agent };
|
|
1210
|
+
const lifecycleOptions = { repoRoot, ...(options.metadataProvider ? { metadataProvider: options.metadataProvider } : {}) };
|
|
1211
|
+
const result = options.lifecycle
|
|
1212
|
+
? options.lifecycle(lifecycleRequest, lifecycleOptions)
|
|
1213
|
+
: applyTaskLifecycleWithinTaskLock(lifecycleRequest, lifecycleOptions, resolved.taskId);
|
|
1197
1214
|
const step = lifecycleStep(result);
|
|
1198
1215
|
if (result.status !== 'applied' && result.status !== 'no-op') {
|
|
1199
1216
|
const updated = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: step.error });
|
|
@@ -1287,6 +1304,19 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
|
|
|
1287
1304
|
async function applyTaskFinalization(request: TaskFinalizationRequest, options: TaskFinalizationOptions): Promise<TaskFinalizationResult> {
|
|
1288
1305
|
const prepared = await prepareTaskFinalization(request, options);
|
|
1289
1306
|
if (prepared.status !== 'prepared') return prepared;
|
|
1307
|
+
if (options.beforeLifecycle) {
|
|
1308
|
+
try {
|
|
1309
|
+
await options.beforeLifecycle(prepared.taskId!);
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
const detail = errorOf(error, 'TASK_FINALIZATION_PRE_LIFECYCLE_FAILED', true);
|
|
1312
|
+
return failed(prepared.taskId, detail, {
|
|
1313
|
+
changed: prepared.changed,
|
|
1314
|
+
completedSteps: prepared.completedSteps,
|
|
1315
|
+
pendingSteps: prepared.pendingSteps,
|
|
1316
|
+
warnings: prepared.warnings
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1290
1320
|
return commitPreparedTaskFinalization(request, options);
|
|
1291
1321
|
}
|
|
1292
1322
|
|