@fitlab-ai/agent-infra 0.9.12 → 0.9.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/README.md +11 -11
- package/README.zh-CN.md +11 -11
- package/dist/bin/internal-cli.js +24 -19
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +63 -24
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +520 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +783 -537
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +112 -238
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +33 -5
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +49 -93
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +43 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +47 -22
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +560 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +826 -543
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +112 -292
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +32 -6
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +52 -85
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +52 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +18 -1
- package/templates/.agents/templates/task.zh-CN.md +18 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
|
@@ -15,7 +15,7 @@ import { inspectLifecycleExecution } from './lifecycle-execution.ts';
|
|
|
15
15
|
import { applyLedgerIntent } from './ledger-intents.ts';
|
|
16
16
|
import { bindPlatformIssue } from '../platform/issues.ts';
|
|
17
17
|
import { bindPlatformPullRequest } from '../platform/pull-requests.ts';
|
|
18
|
-
import type {
|
|
18
|
+
import type { PlatformClient } from '../platform/context.ts';
|
|
19
19
|
import { finalizeReviewSummary } from './review-finalization.ts';
|
|
20
20
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
21
21
|
import { DocumentMutationError, parseTable } from './sections.ts';
|
|
@@ -25,6 +25,9 @@ import { verifyInProcess } from './verification-engine.ts';
|
|
|
25
25
|
import { canonicalTimestamp, writeTask } from './write.ts';
|
|
26
26
|
import type { TaskFileSystem, TaskWriteMetadata, TaskWriteResult } from './write.ts';
|
|
27
27
|
import { readPrDeliveryFact } from './pr-delivery-fact.ts';
|
|
28
|
+
import { resourceIdentity, resourceIdentityNumber } from '../platform/provider-bridge.ts';
|
|
29
|
+
import type { ResourceIdentity } from '../platform/resource-identity.ts';
|
|
30
|
+
import { taskIssueIdentity } from '../platform/task-identities.ts';
|
|
28
31
|
|
|
29
32
|
type Eligibility = 'eligible' | 'repair-only' | 'runtime-recovery-only' | 'never-overridable';
|
|
30
33
|
type OutcomeEffect = 'apply-target' | 'retry-same-intent' | 'record-only' | 'no-write';
|
|
@@ -96,8 +99,8 @@ type HumanOverrideOptions = Readonly<{
|
|
|
96
99
|
probeIssueNumber?: number;
|
|
97
100
|
probePullRequestNumber?: number;
|
|
98
101
|
probeStagingDir?: string;
|
|
99
|
-
platformClient?:
|
|
100
|
-
effectExecutor?: (capability: ManualOverrideCapability) => OverrideError | null
|
|
102
|
+
platformClient?: PlatformClient;
|
|
103
|
+
effectExecutor?: (capability: ManualOverrideCapability) => OverrideError | null | Promise<OverrideError | null>;
|
|
101
104
|
}>;
|
|
102
105
|
|
|
103
106
|
type FailureSnapshot = Readonly<{
|
|
@@ -584,7 +587,7 @@ function snapshotEvidence(
|
|
|
584
587
|
content: string,
|
|
585
588
|
frontmatter: Record<string, unknown> | null,
|
|
586
589
|
producer: ProducerFailure,
|
|
587
|
-
resource: Readonly<{ kind: 'issue' | 'pull-request'; number: number }> | null
|
|
590
|
+
resource: Readonly<{ kind: 'issue' | 'pull-request'; identity: ResourceIdentity; number: number | null }> | null
|
|
588
591
|
): string {
|
|
589
592
|
const stableFrontmatter = frontmatter
|
|
590
593
|
? Object.fromEntries(Object.entries(frontmatter).filter(([key]) => !['updated_at', 'agent_infra_version', 'status', 'blocked_at', 'completed_at', 'cancelled_at'].includes(key)))
|
|
@@ -609,15 +612,22 @@ function resourceForSnapshot(
|
|
|
609
612
|
policy: FailurePolicy,
|
|
610
613
|
options: HumanOverrideOptions,
|
|
611
614
|
frontmatter: Record<string, unknown> | null
|
|
612
|
-
): Readonly<{ kind: 'issue' | 'pull-request'; number: number }> | null {
|
|
615
|
+
): Readonly<{ kind: 'issue' | 'pull-request'; identity: ResourceIdentity; number: number | null }> | null {
|
|
613
616
|
if (policy.producerId === 'platform.issue') {
|
|
614
|
-
const
|
|
615
|
-
|
|
617
|
+
const identity = options.probeIssueNumber !== undefined
|
|
618
|
+
? resourceIdentity(options.probeIssueNumber)
|
|
619
|
+
: taskIssueIdentity((frontmatter || {}) as Record<string, string | number | boolean | null>) || (() => {
|
|
620
|
+
const number = toPositiveInteger(frontmatter?.issue_number);
|
|
621
|
+
return number === null ? null : resourceIdentity(number);
|
|
622
|
+
})();
|
|
623
|
+
return identity ? { kind: 'issue', identity, number: resourceIdentityNumber(identity) } : null;
|
|
616
624
|
}
|
|
617
625
|
if (policy.producerId === 'platform.pull-request') {
|
|
618
626
|
const fact = frontmatter ? readPrDeliveryFact(frontmatter) : null;
|
|
619
|
-
const
|
|
620
|
-
|
|
627
|
+
const identity = options.probePullRequestNumber !== undefined
|
|
628
|
+
? resourceIdentity(options.probePullRequestNumber)
|
|
629
|
+
: fact?.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
630
|
+
return identity ? { kind: 'pull-request', identity, number: resourceIdentityNumber(identity) } : null;
|
|
621
631
|
}
|
|
622
632
|
return null;
|
|
623
633
|
}
|
|
@@ -694,41 +704,52 @@ function probeNotObserved(policy: FailurePolicy): OverrideError {
|
|
|
694
704
|
return error('OVERRIDE_FAILURE_NOT_PRESENT', `producer ${policy.producerId} did not produce ${policy.code}`);
|
|
695
705
|
}
|
|
696
706
|
|
|
697
|
-
function probePlatformFailure(
|
|
707
|
+
async function probePlatformFailure(
|
|
698
708
|
taskRef: string,
|
|
699
709
|
policy: FailurePolicy,
|
|
700
710
|
resolved: ProbeTask,
|
|
701
711
|
options: HumanOverrideOptions
|
|
702
|
-
): ProducerFailure | OverrideError {
|
|
712
|
+
): Promise<ProducerFailure | OverrideError> {
|
|
703
713
|
let frontmatter: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
704
714
|
try {
|
|
705
715
|
frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(path.join(resolved.taskDir, 'task.md'), 'utf8'));
|
|
706
716
|
} catch (cause) {
|
|
707
717
|
return error('OVERRIDE_DOCUMENT_INVALID', cause instanceof Error ? cause.message : String(cause));
|
|
708
718
|
}
|
|
709
|
-
const
|
|
719
|
+
const issueIdentity = options.probeIssueNumber !== undefined
|
|
720
|
+
? resourceIdentity(options.probeIssueNumber)
|
|
721
|
+
: taskIssueIdentity(frontmatter) || (() => {
|
|
722
|
+
const number = toPositiveInteger(frontmatter.issue_number);
|
|
723
|
+
return number === null ? null : resourceIdentity(number);
|
|
724
|
+
})();
|
|
710
725
|
const fact = readPrDeliveryFact(frontmatter);
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
726
|
+
const pullRequestIdentity = options.probePullRequestNumber !== undefined
|
|
727
|
+
? resourceIdentity(options.probePullRequestNumber)
|
|
728
|
+
: fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
729
|
+
const token = (identity: ResourceIdentity | null): string | null => identity ? String(identity.value) : null;
|
|
730
|
+
const issueToken = token(issueIdentity);
|
|
731
|
+
const pullRequestToken = token(pullRequestIdentity);
|
|
732
|
+
const operation = await (policy.producerId === 'platform.issue'
|
|
733
|
+
? issueToken === null
|
|
714
734
|
? null
|
|
715
735
|
: bindPlatformIssue(taskRef, {
|
|
716
|
-
issue:
|
|
736
|
+
issue: issueToken,
|
|
717
737
|
agent: options.probeOperator ?? 'local-operator',
|
|
718
738
|
dryRun: true,
|
|
719
739
|
cwd: resolved.repoRoot,
|
|
720
740
|
...(options.platformClient ? { client: options.platformClient } : {})
|
|
721
741
|
})
|
|
722
|
-
:
|
|
742
|
+
: pullRequestToken === null
|
|
723
743
|
? null
|
|
724
744
|
: bindPlatformPullRequest(taskRef, {
|
|
725
|
-
pr:
|
|
745
|
+
pr: pullRequestToken,
|
|
726
746
|
agent: options.probeOperator ?? 'local-operator',
|
|
727
747
|
dryRun: true,
|
|
728
748
|
cwd: resolved.repoRoot,
|
|
729
749
|
...(options.platformClient ? { client: options.platformClient } : {})
|
|
730
|
-
})
|
|
731
|
-
|
|
750
|
+
})
|
|
751
|
+
);
|
|
752
|
+
if (!operation) return error('OVERRIDE_PROBE_INPUT_MISSING', `${policy.producerId} requires a bound issue or pull request identity`);
|
|
732
753
|
if (operation.status !== 'failed' && operation.status !== 'blocked') return probeNotObserved(policy);
|
|
733
754
|
if (!operation.error) return error('OVERRIDE_PROBE_INPUT_MISSING', `${policy.producerId} returned no platform error evidence`);
|
|
734
755
|
return {
|
|
@@ -766,6 +787,9 @@ function probeTaskEventFailure(taskRef: string, policy: FailurePolicy, resolved:
|
|
|
766
787
|
event: candidate.event,
|
|
767
788
|
agent: 'codex',
|
|
768
789
|
dryRun: true,
|
|
790
|
+
initiator: 'model',
|
|
791
|
+
requestId: `override-probe:${taskRef}:${candidate.event}`,
|
|
792
|
+
reasonCode: 'user-request',
|
|
769
793
|
...(candidate.artifact ? { artifact: candidate.artifact } : {})
|
|
770
794
|
}, { repoRoot: resolved.repoRoot, lockAlreadyHeld: true });
|
|
771
795
|
const matched = probeResult(policy, result, resolved.state);
|
|
@@ -774,8 +798,8 @@ function probeTaskEventFailure(taskRef: string, policy: FailurePolicy, resolved:
|
|
|
774
798
|
return probeNotObserved(policy);
|
|
775
799
|
}
|
|
776
800
|
|
|
777
|
-
function probeVerificationFailure(taskRef: string, policy: FailurePolicy, resolved: ProbeTask): ProducerFailure | OverrideError {
|
|
778
|
-
const result = verifyTaskEvent(
|
|
801
|
+
async function probeVerificationFailure(taskRef: string, policy: FailurePolicy, resolved: ProbeTask): Promise<ProducerFailure | OverrideError> {
|
|
802
|
+
const result = await verifyTaskEvent(
|
|
779
803
|
{
|
|
780
804
|
taskRef,
|
|
781
805
|
event: 'review-code.completed',
|
|
@@ -795,12 +819,12 @@ function latestArtifact(taskDir: string, family: string): string | undefined {
|
|
|
795
819
|
));
|
|
796
820
|
candidates.sort((a, b) => {
|
|
797
821
|
const round = (name: string) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
|
|
798
|
-
|
|
799
|
-
|
|
822
|
+
return round(b) - round(a);
|
|
823
|
+
});
|
|
800
824
|
return candidates[0];
|
|
801
825
|
}
|
|
802
826
|
|
|
803
|
-
function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTask): ProducerFailure | OverrideError {
|
|
827
|
+
async function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTask): Promise<ProducerFailure | OverrideError> {
|
|
804
828
|
const candidates = [
|
|
805
829
|
{ skillName: 'review-code', family: 'review-code' },
|
|
806
830
|
{ skillName: 'review-plan', family: 'review-plan' },
|
|
@@ -814,7 +838,7 @@ function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTa
|
|
|
814
838
|
const artifactFile = candidate.family ? latestArtifact(resolved.taskDir, candidate.family) : undefined;
|
|
815
839
|
if (candidate.family && !artifactFile) continue;
|
|
816
840
|
try {
|
|
817
|
-
const payload = verifyInProcess({
|
|
841
|
+
const payload = await verifyInProcess({
|
|
818
842
|
mode: 'gate',
|
|
819
843
|
skillName: candidate.skillName,
|
|
820
844
|
taskDir: resolved.taskDir,
|
|
@@ -838,12 +862,12 @@ function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTa
|
|
|
838
862
|
return probeNotObserved(policy);
|
|
839
863
|
}
|
|
840
864
|
|
|
841
|
-
function probeGenericFailure(
|
|
865
|
+
async function probeGenericFailure(
|
|
842
866
|
taskRef: string,
|
|
843
867
|
policy: FailurePolicy,
|
|
844
868
|
resolved: ProbeTask,
|
|
845
869
|
options: HumanOverrideOptions = {}
|
|
846
|
-
): ProducerFailure | OverrideError {
|
|
870
|
+
): Promise<ProducerFailure | OverrideError> {
|
|
847
871
|
switch (policy.producerId) {
|
|
848
872
|
case 'task.resolve':
|
|
849
873
|
return probeNotObserved(policy);
|
|
@@ -852,9 +876,9 @@ function probeGenericFailure(
|
|
|
852
876
|
case 'task-event':
|
|
853
877
|
return probeTaskEventFailure(taskRef, policy, resolved);
|
|
854
878
|
case 'task-verify':
|
|
855
|
-
return probeVerificationFailure(taskRef, policy, resolved);
|
|
879
|
+
return await probeVerificationFailure(taskRef, policy, resolved);
|
|
856
880
|
case 'verification-engine':
|
|
857
|
-
return probeVerificationEngineFailure(policy, resolved);
|
|
881
|
+
return await probeVerificationEngineFailure(policy, resolved);
|
|
858
882
|
case 'activity-intent':
|
|
859
883
|
return probeResult(policy, applyPrReviewActivityIntent({
|
|
860
884
|
kind: 'pr-review-start', taskRef, agent: 'codex', artifact: 'pr-review.md', head: '0'.repeat(40), dryRun: true
|
|
@@ -889,7 +913,7 @@ function probeGenericFailure(
|
|
|
889
913
|
}
|
|
890
914
|
case 'platform.issue':
|
|
891
915
|
case 'platform.pull-request':
|
|
892
|
-
return probePlatformFailure(taskRef, policy, resolved, options);
|
|
916
|
+
return await probePlatformFailure(taskRef, policy, resolved, options);
|
|
893
917
|
default:
|
|
894
918
|
return error('OVERRIDE_PROBE_INPUT_MISSING', `no producer probe is registered for ${policy.producerId}`);
|
|
895
919
|
}
|
|
@@ -899,7 +923,7 @@ function isProducerFailure(value: ProducerFailure | OverrideError): value is Pro
|
|
|
899
923
|
return 'observedFacts' in value && 'state' in value && Array.isArray(value.observedFacts);
|
|
900
924
|
}
|
|
901
925
|
|
|
902
|
-
function captureFailureSnapshot(taskRef: string, failureIdValue: string, target: string, options: HumanOverrideOptions = {}): FailureSnapshot | OverrideError {
|
|
926
|
+
async function captureFailureSnapshot(taskRef: string, failureIdValue: string, target: string, options: HumanOverrideOptions = {}): Promise<FailureSnapshot | OverrideError> {
|
|
903
927
|
const policy = policyFor(failureIdValue);
|
|
904
928
|
if (!policy) return error('OVERRIDE_FAILURE_UNKNOWN', `failure policy '${failureIdValue}' is not registered`);
|
|
905
929
|
if (!policy.targets.includes(target)) return error('OVERRIDE_TARGET_INVALID', `target '${target}' is not registered for ${failureIdValue}`);
|
|
@@ -915,7 +939,7 @@ function captureFailureSnapshot(taskRef: string, failureIdValue: string, target:
|
|
|
915
939
|
try { frontmatter = parseTypedTaskFrontmatter(content); } catch { /* probe reports an unconfirmed identity */ }
|
|
916
940
|
const producer: ProducerFailure | OverrideError = policy.producerId === LIFECYCLE_PRODUCER
|
|
917
941
|
? probeLifecycleFailure(taskRef, policy, resolved, options)
|
|
918
|
-
: probeGenericFailure(taskRef, policy, resolved, options);
|
|
942
|
+
: await probeGenericFailure(taskRef, policy, resolved, options);
|
|
919
943
|
if (!isProducerFailure(producer)) return producer;
|
|
920
944
|
const digest = computeFailureDigest(snapshotEvidence(
|
|
921
945
|
resolved.taskId,
|
|
@@ -984,7 +1008,7 @@ function validateResourceNumbers(request: HumanOverrideRequest): OverrideError |
|
|
|
984
1008
|
return null;
|
|
985
1009
|
}
|
|
986
1010
|
|
|
987
|
-
function issueHumanOverride(request: HumanOverrideRequest, options: HumanOverrideOptions = {}): HumanOverrideIssueResult {
|
|
1011
|
+
async function issueHumanOverride(request: HumanOverrideRequest, options: HumanOverrideOptions = {}): Promise<HumanOverrideIssueResult> {
|
|
988
1012
|
const invalid = validateCommon(request);
|
|
989
1013
|
if (invalid) return failedIssue(invalid.code, invalid.message);
|
|
990
1014
|
const policy = policyFor(request.failureId);
|
|
@@ -1005,7 +1029,7 @@ function issueHumanOverride(request: HumanOverrideRequest, options: HumanOverrid
|
|
|
1005
1029
|
const document = readTask(resolved.taskMdPath);
|
|
1006
1030
|
if ('code' in document) return failedIssue(document.code, document.message, resolved.taskId);
|
|
1007
1031
|
if (document.frontmatter.id !== resolved.taskId) return failedIssue('OVERRIDE_DOCUMENT_INVALID', 'task frontmatter id does not match resolved task', resolved.taskId);
|
|
1008
|
-
const snapshotResult = captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1032
|
+
const snapshotResult = await captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1009
1033
|
...options,
|
|
1010
1034
|
probeIntent: request.intent,
|
|
1011
1035
|
probeOperator: request.operator,
|
|
@@ -1127,18 +1151,18 @@ function lifecycleRequestFor(
|
|
|
1127
1151
|
return { taskRef, intent: 'restore', agent: operator, stagingDir, issueNumber };
|
|
1128
1152
|
}
|
|
1129
1153
|
|
|
1130
|
-
function applyOutcomeEffect(
|
|
1154
|
+
async function applyOutcomeEffect(
|
|
1131
1155
|
request: ConsumeHumanOverrideRequest,
|
|
1132
1156
|
row: Readonly<Record<string, string>>,
|
|
1133
1157
|
snapshot: FailureSnapshot,
|
|
1134
1158
|
outcome: Outcome,
|
|
1135
1159
|
options: HumanOverrideOptions
|
|
1136
|
-
): OverrideError | null {
|
|
1160
|
+
): Promise<OverrideError | null> {
|
|
1137
1161
|
if (outcome.effect === 'no-write' || outcome.effect === 'record-only') return null;
|
|
1138
1162
|
const intent = outcome.target === 'safe-close' ? 'cancel' : row.intent || request.intent;
|
|
1139
1163
|
if (!intent && outcome.target === 'continue-local') {
|
|
1140
1164
|
if (!options.effectExecutor) return error('OVERRIDE_EFFECT_EXECUTOR_MISSING', 'the producer must supply an effect executor for a continue-local outcome');
|
|
1141
|
-
return options.effectExecutor({
|
|
1165
|
+
return await options.effectExecutor({
|
|
1142
1166
|
failureId: request.failureId,
|
|
1143
1167
|
operator: row.operator ?? 'local-operator',
|
|
1144
1168
|
reason: row.reason ?? snapshot.message
|
|
@@ -1242,7 +1266,7 @@ function snapshotFromTicket(taskId: string, row: Readonly<Record<string, string>
|
|
|
1242
1266
|
};
|
|
1243
1267
|
}
|
|
1244
1268
|
|
|
1245
|
-
function consumeHumanOverride(request: ConsumeHumanOverrideRequest, options: HumanOverrideOptions = {}): HumanOverrideConsumeResult {
|
|
1269
|
+
async function consumeHumanOverride(request: ConsumeHumanOverrideRequest, options: HumanOverrideOptions = {}): Promise<HumanOverrideConsumeResult> {
|
|
1246
1270
|
const values = [request.taskRef, request.ticketId, request.failureId, request.target, request.scope];
|
|
1247
1271
|
if (values.some((value) => !singleLine(value))) {
|
|
1248
1272
|
return failedConsume('OVERRIDE_PAYLOAD_INVALID', 'consume fields must be non-empty single-line values', request.ticketId || null);
|
|
@@ -1278,7 +1302,7 @@ function consumeHumanOverride(request: ConsumeHumanOverrideRequest, options: Hum
|
|
|
1278
1302
|
if (isOverrideError(recoverySnapshot)) return failedConsume(recoverySnapshot.code, recoverySnapshot.message, request.ticketId, resolved.taskId);
|
|
1279
1303
|
snapshot = recoverySnapshot;
|
|
1280
1304
|
} else {
|
|
1281
|
-
const snapshotResult = captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1305
|
+
const snapshotResult = await captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1282
1306
|
...options,
|
|
1283
1307
|
probeIntent: row.values.intent ? row.values.intent as TaskLifecycleIntent : request.intent,
|
|
1284
1308
|
probeOperator: row.values.operator,
|
|
@@ -1323,7 +1347,7 @@ function consumeHumanOverride(request: ConsumeHumanOverrideRequest, options: Hum
|
|
|
1323
1347
|
if (staged.status === 'planned') return failedConsume('OVERRIDE_WRITE_INVALID', 'override writes cannot be planned', request.ticketId, resolved.taskId);
|
|
1324
1348
|
effectRow = { ...row.values, status: 'consuming', effect: outcome.effect, result: outcome.result, context_id: outcome.contextId, residual: `effect pending: ${outcome.residual}` };
|
|
1325
1349
|
}
|
|
1326
|
-
const effectError = applyOutcomeEffect(request, effectRow, snapshot, outcome, options);
|
|
1350
|
+
const effectError = await applyOutcomeEffect(request, effectRow, snapshot, outcome, options);
|
|
1327
1351
|
if (effectError) return failedConsume(effectError.code, effectError.message, request.ticketId, resolved.taskId);
|
|
1328
1352
|
const after = resolveTask(request.taskRef, options);
|
|
1329
1353
|
if (!after.ok) return failedConsume(after.code, after.message, request.ticketId, after.taskId);
|
|
@@ -1359,7 +1383,7 @@ function diagnoseHumanOverride(failure: string, target?: string) {
|
|
|
1359
1383
|
};
|
|
1360
1384
|
}
|
|
1361
1385
|
|
|
1362
|
-
function diagnoseHumanOverrideForTask(
|
|
1386
|
+
async function diagnoseHumanOverrideForTask(
|
|
1363
1387
|
taskRef: string,
|
|
1364
1388
|
failure?: string,
|
|
1365
1389
|
target?: string,
|
|
@@ -1403,12 +1427,12 @@ function diagnoseHumanOverrideForTask(
|
|
|
1403
1427
|
error: error('OVERRIDE_FAILURE_UNKNOWN', `failure policy '${failure}' is not registered`)
|
|
1404
1428
|
};
|
|
1405
1429
|
}
|
|
1406
|
-
const probes = policies
|
|
1430
|
+
const probes = await Promise.all(policies
|
|
1407
1431
|
.filter((policy) => !target || policy.targets.includes(target))
|
|
1408
|
-
.map((policy) => {
|
|
1432
|
+
.map(async (policy) => {
|
|
1409
1433
|
const producer = policy.producerId === LIFECYCLE_PRODUCER
|
|
1410
1434
|
? probeLifecycleFailure(taskRef, policy, resolved, options)
|
|
1411
|
-
|
|
1435
|
+
: await probeGenericFailure(taskRef, policy, resolved, options);
|
|
1412
1436
|
const observed = isProducerFailure(producer);
|
|
1413
1437
|
return {
|
|
1414
1438
|
failureId: policy.id,
|
|
@@ -1422,7 +1446,7 @@ function diagnoseHumanOverrideForTask(
|
|
|
1422
1446
|
message: observed ? producer.message : producer.message,
|
|
1423
1447
|
errorCode: observed ? null : producer.code
|
|
1424
1448
|
};
|
|
1425
|
-
});
|
|
1449
|
+
}));
|
|
1426
1450
|
const blockedBy = probes.filter((probe) => probe.status === 'observed');
|
|
1427
1451
|
return {
|
|
1428
1452
|
status: 'ready' as const,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { parseArtifactReceipts, sha256File } from './artifact-receipts.ts';
|
|
5
|
+
import { invalidationMutation, parseInvalidationDocument, reconcileInvalidation } from './invalidation.ts';
|
|
6
|
+
import { resolveTaskRef } from './resolve-ref.ts';
|
|
7
|
+
import { writeTask } from './write.ts';
|
|
8
|
+
import type { TaskWriteOptions } from './write.ts';
|
|
9
|
+
|
|
10
|
+
type InvalidationCommandOptions = TaskWriteOptions & { now?: () => string; maxTargets?: number; dryRun?: boolean };
|
|
11
|
+
type InvalidationCommandResult = {
|
|
12
|
+
status: 'applied' | 'planned' | 'no-op' | 'failed';
|
|
13
|
+
changed: boolean;
|
|
14
|
+
taskId: string | null;
|
|
15
|
+
processed: number;
|
|
16
|
+
remaining: number;
|
|
17
|
+
error: { code: string; message: string } | null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function reconcileTaskInvalidation(taskRef: string, options: InvalidationCommandOptions = {}): InvalidationCommandResult {
|
|
21
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
22
|
+
if (!resolved.ok) return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: resolved.code, message: resolved.message } };
|
|
23
|
+
let content: string;
|
|
24
|
+
try { content = fs.readFileSync(resolved.taskMdPath, 'utf8'); }
|
|
25
|
+
catch (error) { return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error) } }; }
|
|
26
|
+
const parsed = parseInvalidationDocument(content);
|
|
27
|
+
if (!parsed.ok) return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: parsed.code, message: parsed.message } };
|
|
28
|
+
if (!parsed.present) return { status: 'no-op', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: null };
|
|
29
|
+
const beforePending = parsed.document.targets.filter((target) => target.status !== 'completed').length;
|
|
30
|
+
let receipts;
|
|
31
|
+
try { receipts = parseArtifactReceipts(content).rows; }
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (parsed.document.targets.some((target) => target.status !== 'completed' && target.targetKind === 'receipt')) {
|
|
34
|
+
return {
|
|
35
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
36
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: error instanceof Error ? error.message : String(error) }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
receipts = [];
|
|
40
|
+
}
|
|
41
|
+
for (const target of parsed.document.targets) {
|
|
42
|
+
if (target.status === 'completed') continue;
|
|
43
|
+
if (target.targetKind === 'receipt') {
|
|
44
|
+
const receipt = receipts.find((candidate) => candidate.output === target.targetArtifact);
|
|
45
|
+
if (!receipt || receipt.inputSha256 !== target.targetSha256) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
48
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation receipt target '${target.targetArtifact}' changed before reconcile` }
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const targetPath = path.join(resolved.taskDir, target.targetArtifact);
|
|
54
|
+
try {
|
|
55
|
+
const stat = fs.lstatSync(targetPath);
|
|
56
|
+
if (!stat.isFile() || stat.isSymbolicLink() || sha256File(targetPath) !== target.targetSha256) {
|
|
57
|
+
return {
|
|
58
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
59
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation target '${target.targetArtifact}' changed before reconcile` }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
return {
|
|
64
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
65
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation target '${target.targetArtifact}' is unavailable before reconcile` }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const reconciled = reconcileInvalidation(parsed.document, (options.now ?? (() => new Date().toISOString()))(), options.maxTargets);
|
|
70
|
+
if (!reconciled.ok) return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending, error: { code: reconciled.code, message: reconciled.message } };
|
|
71
|
+
if (!reconciled.changed) return { status: 'no-op', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending, error: null };
|
|
72
|
+
const result = writeTask({
|
|
73
|
+
taskRef, expectedState: resolved.state, dryRun: options.dryRun,
|
|
74
|
+
mutations: [invalidationMutation(content, reconciled.document)]
|
|
75
|
+
}, { ...options, invalidationContext: 'reconcile', taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state } });
|
|
76
|
+
if (result.status === 'failed') return { status: 'failed', changed: false, taskId: resolved.taskId, processed: beforePending - reconciled.document.targets.filter((target) => target.status !== 'completed').length, remaining: reconciled.document.targets.filter((target) => target.status !== 'completed').length, error: result.error };
|
|
77
|
+
return {
|
|
78
|
+
status: result.status, changed: result.changed, taskId: resolved.taskId,
|
|
79
|
+
processed: beforePending - reconciled.document.targets.filter((target) => target.status !== 'completed').length,
|
|
80
|
+
remaining: reconciled.document.targets.filter((target) => target.status !== 'completed').length,
|
|
81
|
+
error: null
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { reconcileTaskInvalidation };
|
|
86
|
+
export type { InvalidationCommandOptions, InvalidationCommandResult };
|