@fitlab-ai/agent-infra 0.11.2 → 0.11.4
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 +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +7 -2
- package/dist/lib/agent-clients/adapter.js +3 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
- package/dist/lib/agent-clients/adapters/codex.js +10 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +22 -27
- package/dist/lib/internal/sandbox-control.js +1 -43
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +41 -1
- package/dist/lib/internal/task-ledger.js +13 -5
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +7 -2
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/issue-comments.js +2 -31
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -79
- package/dist/lib/sandbox/control/protocol.js +5 -64
- package/dist/lib/sandbox/control/server.js +49 -343
- package/dist/lib/sandbox/control/state.js +1 -7
- package/dist/lib/sandbox/control/task-view.js +1 -2
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +1 -26
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +1 -0
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +37 -38
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/decision-intents.js +12 -0
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +80 -64
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +1 -1
- package/dist/lib/task/finalization.js +319 -234
- package/dist/lib/task/ledger-intents.js +157 -15
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +11 -1
- package/dist/lib/task/local-artifact-finalization.js +1 -20
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +29 -13
- package/dist/lib/task/review-finalization.js +3 -16
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/verification-engine.js +20 -30
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workflow-warning-intents.js +21 -6
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +46 -0
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
- package/lib/agent-clients/adapters/codex.ts +10 -0
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +22 -26
- package/lib/internal/sandbox-control.ts +0 -42
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +40 -1
- package/lib/internal/task-ledger.ts +12 -5
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +8 -4
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/issue-comments.ts +1 -35
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -81
- package/lib/sandbox/control/protocol.ts +7 -83
- package/lib/sandbox/control/server.ts +47 -362
- package/lib/sandbox/control/state.ts +1 -9
- package/lib/sandbox/control/task-view.ts +1 -2
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +1 -26
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +1 -0
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +41 -48
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/decision-intents.ts +9 -0
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +82 -64
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +1 -1
- package/lib/task/finalization.ts +310 -216
- package/lib/task/ledger-intents.ts +155 -15
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +15 -2
- package/lib/task/local-artifact-finalization.ts +2 -25
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +56 -27
- package/lib/task/review-finalization.ts +3 -16
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/verification-engine.ts +19 -30
- package/lib/task/verification.ts +2 -1
- package/lib/task/workflow-warning-intents.ts +18 -6
- package/lib/task/workspace-snapshot.ts +3 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/sandbox/control/finalization-response.js +0 -35
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -142
- package/dist/lib/task/lifecycle-recovery.js +0 -549
- package/dist/lib/task/recovery-warning.js +0 -19
- package/dist/lib/task/workflow-command.js +0 -21
- package/dist/lib/task/workflow-dispatch.js +0 -213
- package/lib/sandbox/control/finalization-response.ts +0 -37
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -156
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -22
- package/lib/task/workflow-dispatch.ts +0 -214
|
@@ -16,7 +16,6 @@ import { allowsManualOverride } from './guard-override.ts';
|
|
|
16
16
|
import type { ManualOverrideCapability } from './guard-override.ts';
|
|
17
17
|
import { getArtifactSchema } from './artifact-schema.ts';
|
|
18
18
|
import { canonicalSemanticDigest, inspectArtifactContract, sha256Content } from './artifact-operations.ts';
|
|
19
|
-
import { expectedQualificationRelations, validateQualificationAudit } from './qualification-audit.ts';
|
|
20
19
|
|
|
21
20
|
type ReviewFinalizationErrorCode =
|
|
22
21
|
| ResolveTaskRefErrorCode
|
|
@@ -30,7 +29,6 @@ type ReviewFinalizationErrorCode =
|
|
|
30
29
|
| 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
|
|
31
30
|
| 'REVIEW_SUMMARY_COUNT_MISMATCH'
|
|
32
31
|
| 'REVIEW_ARTIFACT_STRUCTURE_INVALID'
|
|
33
|
-
| 'REVIEW_ARTIFACT_QUALIFICATION_INVALID'
|
|
34
32
|
| 'REVIEW_DECISION_DETAIL_INVALID'
|
|
35
33
|
| 'REVIEW_ARTIFACT_CONFLICT'
|
|
36
34
|
| 'REVIEW_PROVENANCE_INVALID'
|
|
@@ -143,10 +141,8 @@ function preflightReviewSummaryUnlocked(
|
|
|
143
141
|
const publicationError = validateArtifactPublication(resolved.taskDir, spec.family, request.artifact);
|
|
144
142
|
if (publicationError) return preflightFailed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', publicationError.message, resolved.taskId);
|
|
145
143
|
|
|
146
|
-
let taskContent: string;
|
|
147
144
|
let content: string;
|
|
148
145
|
try {
|
|
149
|
-
taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
150
146
|
const validated = validateCompletedArtifact(resolved.taskDir, spec.family, request.artifact);
|
|
151
147
|
if (!validated.ok) return preflightFailed(request, validated.error.code === 'ARTIFACT_NOT_REGULAR' ? 'REVIEW_ARTIFACT_NOT_REGULAR' : 'REVIEW_ARTIFACT_IDENTITY_INVALID', validated.error.message, resolved.taskId);
|
|
152
148
|
content = fs.readFileSync(validated.artifact.path, 'utf8');
|
|
@@ -163,21 +159,12 @@ function preflightReviewSummaryUnlocked(
|
|
|
163
159
|
const semanticDigest = canonicalSemanticDigest(content);
|
|
164
160
|
const detail = inspectDecisionDetailDuplicates(content);
|
|
165
161
|
const structure = inspectArtifactContract(content, getArtifactSchema(spec.family)!);
|
|
166
|
-
|
|
167
|
-
if (!expected.ok) return preflightFailed(request, 'REVIEW_ARTIFACT_QUALIFICATION_INVALID', `${expected.code}: ${expected.message}`, resolved.taskId, { artifactSha256, semanticDigest });
|
|
168
|
-
const qualification = validateQualificationAudit(taskContent, content, {
|
|
169
|
-
family: spec.family,
|
|
170
|
-
artifact: request.artifact,
|
|
171
|
-
expectedUpstreamRelations: expected.relations
|
|
172
|
-
});
|
|
173
|
-
const qualificationError = qualification.ok ? null : qualification;
|
|
174
|
-
if (!detail.ok || !structure.ok || qualificationError) {
|
|
162
|
+
if (!detail.ok || !structure.ok) {
|
|
175
163
|
const diagnostics = [
|
|
176
164
|
...(!detail.ok ? [`${detail.code}: ${detail.message}`] : []),
|
|
177
|
-
...structure.diagnostics.map((item) => `${item.code}: ${item.message}`)
|
|
178
|
-
...(qualificationError ? [`${qualificationError.code}: ${qualificationError.message}`] : [])
|
|
165
|
+
...structure.diagnostics.map((item) => `${item.code}: ${item.message}`)
|
|
179
166
|
].join('; ');
|
|
180
|
-
return preflightFailed(request, !detail.ok ? 'REVIEW_DECISION_DETAIL_INVALID' :
|
|
167
|
+
return preflightFailed(request, !detail.ok ? 'REVIEW_DECISION_DETAIL_INVALID' : 'REVIEW_ARTIFACT_STRUCTURE_INVALID', diagnostics, resolved.taskId, {
|
|
181
168
|
artifactSha256, semanticDigest,
|
|
182
169
|
});
|
|
183
170
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const REWORK_INTENT_HEADINGS = ['返工意图', 'Rework Intent'] as const;
|
|
2
2
|
const REWORK_INTENT_COLUMNS = [
|
|
3
|
+
'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
|
|
4
|
+
'classification', 'evidence_digest', 'task_fact_digest', 'status', 'declared_at', 'consumed_at'
|
|
5
|
+
] as const;
|
|
6
|
+
const LEGACY_REWORK_INTENT_COLUMNS = [
|
|
3
7
|
'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
|
|
4
8
|
'status', 'declared_at', 'consumed_at'
|
|
5
9
|
] as const;
|
|
6
|
-
type ReworkTarget = 'analysis' | 'plan' | 'code';
|
|
10
|
+
type ReworkTarget = 'analysis' | 'plan' | 'code' | 'pause';
|
|
11
|
+
type ReworkClassification = 'scope-or-requirement' | 'design' | 'implementation' | 'human-decision' | 'insufficient-evidence';
|
|
7
12
|
type ReworkIntentStatus = 'pending' | 'consumed' | 'superseded';
|
|
8
13
|
type ReworkIntent = {
|
|
9
14
|
intentId: string;
|
|
@@ -11,6 +16,9 @@ type ReworkIntent = {
|
|
|
11
16
|
sourceArtifact: string;
|
|
12
17
|
sourceSha256: string;
|
|
13
18
|
target: ReworkTarget;
|
|
19
|
+
classification?: ReworkClassification;
|
|
20
|
+
evidenceDigest?: string;
|
|
21
|
+
taskFactDigest?: string;
|
|
14
22
|
status: ReworkIntentStatus;
|
|
15
23
|
declaredAt: string;
|
|
16
24
|
consumedAt: string;
|
|
@@ -66,15 +74,19 @@ function parseReworkIntentDocument(content: string): ReworkIntentParseResult {
|
|
|
66
74
|
const row = Object.fromEntries(REWORK_INTENT_COLUMNS.map((column, index) => [column, values[index]!])) as Record<string, string>;
|
|
67
75
|
if (!/^RI-[1-9]\d*$/.test(row.intent_id!)) throw new Error(`invalid intent id '${row.intent_id}'`);
|
|
68
76
|
if (!/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id!)) throw new Error(`invalid finding id '${row.finding_id}'`);
|
|
69
|
-
if (!['analysis', 'plan', 'code'].includes(row.target!)) throw new Error(`invalid rework target '${row.target}'`);
|
|
77
|
+
if (!['analysis', 'plan', 'code', 'pause'].includes(row.target!)) throw new Error(`invalid rework target '${row.target}'`);
|
|
78
|
+
if (!['scope-or-requirement', 'design', 'implementation', 'human-decision', 'insufficient-evidence'].includes(row.classification!)) throw new Error(`invalid rework classification '${row.classification}'`);
|
|
70
79
|
if (!['pending', 'consumed', 'superseded'].includes(row.status!)) throw new Error(`invalid rework status '${row.status}'`);
|
|
71
80
|
if (!/^[a-f0-9]{64}$/.test(row.source_sha256!)) throw new Error(`invalid source hash for '${row.intent_id}'`);
|
|
81
|
+
if (!/^[a-f0-9]{64}$/.test(row.evidence_digest!) || !/^[a-f0-9]{64}$/.test(row.task_fact_digest!)) throw new Error(`invalid semantic digest for '${row.intent_id}'`);
|
|
72
82
|
if (!row.source_artifact || !row.declared_at) throw new Error(`rework intent '${row.intent_id}' has invalid required fields`);
|
|
73
83
|
const terminal = row.status === 'consumed' || row.status === 'superseded';
|
|
74
84
|
if (terminal !== Boolean(row.consumed_at)) throw new Error(`rework intent '${row.intent_id}' has invalid consumed_at`);
|
|
75
85
|
return {
|
|
76
86
|
intentId: row.intent_id!, findingId: row.finding_id!, sourceArtifact: row.source_artifact!,
|
|
77
87
|
sourceSha256: row.source_sha256!, target: row.target as ReworkTarget,
|
|
88
|
+
classification: row.classification as ReworkClassification,
|
|
89
|
+
evidenceDigest: row.evidence_digest!, taskFactDigest: row.task_fact_digest!,
|
|
78
90
|
status: row.status as ReworkIntentStatus, declaredAt: row.declared_at!, consumedAt: row.consumed_at!
|
|
79
91
|
} satisfies ReworkIntent;
|
|
80
92
|
});
|
|
@@ -96,7 +108,10 @@ function renderReworkIntents(intents: readonly ReworkIntent[]): string {
|
|
|
96
108
|
`| ${REWORK_INTENT_COLUMNS.map(() => '---').join(' | ')} |`,
|
|
97
109
|
...intents.map((intent) => `| ${[
|
|
98
110
|
intent.intentId, intent.findingId, intent.sourceArtifact, intent.sourceSha256,
|
|
99
|
-
intent.target, intent.
|
|
111
|
+
intent.target, intent.classification ?? (intent.target === 'analysis' ? 'scope-or-requirement' : intent.target === 'plan' ? 'design' : intent.target === 'code' ? 'implementation' : 'insufficient-evidence'),
|
|
112
|
+
intent.evidenceDigest ?? requireDigest(`${intent.findingId}|${intent.sourceArtifact}|${intent.sourceSha256}`),
|
|
113
|
+
intent.taskFactDigest ?? requireDigest(`current|${intent.findingId}|${intent.target}`),
|
|
114
|
+
intent.status, intent.declaredAt, intent.consumedAt
|
|
100
115
|
].map(escape).join(' | ')} |`)
|
|
101
116
|
].join('\n');
|
|
102
117
|
}
|
|
@@ -104,11 +119,63 @@ function renderReworkIntents(intents: readonly ReworkIntent[]): string {
|
|
|
104
119
|
function upsertReworkIntent(intents: readonly ReworkIntent[], intent: ReworkIntent): { changed: boolean; intents: readonly ReworkIntent[] } {
|
|
105
120
|
const existing = intents.find((candidate) => candidate.intentId === intent.intentId);
|
|
106
121
|
if (!existing) return { changed: true, intents: [...intents, intent] };
|
|
107
|
-
const identity = (value: ReworkIntent) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target];
|
|
122
|
+
const identity = (value: ReworkIntent) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target, value.classification, value.evidenceDigest, value.taskFactDigest];
|
|
108
123
|
if (JSON.stringify(identity(existing)) !== JSON.stringify(identity(intent))) throw new Error(`intent '${intent.intentId}' identity conflicts with existing state`);
|
|
109
124
|
return { changed: false, intents };
|
|
110
125
|
}
|
|
111
126
|
|
|
127
|
+
function consumeHumanDecisionReworkIntents(
|
|
128
|
+
intents: readonly ReworkIntent[],
|
|
129
|
+
findingId: string,
|
|
130
|
+
consumedAt: string
|
|
131
|
+
): { changed: boolean; intents: readonly ReworkIntent[] } {
|
|
132
|
+
let changed = false;
|
|
133
|
+
const next = intents.map((intent) => {
|
|
134
|
+
if (intent.status !== 'pending' || intent.target !== 'pause' || intent.classification !== 'human-decision' || intent.findingId !== findingId) return intent;
|
|
135
|
+
changed = true;
|
|
136
|
+
return { ...intent, status: 'consumed' as const, consumedAt };
|
|
137
|
+
});
|
|
138
|
+
return { changed, intents: next };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseLegacyReworkIntentDocument(content: string): ReworkIntentParseResult {
|
|
142
|
+
// TODO(compat): Remove this explicit decoder once no task contains the eight-column rework-intent header.
|
|
143
|
+
const body = sectionBody(content);
|
|
144
|
+
if (body === null) return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'legacy rework intent section is missing' };
|
|
145
|
+
const lines = body.split(/\r?\n/).filter((line) => line.trim());
|
|
146
|
+
const header = lines[0] ? cells(lines[0]) : null;
|
|
147
|
+
const separator = lines[1] ? cells(lines[1]) : null;
|
|
148
|
+
if (!header || !separator || header.length !== LEGACY_REWORK_INTENT_COLUMNS.length || !header.every((value, index) => value === LEGACY_REWORK_INTENT_COLUMNS[index])) {
|
|
149
|
+
return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'rework intent section is not the known legacy table' };
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const intents = lines.slice(2).map((line) => {
|
|
153
|
+
const values = cells(line);
|
|
154
|
+
if (!values || values.length !== LEGACY_REWORK_INTENT_COLUMNS.length) throw new Error('legacy rework intent row has the wrong column count');
|
|
155
|
+
const row = Object.fromEntries(LEGACY_REWORK_INTENT_COLUMNS.map((column, index) => [column, values[index]!])) as Record<string, string>;
|
|
156
|
+
if (!/^RI-[1-9]\d*$/.test(row.intent_id!) || !/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id!)) throw new Error('legacy rework intent identity is invalid');
|
|
157
|
+
if (!['analysis', 'plan', 'code'].includes(row.target!) || !['pending', 'consumed', 'superseded'].includes(row.status!)) throw new Error('legacy rework intent state is invalid');
|
|
158
|
+
if (!/^[a-f0-9]{64}$/.test(row.source_sha256!)) throw new Error('legacy rework intent source hash is invalid');
|
|
159
|
+
const legacyDigest = (value: string) => requireDigest(value);
|
|
160
|
+
const classification: ReworkClassification = row.target === 'analysis' ? 'scope-or-requirement' : row.target === 'plan' ? 'design' : 'implementation';
|
|
161
|
+
return {
|
|
162
|
+
intentId: row.intent_id!, findingId: row.finding_id!, sourceArtifact: row.source_artifact!, sourceSha256: row.source_sha256!,
|
|
163
|
+
target: row.target as ReworkTarget, classification,
|
|
164
|
+
evidenceDigest: legacyDigest(`${row.finding_id}|${row.source_artifact}|${row.source_sha256}`),
|
|
165
|
+
taskFactDigest: legacyDigest(`legacy|${row.finding_id}|${row.target}`),
|
|
166
|
+
status: row.status as ReworkIntentStatus, declaredAt: row.declared_at!, consumedAt: row.consumed_at!
|
|
167
|
+
} satisfies ReworkIntent;
|
|
168
|
+
});
|
|
169
|
+
return { ok: true, present: true, intents };
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: error instanceof Error ? error.message : String(error) };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function requireDigest(value: string): string {
|
|
176
|
+
return createHash('sha256').update(value).digest('hex');
|
|
177
|
+
}
|
|
178
|
+
|
|
112
179
|
function consumeReworkIntents(
|
|
113
180
|
intents: readonly ReworkIntent[],
|
|
114
181
|
target: ReworkTarget,
|
|
@@ -161,5 +228,8 @@ export {
|
|
|
161
228
|
consumeReworkIntents,
|
|
162
229
|
supersedeReworkIntents,
|
|
163
230
|
upsertReworkIntent
|
|
231
|
+
, consumeHumanDecisionReworkIntents
|
|
232
|
+
, parseLegacyReworkIntentDocument
|
|
164
233
|
};
|
|
165
|
-
export type { ReworkIntent, ReworkIntentParseResult, ReworkIntentStatus, ReworkTarget };
|
|
234
|
+
export type { ReworkClassification, ReworkIntent, ReworkIntentParseResult, ReworkIntentStatus, ReworkTarget };
|
|
235
|
+
import { createHash } from 'node:crypto';
|
|
@@ -40,7 +40,6 @@ import type { OrchestrationRun } from "./orchestration.ts";
|
|
|
40
40
|
import { resolveDeliveryTarget } from "./delivery-target.ts";
|
|
41
41
|
import { readPrDeliveryFact } from "./pr-delivery-fact.ts";
|
|
42
42
|
import { validateLocalArtifact } from "./local-artifact-finalization.ts";
|
|
43
|
-
import { validateQualificationAudit } from "./qualification-audit.ts";
|
|
44
43
|
import { getArtifactSchema } from "./artifact-schema.ts";
|
|
45
44
|
import { inspectArtifactContract } from "./artifact-operations.ts";
|
|
46
45
|
import type { VerificationShared } from "./verification-types.ts";
|
|
@@ -78,7 +77,6 @@ const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
78
77
|
|
|
79
78
|
// Review disagreement ledger (see .agents/rules/review-handshake.md).
|
|
80
79
|
const LEDGER_SECTION_NAMES = ["审查分歧账本", "Review Disagreement Ledger"];
|
|
81
|
-
const DEFAULT_MAX_HANDSHAKE_ROUNDS = 3;
|
|
82
80
|
const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
|
|
83
81
|
const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
84
82
|
|
|
@@ -511,19 +509,6 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }: any): any {
|
|
|
511
509
|
}
|
|
512
510
|
|
|
513
511
|
const content = fs.readFileSync(artifactPath, "utf8");
|
|
514
|
-
let taskContent = "";
|
|
515
|
-
const taskPath = path.join(taskDir, "task.md");
|
|
516
|
-
if (fs.existsSync(taskPath)) {
|
|
517
|
-
try {
|
|
518
|
-
taskContent = fs.readFileSync(taskPath, "utf8");
|
|
519
|
-
} catch (error) {
|
|
520
|
-
return failResult("artifact", `Cannot read task qualification contract: ${error instanceof Error ? error.message : String(error)}`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (taskContent && /^review-(?:analysis|plan|code)$/.test(skillName)) {
|
|
524
|
-
const qualification = validateQualificationAudit(taskContent, content, { artifact: path.basename(artifactPath) });
|
|
525
|
-
if (!qualification.ok) return failResult("artifact", `${path.basename(artifactPath)} has qualification audit errors: ${qualification.code}: ${qualification.message}`);
|
|
526
|
-
}
|
|
527
512
|
const schemaFamily = skillName === "analyze-task"
|
|
528
513
|
? "analysis"
|
|
529
514
|
: skillName === "review-analysis"
|
|
@@ -547,9 +532,7 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }: any): any {
|
|
|
547
532
|
}
|
|
548
533
|
if (localFamily) {
|
|
549
534
|
const local = validateLocalArtifact(content, {
|
|
550
|
-
family: localFamily
|
|
551
|
-
...(taskContent ? { taskContent } : {}),
|
|
552
|
-
artifact: path.basename(artifactPath)
|
|
535
|
+
family: localFamily
|
|
553
536
|
});
|
|
554
537
|
if (!local.ok) {
|
|
555
538
|
return failResult(
|
|
@@ -818,12 +801,11 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }: any): any {
|
|
|
818
801
|
}
|
|
819
802
|
|
|
820
803
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
821
|
-
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS, repositoryRoot));
|
|
822
804
|
const problems = [];
|
|
823
805
|
let inScopeCount = 0;
|
|
824
806
|
|
|
825
807
|
for (const row of rows) {
|
|
826
|
-
const { id, stage,
|
|
808
|
+
const { id, stage, status, evidence } = row;
|
|
827
809
|
const stageScoped = stageScope ? stageScope.includes(stage) : true;
|
|
828
810
|
// post-review-commit exemption rows are consumed by the post-review-commit
|
|
829
811
|
// check, not enforced here.
|
|
@@ -843,15 +825,6 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }: any): any {
|
|
|
843
825
|
if (status !== "open" && evidence === "") {
|
|
844
826
|
problems.push(`${id}: status '${status}' requires evidence`);
|
|
845
827
|
}
|
|
846
|
-
const round = Number.parseInt(roundRaw!, 10);
|
|
847
|
-
if (
|
|
848
|
-
Number.isFinite(round) &&
|
|
849
|
-
round >= maxRounds &&
|
|
850
|
-
!LEDGER_TERMINAL.has(status!) &&
|
|
851
|
-
status !== "needs-human-decision"
|
|
852
|
-
) {
|
|
853
|
-
problems.push(`${id}: round ${round} reached limit ${maxRounds} without convergence; escalate to needs-human-decision`);
|
|
854
|
-
}
|
|
855
828
|
if (!LEDGER_TERMINAL.has(status!)) {
|
|
856
829
|
problems.push(`${id}: unresolved (status '${status}')`);
|
|
857
830
|
}
|
|
@@ -1194,7 +1167,8 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
|
|
|
1194
1167
|
let targetHead;
|
|
1195
1168
|
let diffBase;
|
|
1196
1169
|
try {
|
|
1197
|
-
|
|
1170
|
+
const taskRepositoryRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
1171
|
+
gitRoot = reviewWorktreeForTask(taskRepositoryRoot, String(task.metadata.branch || "").trim()) ?? taskRepositoryRoot;
|
|
1198
1172
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
1199
1173
|
const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
|
|
1200
1174
|
if (!target.ok) throw new Error(target.message);
|
|
@@ -1275,6 +1249,21 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }: any): any {
|
|
|
1275
1249
|
);
|
|
1276
1250
|
}
|
|
1277
1251
|
|
|
1252
|
+
function reviewWorktreeForTask(repositoryRoot: string, branch: string): string | null {
|
|
1253
|
+
if (!branch) return null;
|
|
1254
|
+
const expectedRef = `refs/heads/${branch}`;
|
|
1255
|
+
const records = execFileSync("git", ["-C", repositoryRoot, "worktree", "list", "--porcelain"], {
|
|
1256
|
+
encoding: "utf8",
|
|
1257
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1258
|
+
}).trim().split(/\r?\n\r?\n/);
|
|
1259
|
+
for (const record of records) {
|
|
1260
|
+
const worktree = /^worktree (.+)$/m.exec(record)?.[1];
|
|
1261
|
+
const branchRef = /^branch (.+)$/m.exec(record)?.[1];
|
|
1262
|
+
if (worktree && branchRef === expectedRef) return worktree;
|
|
1263
|
+
}
|
|
1264
|
+
throw new Error(`Task branch '${branch}' is not checked out in a registered worktree`);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1278
1267
|
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }: any): any {
|
|
1279
1268
|
if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
|
|
1280
1269
|
return { ok: true, sha: lastReviewedCommit };
|
package/lib/task/verification.ts
CHANGED
|
@@ -15,7 +15,7 @@ type VerificationEvent =
|
|
|
15
15
|
| 'review-analysis.completed' | 'plan.completed' | 'review-plan.completed'
|
|
16
16
|
| 'code.completed' | 'review-code.completed' | 'manual-validation.completed' | 'validation-run.completed'
|
|
17
17
|
| 'block-task.completed' | 'cancel-task.completed' | 'commit.completed'
|
|
18
|
-
| 'complete-task.preflight' | 'complete-task.hard-preflight' | 'complete-task.completed'
|
|
18
|
+
| 'complete-task.preflight' | 'complete-task.hard-preflight' | 'complete-task.prepared' | 'complete-task.completed'
|
|
19
19
|
| 'create-pr.completed' | 'create-task.completed'
|
|
20
20
|
| 'import-codescan.completed' | 'import-dependabot.completed'
|
|
21
21
|
| 'import-issue.completed' | 'watch-pr.completed'
|
|
@@ -71,6 +71,7 @@ const VERIFICATION_CATALOG: Readonly<Record<VerificationEvent, VerificationSpec>
|
|
|
71
71
|
'commit.completed': gate('commit', 'active'),
|
|
72
72
|
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
73
73
|
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
74
|
+
'complete-task.prepared': gate('complete-task', 'active'),
|
|
74
75
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
75
76
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
76
77
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
|
|
3
3
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
4
|
+
import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
4
5
|
import {
|
|
5
6
|
WORKFLOW_WARNING_COLUMNS,
|
|
6
7
|
WORKFLOW_WARNING_HEADINGS,
|
|
@@ -137,11 +138,18 @@ function applyWorkflowWarningIntent(intent: WorkflowWarningIntent, options: Task
|
|
|
137
138
|
function projectFinalizationWarning(
|
|
138
139
|
taskRef: string,
|
|
139
140
|
warning: OperationWarning & { status: 'open' | 'resolved'; resolvedAt: string | null },
|
|
140
|
-
options: TaskWriteOptions = {}
|
|
141
|
+
options: TaskWriteOptions & { resolution?: 'passed' | 'superseded' } = {}
|
|
141
142
|
): FinalizationWarningProjectionResult {
|
|
142
143
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
143
144
|
if (!resolved.ok) return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
144
|
-
|
|
145
|
+
let prepared = resolved.state === 'completed';
|
|
146
|
+
if (!prepared && resolved.state === 'active') {
|
|
147
|
+
try { prepared = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8')).status === 'completed'; }
|
|
148
|
+
catch { prepared = false; }
|
|
149
|
+
}
|
|
150
|
+
if (!prepared) {
|
|
151
|
+
return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected a prepared or completed task` } };
|
|
152
|
+
}
|
|
145
153
|
let content: string;
|
|
146
154
|
let rows: WorkflowWarning[];
|
|
147
155
|
try {
|
|
@@ -160,22 +168,26 @@ function projectFinalizationWarning(
|
|
|
160
168
|
message: warning.message,
|
|
161
169
|
action: retryHintForWarning(warning),
|
|
162
170
|
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
163
|
-
resolution: warning.status === 'resolved'
|
|
171
|
+
resolution: warning.status === 'resolved'
|
|
172
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
173
|
+
: ''
|
|
164
174
|
}
|
|
165
175
|
: {
|
|
166
176
|
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
167
177
|
severity: warning.severity, code: warning.code, status: warning.status,
|
|
168
178
|
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
169
|
-
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
179
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
|
|
180
|
+
? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
|
|
181
|
+
: ''
|
|
170
182
|
};
|
|
171
183
|
if (existing && JSON.stringify(existing) === JSON.stringify(after)) return { status: 'no-op', changed: false, error: null };
|
|
172
184
|
const written = writeTask({
|
|
173
185
|
taskRef: resolved.taskId,
|
|
174
|
-
expectedState:
|
|
186
|
+
expectedState: resolved.state,
|
|
175
187
|
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
176
188
|
}, {
|
|
177
189
|
...options,
|
|
178
|
-
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state:
|
|
190
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
179
191
|
metadataProvider: () => metadata
|
|
180
192
|
});
|
|
181
193
|
if (written.status === 'failed') return { status: 'failed', changed: false, error: { code: written.error.code, message: written.error.message } };
|
|
@@ -42,7 +42,8 @@ function captureWorktreeTree(repoRoot: string, forcedPath: string | null): strin
|
|
|
42
42
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
|
|
43
43
|
const indexFile = path.join(tempDir, 'index');
|
|
44
44
|
try {
|
|
45
|
-
git(repoRoot, indexFile, ['read-tree', 'HEAD']);
|
|
45
|
+
try { git(repoRoot, indexFile, ['read-tree', 'HEAD']); }
|
|
46
|
+
catch { git(repoRoot, indexFile, ['read-tree', '--empty']); }
|
|
46
47
|
git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
|
|
47
48
|
if (forcedPath && fs.existsSync(path.join(repoRoot, forcedPath))) {
|
|
48
49
|
git(repoRoot, indexFile, ['add', '-f', '--', forcedPath]);
|
|
@@ -138,5 +139,5 @@ function diffWorkspaceSnapshots(repoRoot: string, before: string, after: string)
|
|
|
138
139
|
return [...changed].sort();
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
export { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots };
|
|
142
|
+
export { captureRepositorySnapshot, captureWorkspaceSnapshot, captureWorktreeTree, diffWorkspaceSnapshots };
|
|
142
143
|
export type { OrchestrationWorkspaceFingerprint, RepositorySnapshot, WorkspaceSnapshotContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
4
4
|
"description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Antigravity CLI, and OpenCode",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,19 +12,33 @@
|
|
|
12
12
|
|
|
13
13
|
`orchestration.json` records stages, model policy, child identity, and actual outcomes. Records use the current structure; trusted local operators may correct them and rerun validation. Existing task write locks and atomic writes protect updates.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Current Host
|
|
16
16
|
|
|
17
|
-
- Use native spawn, wait, and App Server results from the current host
|
|
17
|
+
- Use native spawn, wait, and App Server results from the current host. A task-bound sandbox executes ordinary task orchestration locally in the current worktree.
|
|
18
|
+
- Codex controller `open`, `verify`, and `close` remain restricted broker operations. After `open`, complete typed `verify` with the same lease proof before accepting the task, control-generation, and controller-instance binding.
|
|
18
19
|
- Prepare validates the current task, model policy, and host preflight. Start and terminal records associate observed parent/child identity; failure must not be recorded as success.
|
|
19
|
-
-
|
|
20
|
+
- Client-specific preflight, event sources, and stop-evidence validation belong to the client adapter. The common orchestrator calls only the shared capability and contains no client-ID branches.
|
|
21
|
+
|
|
22
|
+
## Activated Delegation Recovery
|
|
23
|
+
|
|
24
|
+
- Before `begin-or-resume`, the orchestrator calls internal `task-lifecycle <task> recover-started --agent <client> --auto`. This entry is not user-facing and never infers child termination from missing liveness evidence.
|
|
25
|
+
- The client adapter declares recovery support and validates trusted stop evidence for the current pending delegation. Unsupported adapters and runs without an activated pending delegation return `no-op`; uncertain termination fails closed.
|
|
26
|
+
- Recovery consumes stop evidence, saves an aborted receipt while clearing pending, then appends the Activity Log terminal row. If the run save succeeds and the log write fails, the next invocation appends only the missing row.
|
|
27
|
+
- Routing continues only after `no-op` or `applied`. Lost control responses are not reconstructed; the next operator invocation relies on the idempotent persisted state.
|
|
28
|
+
|
|
29
|
+
## Current-only Cutover and Rollback
|
|
30
|
+
|
|
31
|
+
- Enter a quiet window before cutover: let in-flight orchestration intents return, stop the wrapper/controller, and preserve the worktree, task/run/orchestration state, receipts, registration, and audit. For an unknown result, inspect local `status`/`route` before using existing recover/advance semantics; do not replay an old broker request.
|
|
32
|
+
- Deploy or roll back the CLI, broker, controller adapter, and protocol as one versioned unit. After stopping the old container/broker, rebuild and run `ai sandbox start --recreate <task-ref>` to resume with a new control generation; never reuse an old token, proof, or request.
|
|
33
|
+
- On either the new or rolled-back version, first confirm the same task through `status`/`route`, then require a successful typed controller `open`→`verify`. Keep the quiet window and evidence intact if any check fails; do not mix component versions or rewrite domain facts.
|
|
20
34
|
|
|
21
35
|
## Model Policy
|
|
22
36
|
|
|
23
37
|
- A new run persists model and reasoning effort for both roles. Explicit policy is atomic across all four role fields; only a fully absent explicit policy may fall back to the current client's `agentClients[].orchestration`.
|
|
24
38
|
- Route resolves requested model/effort by role, and prepare matches both before snapshotting. Native spawn must not inherit session defaults.
|
|
25
|
-
- Native start records host-observed actual model/effort.
|
|
39
|
+
- Native start records host-observed actual model/effort. Fields declared unobservable by the adapter follow the recording rules; each observable mismatch needs its own fallback reason, and requested values must never be fabricated as actual evidence.
|
|
26
40
|
- Model selection is labeled as a complete catalog, partial catalog, or interactive-only guidance; a local override enum must not be presented as complete.
|
|
27
|
-
-
|
|
41
|
+
- An adapter must declare any policy field it cannot dispatch per role. Values observed in host events are actual evidence only; they do not promise dispatch or gate activation.
|
|
28
42
|
|
|
29
43
|
## Stable Pause Conditions
|
|
30
44
|
|
|
@@ -12,19 +12,33 @@
|
|
|
12
12
|
|
|
13
13
|
`orchestration.json` 记录阶段、模型策略、child 身份及实际执行结果。记录必须符合当前结构;本地操作者可直接修正后重新校验。已有任务写锁与原子写入继续保护写操作。
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## 当前宿主
|
|
16
16
|
|
|
17
|
-
- 使用当前宿主的原生 spawn、wait 与 App Server
|
|
17
|
+
- 使用当前宿主的原生 spawn、wait 与 App Server 结果;task-bound sandbox 在当前 worktree 本地执行普通任务编排。
|
|
18
|
+
- Codex controller 的 `open`、`verify`、`close` 继续通过受限 broker 执行。`open` 后必须用同一 lease proof 完成 typed `verify`,再接受 task、control generation 和 controller instance 绑定。
|
|
18
19
|
- prepare 校验当前任务、模型策略与宿主 preflight。启动和完成记录关联实际 parent/child 身份,失败不得写成成功。
|
|
19
|
-
-
|
|
20
|
+
- 客户端特有的 preflight、事件来源与停止证据校验由客户端适配器实现;公共总控只调用统一能力。不得在公共流程中新增客户端 ID 分支。
|
|
21
|
+
|
|
22
|
+
## Activated delegation 自动恢复
|
|
23
|
+
|
|
24
|
+
- 总控在 `begin-or-resume` 前调用内部 `task-lifecycle <task> recover-started --agent <client> --auto`。该入口不公开给用户,也不从缺少存活证据推断 child 已终止。
|
|
25
|
+
- 客户端适配器决定是否支持恢复,并验证当前 pending delegation 的可信停止证据;不支持或没有 activated pending 时返回 `no-op`,无法确认停止时失败关闭。
|
|
26
|
+
- 恢复按固定顺序消费停止证据、保存 aborted receipt 并清除 pending,再写 Activity Log 结束记录。任务状态已保存而日志未写时,下次调用只补日志。
|
|
27
|
+
- 只有 `no-op` 或 `applied` 才能继续 route。控制响应丢失时不重建恢复结果;下一次人为调用依靠幂等状态继续。
|
|
28
|
+
|
|
29
|
+
## Current-only 切换与回滚
|
|
30
|
+
|
|
31
|
+
- 切换前进入静默窗口:等待在途编排 intent 返回,停止 wrapper/controller,并保留 worktree、task/run/orchestration 状态、receipts、registration 与 audit。结果未知时先用本地 `status`/`route` 判断 domain 是否已提交,再按现有 recover/advance 语义处理;旧 broker request 不重放。
|
|
32
|
+
- CLI、broker、controller adapter 和协议必须作为同一版本单元部署或回退。停止旧 container/broker 后执行 rebuild 与 `ai sandbox start --recreate <task-ref>`,以新 control generation 恢复执行;旧 token、proof 和 request 不得复用。
|
|
33
|
+
- 新版本或回退版本都必须先核对同一任务的 `status`/`route`,再要求 controller typed `open`→`verify` 成功。任一检查失败时继续保持静默并保留证据,不局部混用组件或改写 domain 事实。
|
|
20
34
|
|
|
21
35
|
## 模型策略
|
|
22
36
|
|
|
23
37
|
- 新 run 必须固化 executor/reviewer 各自的 model 与 reasoning effort;显式策略必须四字段原子完整,完全没有显式字段时才读取当前 client 的 `agentClients[].orchestration`。重入不得静默改写策略。
|
|
24
38
|
- route 按 role 返回 requested model/effort,prepare 必须在工作区快照前精确匹配两者;原生 spawn 不能继承会话默认值。
|
|
25
|
-
- 原生 start 必须记录宿主观察到的 actual model/effort
|
|
39
|
+
- 原生 start 必须记录宿主观察到的 actual model/effort。适配器声明无法观察的字段按记录规则处理;任一可观察字段与 requested 不同时必须记录独立 fallback reason,requested 值不得补造 actual 证据。
|
|
26
40
|
- 模型选择能力必须标记 complete catalog、partial catalog 或 interactive-only guidance;局部 override 枚举不得冒充完整目录。
|
|
27
|
-
-
|
|
41
|
+
- 客户端无法按角色下发的策略字段必须由适配器明确声明;宿主事件中观察到的值只作为实际证据,不构成下发承诺或放行门禁。
|
|
28
42
|
|
|
29
43
|
## 稳定暂停条件
|
|
30
44
|
|
|
@@ -29,11 +29,11 @@ After the executor gives `adjusted` / `refuted` / `cannot-judge`, the reviewer m
|
|
|
29
29
|
- **Hold with new evidence** → set back to `open` (with new evidence, returned to the executor).
|
|
30
30
|
- **Escalate to human** → set to `needs-human-decision`.
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Evidence-driven rework convergence
|
|
33
33
|
|
|
34
|
-
- The
|
|
35
|
-
-
|
|
36
|
-
-
|
|
34
|
+
- The first Changes Requested verdict permits an ordinary revision. After one revision-review cycle, another Changes Requested verdict must classify the problem as scope or requirements, design, implementation, human decision, or insufficient evidence before another executor artifact is created.
|
|
35
|
+
- Classification binds the finding, source review SHA, and stable task-fact digest. Artifact names, rounds, and retry counts are not evidence by themselves.
|
|
36
|
+
- Human choice and insufficient evidence pause stably; implementation can continue when new decisive evidence exists. Retry count alone never forces a human escalation.
|
|
37
37
|
|
|
38
38
|
## Same-model convergence-bias mitigation (documentation-level discipline)
|
|
39
39
|
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
- **补充新证据后坚持** → 账本置回 `open`(带新证据,回到执行方)。
|
|
30
30
|
- **升级人工裁决** → 账本置 `needs-human-decision`。
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## 证据驱动的返工收敛
|
|
33
33
|
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
34
|
+
- 首次 Changes Requested 允许正常修订。完成一次修订—复审后再次收到 Changes Requested 时,必须在下一执行产物前把问题分类为范围或需求、方案、实现、人工裁决或证据不足。
|
|
35
|
+
- 分类必须绑定 finding、来源审查 SHA 和稳定任务事实摘要。文件名、artifact round 或返工次数本身不构成新证据。
|
|
36
|
+
- 人工选择或证据不足进入稳定暂停;具有新决定性证据的实现缺陷可以继续修订。不得仅因次数达到阈值强制升级人工裁决。
|
|
37
37
|
|
|
38
38
|
## 同源模型收敛偏差缓解(文档级纪律)
|
|
39
39
|
|
|
@@ -13,6 +13,10 @@ If the entry operands contain `--orchestrated`, bind `{execution-flag}` to `--or
|
|
|
13
13
|
|
|
14
14
|
## Boundary / Critical Rules
|
|
15
15
|
|
|
16
|
+
### Flow decision
|
|
17
|
+
|
|
18
|
+
The analysis artifact must record the selected path, factual basis, unmet higher-path conditions, and factual upgrade triggers in `## Flow Decision`. The path is streamlined, standard, or full; file count, module count, and speculative risk cannot upgrade it by themselves.
|
|
19
|
+
|
|
16
20
|
### Persisted Report Evidence
|
|
17
21
|
|
|
18
22
|
Before generating the analysis report, read `.agents/rules/evidence-reporting.md`. Record the command, target scope, status or structured result, actual result, and uncovered parts for state checks and successful checks; include decisive raw excerpts only for failures, blocking conditions, or disputes.
|
|
@@ -61,7 +65,7 @@ If `task.md` is missing, tell the user to create or import the task first.
|
|
|
61
65
|
|
|
62
66
|
### 2. Resolve the Analysis Context
|
|
63
67
|
|
|
64
|
-
Run `agent-infra-internal task-artifact {task-id} inspect --family analysis`. Continue only for `ready
|
|
68
|
+
Run `agent-infra-internal task-artifact {task-id} inspect --family analysis`. Continue only for `ready`. When `selection.disposition` is `reuse`, reuse `selection.artifact`, do not run started/init or write a new artifact, and continue directly to completion verification and next-step guidance. Otherwise take `{analysis-round}` / `{analysis-artifact}` from `next.round` / `next.name` and revision inputs from `inputs`. Do not scan rounds or construct names in the skill.
|
|
65
69
|
|
|
66
70
|
### 3. Read Task Context
|
|
67
71
|
|
|
@@ -261,7 +265,10 @@ Keep the gate output in your reply as fresh evidence. Do not claim completion wi
|
|
|
261
265
|
> Before rendering next steps, read `.agents/rules/next-step-output.md`, invoke the shared helper only for the selected scenario, and insert its stdout at `{next-step-commands}`.
|
|
262
266
|
|
|
263
267
|
Output format:
|
|
264
|
-
|
|
268
|
+
Select one command from the canonical path to populate `{next-step-commands}`:
|
|
269
|
+
- Streamlined: `agent-infra-internal agent-client next-steps --skill code-task --task-ref {task-ref}`
|
|
270
|
+
- Standard: `agent-infra-internal agent-client next-steps --skill plan-task --task-ref {task-ref}`
|
|
271
|
+
- Full: `agent-infra-internal agent-client next-steps --skill review-analysis --task-ref {task-ref}`
|
|
265
272
|
|
|
266
273
|
```
|
|
267
274
|
Analysis complete for task {task-id}.
|
|
@@ -274,7 +281,7 @@ Summary:
|
|
|
274
281
|
Output file:
|
|
275
282
|
- Analysis report: .agents/workspace/active/{task-id}/{analysis-artifact}
|
|
276
283
|
|
|
277
|
-
Next step -
|
|
284
|
+
Next step - continue on the selected path:
|
|
278
285
|
{next-step-commands}
|
|
279
286
|
```
|
|
280
287
|
|
|
@@ -292,7 +299,7 @@ Next step - review the analysis:
|
|
|
292
299
|
|
|
293
300
|
## STOP
|
|
294
301
|
|
|
295
|
-
After completing the checklist, **stop immediately**. Wait for the user to
|
|
302
|
+
After completing the checklist, **stop immediately**. Wait for the user to invoke the next stage selected above.
|
|
296
303
|
|
|
297
304
|
## Notes
|
|
298
305
|
|
|
@@ -13,6 +13,10 @@ description: >
|
|
|
13
13
|
|
|
14
14
|
## 行为边界 / 关键规则
|
|
15
15
|
|
|
16
|
+
### 流程裁定
|
|
17
|
+
|
|
18
|
+
分析产物必须在 `## 流程裁定` 中记录本任务路径、判定依据、未满足的更高路径条件和事实型升级触发条件。路径仅可为精简、标准或完整;文件数、模块数和推测性风险不能单独升级路径。
|
|
19
|
+
|
|
16
20
|
### 持久化报告证据
|
|
17
21
|
|
|
18
22
|
生成分析报告时,先读取 `.agents/rules/evidence-reporting.md`。状态核对和成功检查记录命令、目标范围、状态/结构化结果、实际结果和未覆盖部分;失败、阻塞或争议才附决定性原文摘录。
|
|
@@ -64,7 +68,7 @@ agent-infra-internal task-event {task-id} analyze.started --agent {standard-agen
|
|
|
64
68
|
|
|
65
69
|
### 2. 解析分析上下文
|
|
66
70
|
|
|
67
|
-
运行 `agent-infra-internal task-artifact {task-id} inspect --family analysis`。仅当结果为 `ready`
|
|
71
|
+
运行 `agent-infra-internal task-artifact {task-id} inspect --family analysis`。仅当结果为 `ready` 时继续。若 `selection.disposition` 为 `reuse`,复用 `selection.artifact`,不得执行 started、init 或写入新产物,并直接进入完成校验与下一步提示。其他状态从 `next.round` / `next.name` 记录 `{analysis-round}` / `{analysis-artifact}`,从 `inputs` 读取修订上下文;不得自行扫描轮次或拼装文件名。随后执行 started 事件,并以事件返回的 `artifactContext` 复核同一身份。
|
|
68
72
|
|
|
69
73
|
### 3. 阅读任务上下文
|
|
70
74
|
|
|
@@ -261,7 +265,10 @@ agent-infra-internal task-verify {task-id} analyze.completed --artifact {analysi
|
|
|
261
265
|
> 渲染下一步前先读取 `.agents/rules/next-step-output.md`,仅为已选场景调用统一 helper,并将 stdout 填入 `{next-step-commands}`。
|
|
262
266
|
|
|
263
267
|
输出格式:
|
|
264
|
-
|
|
268
|
+
按分析中的规范路径选择一条命令生成 `{next-step-commands}`:
|
|
269
|
+
- 精简路径:`agent-infra-internal agent-client next-steps --skill code-task --task-ref {task-ref}`
|
|
270
|
+
- 标准路径:`agent-infra-internal agent-client next-steps --skill plan-task --task-ref {task-ref}`
|
|
271
|
+
- 完整路径:`agent-infra-internal agent-client next-steps --skill review-analysis --task-ref {task-ref}`
|
|
265
272
|
|
|
266
273
|
```
|
|
267
274
|
任务 {task-id} 分析完成。
|
|
@@ -274,7 +281,7 @@ agent-infra-internal task-verify {task-id} analyze.completed --artifact {analysi
|
|
|
274
281
|
产出文件:
|
|
275
282
|
- 分析报告:.agents/workspace/active/{task-id}/{analysis-artifact}
|
|
276
283
|
|
|
277
|
-
下一步 -
|
|
284
|
+
下一步 - 按所选路径继续:
|
|
278
285
|
{next-step-commands}
|
|
279
286
|
```
|
|
280
287
|
|
|
@@ -292,7 +299,7 @@ agent-infra-internal task-verify {task-id} analyze.completed --artifact {analysi
|
|
|
292
299
|
|
|
293
300
|
## 停止
|
|
294
301
|
|
|
295
|
-
|
|
302
|
+
完成检查清单后,**立即停止**。等待用户调用上述所选路径的下一阶段。
|
|
296
303
|
|
|
297
304
|
## 注意事项
|
|
298
305
|
|