@fitlab-ai/agent-infra 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +6 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +58 -21
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +56 -67
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +2 -1
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-orchestration.js +156 -291
- package/dist/lib/platform/pr-summary.js +48 -10
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +137 -5
- package/dist/lib/sandbox/control/lifecycle.js +3 -1
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/delegation-receipts.js +243 -2
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +187 -761
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +113 -60
- package/dist/lib/task/verification.js +2 -1
- package/dist/lib/task/workflow-warning-intents.js +50 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +83 -91
- package/lib/git/workflow.ts +11 -2
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +2 -1
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-orchestration.ts +20 -103
- package/lib/platform/pr-summary.ts +55 -11
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +163 -5
- package/lib/sandbox/control/lifecycle.ts +2 -1
- package/lib/sandbox/control/protocol.ts +80 -7
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +3 -3
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/delegation-receipts.ts +258 -1
- package/lib/task/finalization.ts +719 -0
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +217 -982
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +118 -63
- package/lib/task/verification.ts +3 -2
- package/lib/task/workflow-warning-intents.ts +57 -2
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
package/lib/task/short-id.ts
CHANGED
|
@@ -201,6 +201,10 @@ type RegistrySchema = {
|
|
|
201
201
|
ids: Record<string, string>;
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
+
type ShortIdRegistryInspection =
|
|
205
|
+
| { status: 'valid'; shortIds: Map<string, string> }
|
|
206
|
+
| { status: 'missing' | 'invalid'; error: { code: string; message: string } };
|
|
207
|
+
|
|
204
208
|
type ShortIdMutationEffect = 'alloc' | 'release' | 'none';
|
|
205
209
|
type ShortIdMutationResult = {
|
|
206
210
|
effect: 'allocated' | 'released' | 'unchanged';
|
|
@@ -446,6 +450,64 @@ function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
|
|
|
446
450
|
return map;
|
|
447
451
|
}
|
|
448
452
|
|
|
453
|
+
function inspectShortIdRegistry(repoRoot: string): ShortIdRegistryInspection {
|
|
454
|
+
const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
|
|
455
|
+
if (!fs.existsSync(registryPath)) {
|
|
456
|
+
return {
|
|
457
|
+
status: 'missing',
|
|
458
|
+
error: {
|
|
459
|
+
code: 'SHORT_ID_REGISTRY_NOT_FOUND',
|
|
460
|
+
message: 'canonical short-id registry is missing'
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
let raw: string;
|
|
466
|
+
try {
|
|
467
|
+
raw = fs.readFileSync(registryPath, 'utf8');
|
|
468
|
+
} catch {
|
|
469
|
+
return {
|
|
470
|
+
status: 'invalid',
|
|
471
|
+
error: {
|
|
472
|
+
code: 'SHORT_ID_REGISTRY_READ_FAILED',
|
|
473
|
+
message: 'canonical short-id registry is unreadable'
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
let parsed: unknown;
|
|
479
|
+
try {
|
|
480
|
+
parsed = JSON.parse(raw);
|
|
481
|
+
} catch {
|
|
482
|
+
return {
|
|
483
|
+
status: 'invalid',
|
|
484
|
+
error: {
|
|
485
|
+
code: 'SHORT_ID_REGISTRY_INVALID_JSON',
|
|
486
|
+
message: 'canonical short-id registry contains invalid JSON'
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
|
|
493
|
+
const shortIds = new Map<string, string>();
|
|
494
|
+
for (const [key, taskId] of Object.entries(registry.ids)) shortIds.set(taskId, key);
|
|
495
|
+
return { status: 'valid', shortIds };
|
|
496
|
+
} catch (error) {
|
|
497
|
+
return {
|
|
498
|
+
status: 'invalid',
|
|
499
|
+
error: {
|
|
500
|
+
code: typeof (error as { code?: unknown }).code === 'string'
|
|
501
|
+
? (error as { code: string }).code
|
|
502
|
+
: 'SHORT_ID_REGISTRY_INVALID_SCHEMA',
|
|
503
|
+
message: (error as { code?: unknown }).code === 'SHORT_ID_REGISTRY_DUPLICATE_TASK'
|
|
504
|
+
? 'canonical short-id registry contains duplicate task entries'
|
|
505
|
+
: 'canonical short-id registry has invalid schema'
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
449
511
|
/**
|
|
450
512
|
* Resolve a branch to its active-task short id (`NN`), or `null` when no
|
|
451
513
|
* active task is bound to that branch.
|
|
@@ -485,6 +547,7 @@ export {
|
|
|
485
547
|
resolveShortIdReadOnly,
|
|
486
548
|
lookupShortIdByBranch,
|
|
487
549
|
loadShortIdByTaskId,
|
|
550
|
+
inspectShortIdRegistry,
|
|
488
551
|
configuredShortIdLength,
|
|
489
552
|
mutateShortIdRegistry,
|
|
490
553
|
executeShortIdCommand
|
|
@@ -497,5 +560,6 @@ export type {
|
|
|
497
560
|
ShortIdMutationEffect,
|
|
498
561
|
ShortIdMutationResult,
|
|
499
562
|
ShortIdCommandRequest,
|
|
500
|
-
ShortIdCommandResult
|
|
563
|
+
ShortIdCommandResult,
|
|
564
|
+
ShortIdRegistryInspection
|
|
501
565
|
};
|
|
@@ -272,6 +272,7 @@ function withTaskExecutionLock<T>(
|
|
|
272
272
|
return callback();
|
|
273
273
|
} catch (error) {
|
|
274
274
|
if (error instanceof TaskExecutionLockError) throw error;
|
|
275
|
+
if (error instanceof Error && error.name === 'OrchestrationStateError') throw error;
|
|
275
276
|
throw new TaskExecutionLockError(
|
|
276
277
|
'ORCHESTRATION_LOCK_FAILED',
|
|
277
278
|
'lifecycle task lock operation failed',
|
|
@@ -294,5 +295,13 @@ function withTaskExecutionLock<T>(
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
297
|
-
|
|
298
|
+
function withRepositoryMutationLock<T>(
|
|
299
|
+
repoRoot: string,
|
|
300
|
+
callback: () => T,
|
|
301
|
+
options: TaskExecutionLockOptions = {}
|
|
302
|
+
): T {
|
|
303
|
+
return withTaskExecutionLock(repoRoot, '__repository_mutation__', 'repository-mutation', callback, options);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export { TaskExecutionLockError, lockKey, mapLinkError, withRepositoryMutationLock, withTaskExecutionLock };
|
|
298
307
|
export type { LinkDisposition, LinkOperation, TaskExecutionLockErrorCode, TaskExecutionLockOptions, TaskExecutionLockOwner };
|
|
@@ -27,6 +27,7 @@ import type { LedgerRow } from "./ledger.ts";
|
|
|
27
27
|
import { equalCounts, parseReviewSummary } from "./review-artifacts.ts";
|
|
28
28
|
import { loadVerificationConfig } from "./verification-config.ts";
|
|
29
29
|
import { snapshotReview } from "../git/review-snapshot.ts";
|
|
30
|
+
import { OrchestrationStateError, readRun } from "./orchestration.ts";
|
|
30
31
|
|
|
31
32
|
const TASK_ENUMS = {
|
|
32
33
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
@@ -123,6 +124,8 @@ function runCheck(type: any, context: any, shared: any): any {
|
|
|
123
124
|
return checkReviewFact(context);
|
|
124
125
|
case "post-review-commit":
|
|
125
126
|
return checkPostReviewCommit(context);
|
|
127
|
+
case "required-pr-delivery":
|
|
128
|
+
return checkRequiredPrDelivery(context);
|
|
126
129
|
case "orchestration-state":
|
|
127
130
|
return checkOrchestrationState(context);
|
|
128
131
|
case "orchestration-evidence":
|
|
@@ -142,40 +145,19 @@ function runCheck(type: any, context: any, shared: any): any {
|
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
function isFailClosedLegacyPause(run: any): boolean {
|
|
146
|
-
if (
|
|
147
|
-
run.schemaVersion !== 1
|
|
148
|
-
|| run.status !== 'paused'
|
|
149
|
-
|| run.pause?.recoverable !== false
|
|
150
|
-
|| !exactText(run.pause?.message)
|
|
151
|
-
|| !Array.isArray(run.receipts)
|
|
152
|
-
) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
if (run.pause.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED') {
|
|
156
|
-
return run.receipts.length > 0 && run.pendingDelegation === null;
|
|
157
|
-
}
|
|
158
|
-
if (run.pause.code === 'ORCHESTRATION_DELEGATION_BUSY') {
|
|
159
|
-
return run.pendingDelegation != null && run.pendingDelegation.status !== 'sealed';
|
|
160
|
-
}
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
148
|
function checkOrchestrationState({ taskDir }: any): any {
|
|
165
149
|
const file = path.join(taskDir, 'orchestration.json');
|
|
166
150
|
const stat = safeStat(file);
|
|
167
151
|
if (!stat?.isFile()) return failResult('orchestration-state', 'orchestration.json is missing');
|
|
168
152
|
let run: any;
|
|
169
153
|
try {
|
|
170
|
-
run =
|
|
154
|
+
run = readRun(taskDir);
|
|
171
155
|
} catch (error) {
|
|
172
|
-
|
|
156
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
157
|
+
return failResult('orchestration-state', `Invalid orchestration.json: ${message}`);
|
|
173
158
|
}
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
if (![2, 3].includes(run.schemaVersion) || !['paused', 'completed'].includes(run.status)) {
|
|
178
|
-
return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
|
|
159
|
+
if (!run || !['paused', 'completed'].includes(run.status)) {
|
|
160
|
+
return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
|
|
179
161
|
}
|
|
180
162
|
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
181
163
|
return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
|
|
@@ -242,12 +224,10 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
242
224
|
if (!stat?.isFile()) return failResult('orchestration-evidence', 'orchestration.json is missing');
|
|
243
225
|
let run: any;
|
|
244
226
|
try {
|
|
245
|
-
run =
|
|
227
|
+
run = readRun(taskDir);
|
|
246
228
|
} catch (error) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (isFailClosedLegacyPause(run)) {
|
|
250
|
-
return passResult('orchestration-evidence', 'Legacy orchestration evidence is preserved in a fail-closed pause');
|
|
229
|
+
const message = error instanceof OrchestrationStateError ? error.message : String(error);
|
|
230
|
+
return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
|
|
251
231
|
}
|
|
252
232
|
const policy = run.modelPolicy;
|
|
253
233
|
if (
|
|
@@ -269,30 +249,8 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
269
249
|
return failResult('orchestration-evidence', 'Run model policy source or recovery history is invalid');
|
|
270
250
|
}
|
|
271
251
|
for (const recovery of run.recoveryHistory) {
|
|
272
|
-
const validModelRecovery = recovery.code === 'MODEL_POLICY_SUPPLEMENTED'
|
|
273
|
-
&& recovery.previousSchemaVersion === 1
|
|
274
|
-
&& recovery.receiptCount === 0
|
|
275
|
-
&& recovery.pendingDelegation === false
|
|
276
|
-
&& exactText(recovery.recoveredAt)
|
|
277
|
-
&& recovery.policySource;
|
|
278
252
|
const guards = recovery.guards;
|
|
279
|
-
const
|
|
280
|
-
&& recovery.previousSchemaVersion === 2
|
|
281
|
-
&& recovery.previousStatus === 'paused'
|
|
282
|
-
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
283
|
-
&& recovery.client === 'codex'
|
|
284
|
-
&& recovery.resultingStatus === 'running'
|
|
285
|
-
&& exactText(recovery.recoveredAt)
|
|
286
|
-
&& guards?.stepCount === 0
|
|
287
|
-
&& guards?.nextStage === null
|
|
288
|
-
&& guards?.baselineEmpty === true
|
|
289
|
-
&& guards?.receiptCount === 0
|
|
290
|
-
&& guards?.pendingDelegation === false
|
|
291
|
-
&& guards?.commitAuthorizationUnused === true
|
|
292
|
-
&& guards?.completionEvidenceAbsent === true
|
|
293
|
-
&& guards?.commitIntentAbsent === true;
|
|
294
|
-
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION'
|
|
295
|
-
&& recovery.previousSchemaVersion === 3
|
|
253
|
+
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED'
|
|
296
254
|
&& recovery.previousStatus === 'paused'
|
|
297
255
|
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
298
256
|
&& recovery.client === 'claude-code'
|
|
@@ -304,9 +262,8 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
304
262
|
&& guards?.receiptCount === 0
|
|
305
263
|
&& guards?.pendingDelegation === false
|
|
306
264
|
&& guards?.commitAuthorizationUnused === true
|
|
307
|
-
&& guards?.completionEvidenceAbsent === true
|
|
308
|
-
|
|
309
|
-
if (!validModelRecovery && !validCapabilityRecovery && !validClaudeCodeRecovery) {
|
|
265
|
+
&& guards?.completionEvidenceAbsent === true;
|
|
266
|
+
if (!validClaudeCodeRecovery) {
|
|
310
267
|
return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
|
|
311
268
|
}
|
|
312
269
|
}
|
|
@@ -376,12 +333,37 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
376
333
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
|
|
377
334
|
}
|
|
378
335
|
if (receipt.client === 'codex') {
|
|
336
|
+
const activatedCodex = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
379
337
|
const host = receipt.hostEvidence;
|
|
338
|
+
const provenance = receipt.lifecycleProvenance;
|
|
380
339
|
if (
|
|
381
|
-
|
|
382
|
-
||
|
|
383
|
-
|
|
384
|
-
|
|
340
|
+
!provenance
|
|
341
|
+
|| (activatedCodex && (
|
|
342
|
+
host?.kind !== 'codex-lifecycle-v2'
|
|
343
|
+
|| host.protocolVersion !== provenance.protocolVersion
|
|
344
|
+
|| host.packageVersion !== provenance.packageVersion
|
|
345
|
+
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
346
|
+
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
347
|
+
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
348
|
+
|| host.hookSource !== provenance.hookSource
|
|
349
|
+
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
350
|
+
|| host.hookSourceHash !== provenance.hookSourceHash
|
|
351
|
+
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
352
|
+
|| receipt.parentId !== provenance.capabilitySessionId
|
|
353
|
+
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
354
|
+
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
355
|
+
|| host.controlGeneration !== provenance.controlGeneration
|
|
356
|
+
|| host.spawnToolUseId === provenance.capabilityToolUseId
|
|
357
|
+
|| !exactText(host.spawnToolUseId)
|
|
358
|
+
|| !Number.isFinite(Date.parse(host.spawnObservedAt ?? ''))
|
|
359
|
+
|| !Number.isFinite(Date.parse(receipt.spawnDispatchedAt ?? ''))
|
|
360
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt ?? ''))
|
|
361
|
+
|| Date.parse(host.spawnObservedAt ?? '') < Date.parse(receipt.spawnDispatchedAt ?? '')
|
|
362
|
+
|| Date.parse(host.spawnObservedAt ?? '') > Date.parse(receipt.activationDeadlineAt ?? '')
|
|
363
|
+
|| !exactText(host.hookDefinitionHash)
|
|
364
|
+
|| !Number.isSafeInteger(host.startRevision)
|
|
365
|
+
|| host.startRevision < 1
|
|
366
|
+
))
|
|
385
367
|
) {
|
|
386
368
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid Codex start evidence`);
|
|
387
369
|
}
|
|
@@ -510,6 +492,63 @@ function checkTaskMeta({ taskDir, config }: any): any {
|
|
|
510
492
|
);
|
|
511
493
|
}
|
|
512
494
|
|
|
495
|
+
function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }: any): any {
|
|
496
|
+
const task = loadTask(taskDir);
|
|
497
|
+
if (!task.ok) return failResult('required-pr-delivery', task.message);
|
|
498
|
+
let projectConfig: any = {};
|
|
499
|
+
try {
|
|
500
|
+
const file = path.join(repositoryRoot, '.agents', '.airc.json');
|
|
501
|
+
if (safeStat(file)?.isFile()) projectConfig = JSON.parse(fs.readFileSync(file, 'utf8')) as any;
|
|
502
|
+
} catch {
|
|
503
|
+
return failResult('required-pr-delivery', 'Project configuration is invalid');
|
|
504
|
+
}
|
|
505
|
+
const flow = projectConfig.prFlow;
|
|
506
|
+
const status = String(task.metadata.pr_status || 'pending');
|
|
507
|
+
const prNumber = parsePrNumber(task.metadata.pr_number);
|
|
508
|
+
if (flow === 'disabled') return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
|
|
509
|
+
if (flow === 'required' && (status !== 'created' || prNumber === null)) {
|
|
510
|
+
return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
|
|
511
|
+
}
|
|
512
|
+
if (flow !== 'required' && status === 'pending') {
|
|
513
|
+
return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
|
|
514
|
+
}
|
|
515
|
+
if (status === 'created' && prNumber === null) {
|
|
516
|
+
return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
|
|
517
|
+
}
|
|
518
|
+
if (flow === 'required') {
|
|
519
|
+
if (mode === 'gate') {
|
|
520
|
+
return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
|
|
521
|
+
}
|
|
522
|
+
const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
|
|
523
|
+
if (inspected.status === 'blocked') {
|
|
524
|
+
return blockedResult(
|
|
525
|
+
'required-pr-delivery',
|
|
526
|
+
inspected.error?.message || 'Unable to inspect the required pull request',
|
|
527
|
+
inspected.error?.code || 'PR_INSPECTION_BLOCKED'
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
const pullRequest = inspected.pullRequest;
|
|
531
|
+
const repository = inspected.platform.repository?.toLowerCase() || '';
|
|
532
|
+
const taskBranch = String(task.metadata.branch || '').trim();
|
|
533
|
+
if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
|
|
534
|
+
return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
|
|
535
|
+
}
|
|
536
|
+
if (
|
|
537
|
+
!repository
|
|
538
|
+
|| pullRequest.repository.toLowerCase() !== repository
|
|
539
|
+
|| pullRequest.base.repository.toLowerCase() !== repository
|
|
540
|
+
|| (taskBranch && pullRequest.head.ref !== taskBranch)
|
|
541
|
+
) {
|
|
542
|
+
return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
|
|
543
|
+
}
|
|
544
|
+
if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
|
|
545
|
+
return failResult('required-pr-delivery', 'Project policy requires the bound pull request to be merged');
|
|
546
|
+
}
|
|
547
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR #${prNumber})`);
|
|
548
|
+
}
|
|
549
|
+
return passResult('required-pr-delivery', `Pull request delivery policy satisfied (${status})`);
|
|
550
|
+
}
|
|
551
|
+
|
|
513
552
|
function validateTaskBranch(metadata: any): any {
|
|
514
553
|
if (isBlank(metadata.branch)) {
|
|
515
554
|
return null;
|
|
@@ -1600,7 +1639,14 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1600
1639
|
const checks = [];
|
|
1601
1640
|
for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
|
|
1602
1641
|
if (checkConfig === null) continue;
|
|
1603
|
-
const result = runCheck(type, {
|
|
1642
|
+
const result = runCheck(type, {
|
|
1643
|
+
mode,
|
|
1644
|
+
skillName,
|
|
1645
|
+
taskDir: path.resolve(taskDir),
|
|
1646
|
+
artifactFile,
|
|
1647
|
+
repositoryRoot: repoRoot,
|
|
1648
|
+
config: checkConfig
|
|
1649
|
+
}, shared);
|
|
1604
1650
|
checks.push(result);
|
|
1605
1651
|
if (result.status === "blocked") break;
|
|
1606
1652
|
}
|
|
@@ -1611,7 +1657,16 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
|
|
|
1611
1657
|
const config = (verifyConfig.checks || {})[type];
|
|
1612
1658
|
if (config === undefined) return { skill: skillName, ...failResult(type, `Unknown check type '${type}' for skill '${skillName}'.`) };
|
|
1613
1659
|
if (config === null) return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
|
|
1614
|
-
return {
|
|
1660
|
+
return {
|
|
1661
|
+
skill: skillName,
|
|
1662
|
+
...runCheck(type, {
|
|
1663
|
+
skillName,
|
|
1664
|
+
taskDir: path.resolve(taskDir),
|
|
1665
|
+
artifactFile,
|
|
1666
|
+
repositoryRoot: repoRoot,
|
|
1667
|
+
config
|
|
1668
|
+
}, shared)
|
|
1669
|
+
};
|
|
1615
1670
|
}
|
|
1616
1671
|
|
|
1617
1672
|
export { verifyInProcess };
|
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.completed'
|
|
18
|
+
| 'complete-task.preflight' | 'complete-task.hard-preflight' | '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'
|
|
@@ -69,7 +69,8 @@ const VERIFICATION_CATALOG: Readonly<Record<VerificationEvent, VerificationSpec>
|
|
|
69
69
|
'block-task.completed': gate('block-task', 'blocked'),
|
|
70
70
|
'cancel-task.completed': gate('cancel-task', 'completed'),
|
|
71
71
|
'commit.completed': gate('commit', 'active'),
|
|
72
|
-
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['
|
|
72
|
+
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
73
|
+
'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
|
|
73
74
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
74
75
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
75
76
|
'create-task.completed': gate('create-task', 'active'),
|
|
@@ -12,6 +12,7 @@ import type { WorkflowWarning } from './workflow-warnings.ts';
|
|
|
12
12
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
13
13
|
import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
|
|
14
14
|
import { allowsManualOverride } from './guard-override.ts';
|
|
15
|
+
import { retryHintForWarning, type OperationWarning } from './operation-outcome.ts';
|
|
15
16
|
|
|
16
17
|
type WarningSeverity = 'IMPORTANT' | 'ACTION_REQUIRED';
|
|
17
18
|
type WarningStatus = 'open' | 'resolved' | 'ignored';
|
|
@@ -28,6 +29,12 @@ type WorkflowWarningIntentResult = {
|
|
|
28
29
|
error: { code: string; message: string } | null;
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
type FinalizationWarningProjectionResult = Readonly<{
|
|
33
|
+
status: 'applied' | 'no-op' | 'failed';
|
|
34
|
+
changed: boolean;
|
|
35
|
+
error: { code: string; message: string } | null;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
31
38
|
function failed(intent: WorkflowWarningIntent, code: string, message: string, taskId: string | null = null, entityId: string | null = null): WorkflowWarningIntentResult {
|
|
32
39
|
return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
|
|
33
40
|
}
|
|
@@ -140,5 +147,53 @@ function applyWorkflowWarningIntent(intent: WorkflowWarningIntent, options: Task
|
|
|
140
147
|
};
|
|
141
148
|
}
|
|
142
149
|
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
function projectFinalizationWarning(
|
|
151
|
+
taskRef: string,
|
|
152
|
+
warning: OperationWarning & { status: 'open' | 'resolved'; resolvedAt: string | null },
|
|
153
|
+
options: TaskWriteOptions = {}
|
|
154
|
+
): FinalizationWarningProjectionResult {
|
|
155
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
156
|
+
if (!resolved.ok) return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
|
|
157
|
+
if (resolved.state !== 'completed') return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected completed` } };
|
|
158
|
+
let content: string;
|
|
159
|
+
let rows: WorkflowWarning[];
|
|
160
|
+
try {
|
|
161
|
+
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
162
|
+
rows = parseWorkflowWarnings(content);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return { status: 'failed', changed: false, error: { code: 'WARNING_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
165
|
+
}
|
|
166
|
+
const metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
167
|
+
const existing = rows.find((row) => row.step === warning.step && row.code === warning.code && row.target === warning.target);
|
|
168
|
+
const after: WorkflowWarning = existing
|
|
169
|
+
? {
|
|
170
|
+
...existing,
|
|
171
|
+
severity: warning.severity,
|
|
172
|
+
status: warning.status,
|
|
173
|
+
message: warning.message,
|
|
174
|
+
action: retryHintForWarning(warning),
|
|
175
|
+
resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
|
|
176
|
+
resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
177
|
+
}
|
|
178
|
+
: {
|
|
179
|
+
id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
|
|
180
|
+
severity: warning.severity, code: warning.code, status: warning.status,
|
|
181
|
+
target: warning.target, message: warning.message, action: retryHintForWarning(warning),
|
|
182
|
+
resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
|
|
183
|
+
};
|
|
184
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(after)) return { status: 'no-op', changed: false, error: null };
|
|
185
|
+
const written = writeTask({
|
|
186
|
+
taskRef: resolved.taskId,
|
|
187
|
+
expectedState: 'completed',
|
|
188
|
+
mutations: [...sectionMutation(content), rowMutation(after)]
|
|
189
|
+
}, {
|
|
190
|
+
...options,
|
|
191
|
+
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: 'completed' },
|
|
192
|
+
metadataProvider: () => metadata
|
|
193
|
+
});
|
|
194
|
+
if (written.status === 'failed') return { status: 'failed', changed: false, error: { code: written.error.code, message: written.error.message } };
|
|
195
|
+
return { status: written.status === 'applied' ? 'applied' : 'no-op', changed: written.changed, error: null };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { applyWorkflowWarningIntent, projectFinalizationWarning };
|
|
199
|
+
export type { FinalizationWarningProjectionResult, WarningSeverity, WarningStatus, WorkflowWarningIntent, WorkflowWarningIntentResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"yaml": "^2.9.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
|
-
"build": "
|
|
57
|
+
"build": "node scripts/run-build.js",
|
|
58
58
|
"demo:regen": "sh scripts/demo-regen.sh",
|
|
59
59
|
"prepare": "git config core.hooksPath .git-hooks || true",
|
|
60
60
|
"typecheck": "tsc -p tsconfig.test.json --noEmit && tsc -p tsconfig.jschecks.json --noEmit",
|
|
@@ -20,14 +20,14 @@ This makes Git invoke the hooks in the project repository's `.git-hooks/` direct
|
|
|
20
20
|
|
|
21
21
|
## Development Checkout CLI Alignment
|
|
22
22
|
|
|
23
|
-
When developing agent-infra from a source checkout, install dependencies and rebuild
|
|
23
|
+
When developing the agent-infra CLI from a source checkout, install dependencies and rebuild it as needed:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npm install
|
|
27
27
|
npm run build
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Lifecycle hooks
|
|
30
|
+
Lifecycle hooks always invoke `agent-infra-internal` through `PATH`; they do not implicitly switch to checkout source or `dist`. In a Codex sandbox, the controller's `PATH`-first shim is authoritative and binds the controller's selected executable. Skills, rules, hooks, and task data continue to come from the current checkout. The commands above develop the CLI itself and do not change hook executable selection. If shell commands must also resolve to this checkout, link it and compare the active version with the package version:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
npm link
|
|
@@ -20,14 +20,14 @@ git config core.hooksPath .git-hooks
|
|
|
20
20
|
|
|
21
21
|
## 开发检出中的 CLI 对齐
|
|
22
22
|
|
|
23
|
-
从源码检出开发 agent-infra
|
|
23
|
+
从源码检出开发 agent-infra CLI 时,请按需安装依赖并重新构建 CLI:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npm install
|
|
27
27
|
npm run build
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
生命周期 hook 始终通过 `PATH` 调用 `agent-infra-internal`,不会隐式切换到当前检出的 source 或 `dist`。在 Codex sandbox 中,controller 放在 `PATH` 首位的 shim 是权威入口,并绑定 controller 选定的 executable。skills、rules、hooks 和任务数据仍从当前检出读取。上面的命令只用于开发 CLI 本身,不会改变 hook 的 executable 选择。如果 shell 命令也必须解析到当前检出,请建立链接并比较当前版本与包版本:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
npm link
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
4
3
|
import { readFileSync } from 'node:fs';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
|
|
7
6
|
const MAX_INPUT_BYTES = 64 * 1024;
|
|
8
|
-
const repoPackageJson = fileURLToPath(new URL('../../package.json', import.meta.url));
|
|
9
|
-
const sourceInternalCli = fileURLToPath(new URL('../../bin/internal-cli.ts', import.meta.url));
|
|
10
|
-
const localInternalCli = fileURLToPath(new URL('../../dist/bin/internal-cli.js', import.meta.url));
|
|
11
7
|
const codexHooks = fileURLToPath(new URL('../../.codex/hooks.json', import.meta.url));
|
|
12
8
|
const clientIndex = process.argv.indexOf('--client');
|
|
13
9
|
const client = clientIndex >= 0 ? process.argv[clientIndex + 1] : '';
|
|
@@ -17,22 +13,6 @@ if (client !== 'claude-code' && client !== 'codex') {
|
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
function internalCliInvocation(args) {
|
|
20
|
-
let isAgentInfraSourceCheckout = false;
|
|
21
|
-
try {
|
|
22
|
-
isAgentInfraSourceCheckout = JSON.parse(readFileSync(repoPackageJson, 'utf8')).name === '@fitlab-ai/agent-infra';
|
|
23
|
-
} catch {
|
|
24
|
-
// Installed project hooks do not require a repository package.json.
|
|
25
|
-
}
|
|
26
|
-
if (isAgentInfraSourceCheckout && existsSync(sourceInternalCli)) {
|
|
27
|
-
return {
|
|
28
|
-
command: process.execPath,
|
|
29
|
-
commandArgs: ['--experimental-strip-types', sourceInternalCli, ...args],
|
|
30
|
-
shell: false
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
if (existsSync(localInternalCli)) {
|
|
34
|
-
return { command: process.execPath, commandArgs: [localInternalCli, ...args], shell: false };
|
|
35
|
-
}
|
|
36
16
|
return { command: 'agent-infra-internal', commandArgs: args, shell: process.platform === 'win32' };
|
|
37
17
|
}
|
|
38
18
|
|
|
@@ -125,7 +105,7 @@ process.stdin.on('end', () => {
|
|
|
125
105
|
input: JSON.stringify(normalized),
|
|
126
106
|
shell,
|
|
127
107
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
128
|
-
timeout:
|
|
108
|
+
timeout: 30_000
|
|
129
109
|
});
|
|
130
110
|
process.stdout.write(output);
|
|
131
111
|
} catch (error) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
## Recovery
|
|
12
12
|
|
|
13
|
-
`orchestration.json` is the detailed state source. A
|
|
13
|
+
`orchestration.json` is the detailed state source. A current run persists complete policy, append-only recovery, build/contract/hook-source/controller provenance, and activation monotonic timestamps. Readers accept only the complete structure emitted by the current writer; unknown fields, missing fields, invalid provenance, and old runs fail closed without rewriting. Finish or clear every active run before upgrading agent-infra. An expired prepared orphan may be explicitly recovered only with the exact task fingerprint, no consumed authorization, and no matching unconsumed active lifecycle evidence.
|
|
14
14
|
|
|
15
15
|
## Codex Host and Capability
|
|
16
16
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
## 恢复语义
|
|
12
12
|
|
|
13
|
-
`orchestration.json` 是详细状态源。
|
|
13
|
+
`orchestration.json` 是详细状态源。current run 保存完整策略、append-only 恢复历史、build/contract/hook source/controller provenance 与 activation 单调时钟。读取时只接受当前 writer 的完整结构;未知字段、缺失字段、非法 provenance 或旧 run 一律失败关闭且不改写。升级 agent-infra 前必须完成或清空所有 active run。prepared orphan 只允许在 deadline 已过、task fingerprint 精确不变、无 authorization 消耗且无匹配的未消费 active lifecycle evidence 时显式恢复。
|
|
14
14
|
|
|
15
15
|
## Codex 宿主与 capability
|
|
16
16
|
|
|
@@ -28,7 +28,7 @@ Map user intent to the corresponding workflow command:
|
|
|
28
28
|
- Historical `gemini` / `gemini-cli` remain read-only for `ai task log` rendering and are not writable
|
|
29
29
|
- Manual-step exception: no matter which agent is used, a human-made decision is always declared as `human`
|
|
30
30
|
|
|
31
|
-
The
|
|
31
|
+
The seven internal commands (`task-event` / `task-lifecycle` / `task-finalization` / `platform-issue` / `platform-comment` / `platform-pr` / `task-orchestration`) hard-validate `--agent` against a whitelist: they accept a short token, a long name, or `human`, and reject non-standard values with the matching `*_PAYLOAD_INVALID` error; long names are normalized to the short token before being persisted or forwarded. The `ai task log` renderer keeps unknown tokens grouped as `human` but appends a visible `(unknown)` marker.
|
|
32
32
|
|
|
33
33
|
## State Snapshot and Completion Verification Entrypoints
|
|
34
34
|
|
|
@@ -68,7 +68,7 @@ The six internal commands (`task-event` / `task-lifecycle` / `platform-issue` /
|
|
|
68
68
|
`- {time} — **{base} [started]** by {agent} — started`
|
|
69
69
|
- **done line** (written when the step completes, unchanged from today): the action is the base itself:
|
|
70
70
|
`- {time} — **{base}** by {agent} — {completion summary}`
|
|
71
|
-
- **Commit
|
|
71
|
+
- **Commit execution boundary**: the commit skill must not hand-write task Activity Log, review anchors, receipts, or checkpoints. Task-bound direct and orchestrated modes both call the single `commit-operation.execute`; taskless direct is allowed only for `TASK_CONTEXT_NOT_FOUND` and creates no task-state side effects.
|
|
72
72
|
- `{base}` is that skill's existing done action text, including `(Round {N})` (e.g. `Plan Task (Round 1)`). started and done must share the same `{base}` to pair.
|
|
73
73
|
|
|
74
74
|
**Pairing and rendering** (`ai task log`): a started entry pairs with the next same-`{base}` done entry onto one row (repeated executions of the same base pair FIFO by ascending time). The STARTED column shows the start time, DONE the completion time; started with no done = in progress (DONE shows `(in progress)`); done with no started (legacy logs) = a standalone completed row. All three shapes are valid and never error.
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
- 历史 `gemini` / `gemini-cli` 仅保留 `ai task log` 渲染兼容,不接受写入
|
|
29
29
|
- 人工步骤例外:无论使用哪个 Agent,只要是人做的决定,统一声明 `human`
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
七个内部命令(`task-event` / `task-lifecycle` / `task-finalization` / `platform-issue` / `platform-comment` / `platform-pr` / `task-orchestration`)对 `--agent` 做白名单硬校验:接受短名、长名或 `human`,非标准值返回对应 `*_PAYLOAD_INVALID` 错误;长名在落库/透传前归一化为短名。渲染端 `ai task log` 对无法识别的 token 保留 `human` 归类并附加 `(unknown)` 标记。
|
|
32
32
|
|
|
33
33
|
## 状态快照与完成验证入口
|
|
34
34
|
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
`- {time} — **{基名} [started]** by {agent} — started`
|
|
71
71
|
- **done 行**(步骤完成时写,与现状一致):action 即基名本身:
|
|
72
72
|
`- {time} — **{基名}** by {agent} — {完成说明}`
|
|
73
|
-
- **Commit
|
|
73
|
+
- **Commit 执行边界**:commit 技能不得手写 task Activity Log、review anchor、receipt 或 checkpoint。task-bound direct 与 orchestrated 都调用唯一 `commit-operation.execute`;taskless direct 只在 `TASK_CONTEXT_NOT_FOUND` 时允许,且不产生任务状态副作用。
|
|
74
74
|
- `{基名}` 指该 SKILL 既有 done 条目的 action 文本,含 `(Round {N})`(如 `Plan Task (Round 1)`)。
|
|
75
75
|
started 与 done 共用同一 `{基名}` 才能配对。
|
|
76
76
|
|