@fitlab-ai/agent-infra 0.11.13 → 0.11.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/internal-cli.js +2 -7
- package/dist/lib/agent-clients/adapter.js +0 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
- package/dist/lib/agent-clients/adapters/codex.js +0 -2
- package/dist/lib/internal/cli-route-inventory.js +0 -1
- package/dist/lib/internal/task-finalization.js +7 -5
- package/dist/lib/internal/task-lifecycle.js +3 -1
- package/dist/lib/internal/task-operation-registry.js +1 -3
- package/dist/lib/internal/task-validate.js +14 -124
- package/dist/lib/platform/pr-change-report.js +10 -3
- package/dist/lib/platform/release-notes.js +2 -2
- package/dist/lib/process-data/index.js +15 -4
- package/dist/lib/sandbox/commands/control-status.js +0 -1
- package/dist/lib/sandbox/commands/create.js +5 -4
- package/dist/lib/sandbox/config.js +12 -0
- package/dist/lib/sandbox/control/audit.js +1 -2
- package/dist/lib/sandbox/control/client.js +12 -47
- package/dist/lib/sandbox/control/executor.js +6 -10
- package/dist/lib/sandbox/control/host-recovery.js +89 -27
- package/dist/lib/sandbox/control/lifecycle.js +22 -37
- package/dist/lib/sandbox/control/protocol.js +8 -36
- package/dist/lib/sandbox/control/server.js +2 -21
- package/dist/lib/sandbox/environment.js +7 -8
- package/dist/lib/sandbox/mounts.js +71 -59
- package/dist/lib/sandbox/recovery.js +2 -3
- package/dist/lib/sandbox/removal.js +19 -4
- package/dist/lib/sandbox/workspace-identity.js +30 -15
- package/dist/lib/sandbox/workspace-view.js +41 -4
- package/dist/lib/task/control-authority.js +39 -17
- package/dist/lib/task/control-recovery.js +1 -2
- package/dist/lib/task/finalization.js +13 -2
- package/dist/lib/task/lifecycle.js +122 -35
- package/dist/lib/task/short-id.js +2 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +0 -27
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
- package/lib/agent-clients/adapters/codex.ts +0 -2
- package/lib/internal/cli-route-inventory.ts +0 -1
- package/lib/internal/task-finalization.ts +8 -6
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +1 -3
- package/lib/internal/task-validate.ts +13 -127
- package/lib/platform/pr-change-report.ts +9 -3
- package/lib/platform/release-notes.ts +2 -2
- package/lib/process-data/index.ts +15 -4
- package/lib/sandbox/commands/control-status.ts +0 -1
- package/lib/sandbox/commands/create.ts +5 -4
- package/lib/sandbox/config.ts +13 -0
- package/lib/sandbox/control/audit.ts +1 -2
- package/lib/sandbox/control/client.ts +13 -61
- package/lib/sandbox/control/executor.ts +7 -13
- package/lib/sandbox/control/host-recovery.ts +94 -21
- package/lib/sandbox/control/lifecycle.ts +22 -37
- package/lib/sandbox/control/protocol.ts +9 -45
- package/lib/sandbox/control/server.ts +2 -17
- package/lib/sandbox/environment.ts +7 -8
- package/lib/sandbox/mounts.ts +92 -75
- package/lib/sandbox/recovery.ts +2 -3
- package/lib/sandbox/removal.ts +16 -4
- package/lib/sandbox/workspace-identity.ts +36 -13
- package/lib/sandbox/workspace-view.ts +40 -5
- package/lib/task/control-authority.ts +40 -26
- package/lib/task/control-recovery.ts +2 -3
- package/lib/task/finalization.ts +15 -2
- package/lib/task/lifecycle.ts +135 -33
- package/lib/task/short-id.ts +5 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
- package/templates/.agents/rules/task-short-id.en.md +18 -12
- package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
- package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
- package/dist/lib/runtime/agent-runtime.js +0 -33
- package/dist/lib/sandbox/control/agent-operation.js +0 -34
- package/dist/lib/task/runtime-paths.js +0 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
- package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
- package/lib/runtime/agent-runtime.ts +0 -41
- package/lib/sandbox/control/agent-operation.ts +0 -49
- package/lib/task/runtime-paths.ts +0 -12
package/lib/task/lifecycle.ts
CHANGED
|
@@ -103,9 +103,14 @@ type LifecycleFileSystem = {
|
|
|
103
103
|
readFileSync: (file: string) => string;
|
|
104
104
|
writeFileSync: (file: string, content: string, flag?: string) => void;
|
|
105
105
|
unlinkSync: (file: string) => void;
|
|
106
|
+
rmdirSync: (directory: string) => void;
|
|
107
|
+
readdirSync: (directory: string) => string[];
|
|
108
|
+
readMountInfoSync: () => string;
|
|
106
109
|
};
|
|
107
110
|
type TaskLifecycleOptions = {
|
|
108
111
|
repoRoot?: string;
|
|
112
|
+
controlRoot?: string;
|
|
113
|
+
controlProject?: string;
|
|
109
114
|
metadataProvider?: () => TaskWriteMetadata;
|
|
110
115
|
fileSystem?: Partial<LifecycleFileSystem>;
|
|
111
116
|
taskFileSystem?: Partial<TaskFileSystem>;
|
|
@@ -125,9 +130,25 @@ const DEFAULT_IO: LifecycleFileSystem = {
|
|
|
125
130
|
renameSync: (source, target) => fs.renameSync(source, target),
|
|
126
131
|
readFileSync: (file) => fs.readFileSync(file, 'utf8'),
|
|
127
132
|
writeFileSync: (file, content, flag) => fs.writeFileSync(file, content, { encoding: 'utf8', flag: flag ?? 'w' }),
|
|
128
|
-
unlinkSync: (file) => fs.unlinkSync(file)
|
|
133
|
+
unlinkSync: (file) => fs.unlinkSync(file),
|
|
134
|
+
rmdirSync: (directory) => fs.rmdirSync(directory),
|
|
135
|
+
readdirSync: (directory) => fs.readdirSync(directory),
|
|
136
|
+
readMountInfoSync: () => fs.readFileSync('/proc/self/mountinfo', 'utf8')
|
|
129
137
|
};
|
|
130
138
|
|
|
139
|
+
function isVirtiofsMountPoint(directory: string, mountInfo: string): boolean {
|
|
140
|
+
const decodeMountInfoPath = (value: string): string => value.replace(/\\([0-7]{3})/gu, (_match, octal: string) => String.fromCharCode(Number.parseInt(octal, 8)));
|
|
141
|
+
const expected = path.resolve(directory);
|
|
142
|
+
return mountInfo.split('\n').some((line) => {
|
|
143
|
+
if (!line) return false;
|
|
144
|
+
const fields = line.split(' ');
|
|
145
|
+
const separator = fields.indexOf('-');
|
|
146
|
+
return separator >= 0
|
|
147
|
+
&& fields[separator + 1] === 'virtiofs'
|
|
148
|
+
&& path.resolve(decodeMountInfoPath(fields[4] ?? '')) === expected;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
131
152
|
function failed(request: Pick<TaskLifecycleRequest, 'taskRef' | 'intent'>, error: LifecycleError, extra: Partial<TaskLifecycleResult> = {}): TaskLifecycleResult {
|
|
132
153
|
return {
|
|
133
154
|
status: 'failed', changed: false, requestRef: request.taskRef, taskId: null,
|
|
@@ -276,6 +297,105 @@ function transferTaskContents(sourcePath: string, targetPath: string): void {
|
|
|
276
297
|
}
|
|
277
298
|
}
|
|
278
299
|
|
|
300
|
+
function pathExists(candidate: string): boolean {
|
|
301
|
+
try { fs.lstatSync(candidate); return true; }
|
|
302
|
+
catch (error) {
|
|
303
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function assertTaskControlRootPath(controlRoot: string, taskId: string, expectedProject?: string): void {
|
|
309
|
+
if (path.basename(controlRoot) !== taskId || path.basename(path.dirname(controlRoot)) !== 'tasks'
|
|
310
|
+
|| (expectedProject !== undefined && path.basename(path.dirname(path.dirname(controlRoot))) !== expectedProject)) {
|
|
311
|
+
throw new Error('control root path does not match the task/project layout');
|
|
312
|
+
}
|
|
313
|
+
const managedBase = path.dirname(path.dirname(path.dirname(controlRoot)));
|
|
314
|
+
let current = managedBase;
|
|
315
|
+
for (const segment of path.relative(managedBase, controlRoot).split(path.sep).filter(Boolean)) {
|
|
316
|
+
const stat = fs.lstatSync(current);
|
|
317
|
+
if (stat.isSymbolicLink()) throw new Error('control root ancestor is a symbolic link');
|
|
318
|
+
current = path.join(current, segment);
|
|
319
|
+
}
|
|
320
|
+
const stat = fs.lstatSync(controlRoot);
|
|
321
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('control root is not a real directory');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function assertTaskControlManifestIdentity(controlRoot: string, taskId: string, repoRoot: string, expectedProject?: string): void {
|
|
325
|
+
const controlManifest = path.join(controlRoot, 'manifest.json');
|
|
326
|
+
if (!fs.existsSync(controlManifest)) throw new Error('control manifest is missing');
|
|
327
|
+
const manifestStat = fs.lstatSync(controlManifest);
|
|
328
|
+
if (!manifestStat.isFile() || manifestStat.isSymbolicLink()) throw new Error('control manifest is not a real file');
|
|
329
|
+
const manifest = JSON.parse(fs.readFileSync(controlManifest, 'utf8')) as {
|
|
330
|
+
mode?: unknown; taskId?: unknown; project?: unknown; repoRoot?: unknown;
|
|
331
|
+
};
|
|
332
|
+
const project = expectedProject ?? path.basename(path.dirname(path.dirname(controlRoot)));
|
|
333
|
+
if (manifest.mode !== 'task-bound' || manifest.taskId !== taskId || manifest.project !== project
|
|
334
|
+
|| typeof manifest.repoRoot !== 'string' || fs.realpathSync.native(manifest.repoRoot) !== fs.realpathSync.native(repoRoot)) {
|
|
335
|
+
throw new Error('control manifest identity does not match the task');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function taskBoundControlMoveSafety(
|
|
340
|
+
request: TaskLifecycleRequest,
|
|
341
|
+
controlRoot: string | undefined,
|
|
342
|
+
options: TaskLifecycleOptions,
|
|
343
|
+
taskId: string,
|
|
344
|
+
repoRoot: string,
|
|
345
|
+
validateManifestIdentity: boolean
|
|
346
|
+
): LifecycleError | null {
|
|
347
|
+
if (!controlRoot) return null;
|
|
348
|
+
const controlManifest = path.join(controlRoot, 'manifest.json');
|
|
349
|
+
const controlLease = path.join(controlRoot, 'lease.json');
|
|
350
|
+
const stateExists = pathExists(controlRoot) || pathExists(controlManifest) || pathExists(controlLease);
|
|
351
|
+
if (stateExists) {
|
|
352
|
+
try {
|
|
353
|
+
assertTaskControlRootPath(controlRoot, taskId, options.controlProject);
|
|
354
|
+
if (validateManifestIdentity) {
|
|
355
|
+
assertTaskControlManifestIdentity(controlRoot, taskId, repoRoot, options.controlProject);
|
|
356
|
+
} else {
|
|
357
|
+
const manifestStat = fs.lstatSync(controlManifest);
|
|
358
|
+
if (!manifestStat.isFile() || manifestStat.isSymbolicLink()) throw new Error('control manifest is not a real file');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task-bound sandbox control root is missing, malformed, or belongs to another task' };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (request.intent !== 'complete' && (pathExists(controlManifest) || pathExists(controlLease))) {
|
|
366
|
+
return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task-bound sandbox control state exists; stop and remove the verified sandbox before moving the task directory' };
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function orchestrationMoveSafety(sourcePath: string): LifecycleError | null {
|
|
372
|
+
const orchestrationPath = path.join(sourcePath, '.runtime', 'orchestration.json');
|
|
373
|
+
if (!pathExists(orchestrationPath)) return null;
|
|
374
|
+
try {
|
|
375
|
+
const run = JSON.parse(fs.readFileSync(orchestrationPath, 'utf8')) as { status?: unknown; pendingDelegation?: unknown };
|
|
376
|
+
if (run.status === 'running' || run.pendingDelegation !== null && run.pendingDelegation !== undefined) {
|
|
377
|
+
return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration has an active or pending delegation; settle it before moving the task directory' };
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration state is unreadable; refusing to move the task directory' };
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function runtimeMoveSafety(
|
|
386
|
+
request: TaskLifecycleRequest,
|
|
387
|
+
movingTaskDirectory: boolean,
|
|
388
|
+
options: TaskLifecycleOptions,
|
|
389
|
+
taskId: string,
|
|
390
|
+
repoRoot: string,
|
|
391
|
+
sourcePath: string,
|
|
392
|
+
validateControlManifestIdentity: boolean
|
|
393
|
+
): LifecycleError | null {
|
|
394
|
+
if (!movingTaskDirectory) return null;
|
|
395
|
+
const controlSafety = taskBoundControlMoveSafety(request, options.controlRoot, options, taskId, repoRoot, validateControlManifestIdentity);
|
|
396
|
+
return controlSafety ?? orchestrationMoveSafety(sourcePath);
|
|
397
|
+
}
|
|
398
|
+
|
|
279
399
|
function targetContainsTask(targetPath: string, taskId: string, request: TaskLifecycleRequest): boolean {
|
|
280
400
|
try {
|
|
281
401
|
const stat = fs.lstatSync(targetPath);
|
|
@@ -554,35 +674,6 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
|
|
|
554
674
|
const targetPath = path.join(repoRoot, '.agents', 'workspace', spec.target, taskId);
|
|
555
675
|
const requiredSteps = stepsForIntent(request.intent);
|
|
556
676
|
const movingTaskDirectory = sourcePath !== targetPath;
|
|
557
|
-
const assertRuntimeMoveSafe = (): LifecycleError | null => {
|
|
558
|
-
if (!movingTaskDirectory) return null;
|
|
559
|
-
const controlManifest = path.join(sourcePath, '.runtime', 'sandbox-control', 'manifest.json');
|
|
560
|
-
const controlLease = path.join(sourcePath, '.runtime', 'sandbox-control', 'lease.json');
|
|
561
|
-
if (request.intent !== 'complete' && (fs.existsSync(controlManifest) || fs.existsSync(controlLease))) {
|
|
562
|
-
return {
|
|
563
|
-
code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
|
|
564
|
-
message: 'task-bound sandbox control state exists; stop and remove the verified sandbox before moving the task directory'
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
const orchestrationPath = path.join(sourcePath, '.runtime', 'orchestration.json');
|
|
568
|
-
if (fs.existsSync(orchestrationPath)) {
|
|
569
|
-
try {
|
|
570
|
-
const run = JSON.parse(fs.readFileSync(orchestrationPath, 'utf8')) as { status?: unknown; pendingDelegation?: unknown };
|
|
571
|
-
if (run.status === 'running' || run.pendingDelegation !== null && run.pendingDelegation !== undefined) {
|
|
572
|
-
return {
|
|
573
|
-
code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
|
|
574
|
-
message: 'task orchestration has an active or pending delegation; settle it before moving the task directory'
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
} catch {
|
|
578
|
-
return {
|
|
579
|
-
code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
|
|
580
|
-
message: 'task orchestration state is unreadable; refusing to move the task directory'
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
return null;
|
|
585
|
-
};
|
|
586
677
|
const currentTaskFile = path.join(sourcePath, 'task.md');
|
|
587
678
|
const existingJournal = fs.existsSync(path.join(sourcePath, '.task-lifecycle.json'));
|
|
588
679
|
if (sourceState === spec.target && !existingJournal) {
|
|
@@ -605,7 +696,7 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
|
|
|
605
696
|
&& !(request.intent === 'complete' && targetContainsTask(targetPath, taskId, request))) {
|
|
606
697
|
return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
607
698
|
}
|
|
608
|
-
const moveSafety =
|
|
699
|
+
const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, true);
|
|
609
700
|
if (moveSafety) return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
610
701
|
const content = io.readFileSync(currentTaskFile);
|
|
611
702
|
let frontmatter;
|
|
@@ -732,7 +823,7 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
|
|
|
732
823
|
}
|
|
733
824
|
if (fs.existsSync(targetPath) && !fs.existsSync(path.join(sourcePath, 'task.md'))) completed.add('directory-moved');
|
|
734
825
|
if (!completed.has('directory-moved')) {
|
|
735
|
-
const moveSafety =
|
|
826
|
+
const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, false);
|
|
736
827
|
if (moveSafety) return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, completedSteps: [...completed], changed: completed.size > 0, timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
737
828
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
738
829
|
if (request.intent === 'complete') {
|
|
@@ -791,7 +882,18 @@ function applyTaskLifecycleUnlocked(requestInput: TaskLifecycleRequest, options:
|
|
|
791
882
|
}
|
|
792
883
|
const activeTaskPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
|
|
793
884
|
if (request.intent === 'complete' && fs.existsSync(activeTaskPath)) {
|
|
794
|
-
try {
|
|
885
|
+
try {
|
|
886
|
+
let sourceIsVirtiofsMount = false;
|
|
887
|
+
try { sourceIsVirtiofsMount = isVirtiofsMountPoint(activeTaskPath, io.readMountInfoSync()); }
|
|
888
|
+
catch { /* Platforms without mountinfo retain the ordinary directory cleanup path. */ }
|
|
889
|
+
if (sourceIsVirtiofsMount) {
|
|
890
|
+
if (io.readdirSync(activeTaskPath).length > 0) {
|
|
891
|
+
throw new Error('virtiofs source directory is not empty after task contents were transferred');
|
|
892
|
+
}
|
|
893
|
+
} else {
|
|
894
|
+
io.rmdirSync(activeTaskPath);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
795
897
|
catch (error) {
|
|
796
898
|
return failed(request, { code: 'LIFECYCLE_SOURCE_DIRECTORY_CLEANUP_FAILED', message: String(error) }, {
|
|
797
899
|
taskId, sourceState, targetState: spec.target, sourcePath: activeTaskPath, targetPath, journalPath,
|
package/lib/task/short-id.ts
CHANGED
|
@@ -464,7 +464,10 @@ function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
|
|
|
464
464
|
return map;
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
-
function inspectShortIdRegistry(
|
|
467
|
+
function inspectShortIdRegistry(
|
|
468
|
+
repoRoot: string,
|
|
469
|
+
shortIdLength = configuredShortIdLength(repoRoot)
|
|
470
|
+
): ShortIdRegistryInspection {
|
|
468
471
|
const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
|
|
469
472
|
if (!fs.existsSync(registryPath)) {
|
|
470
473
|
return {
|
|
@@ -503,7 +506,7 @@ function inspectShortIdRegistry(repoRoot: string): ShortIdRegistryInspection {
|
|
|
503
506
|
}
|
|
504
507
|
|
|
505
508
|
try {
|
|
506
|
-
const registry = validateRegistry(parsed, registryPath,
|
|
509
|
+
const registry = validateRegistry(parsed, registryPath, shortIdLength);
|
|
507
510
|
const shortIds = new Map<string, string>();
|
|
508
511
|
for (const [key, taskId] of Object.entries(registry.ids)) shortIds.set(taskId, key);
|
|
509
512
|
return { status: 'valid', shortIds };
|
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.15",
|
|
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",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
## Current Host
|
|
16
16
|
|
|
17
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.
|
|
19
18
|
- 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.
|
|
20
19
|
- 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
20
|
|
|
@@ -28,9 +27,9 @@
|
|
|
28
27
|
|
|
29
28
|
## Current-only Cutover and Rollback
|
|
30
29
|
|
|
31
|
-
- Enter a quiet window before cutover: let in-flight orchestration intents return
|
|
32
|
-
- Deploy or roll back the CLI, broker,
|
|
33
|
-
- On either the new or rolled-back version, first confirm the same task through `status`/`route
|
|
30
|
+
- Enter a quiet window before cutover: let in-flight orchestration intents return and preserve the worktree, task/run/orchestration state, receipts, and audit. For an unknown result, inspect local `status`/`route` before using existing recover/advance semantics; do not replay an old broker request.
|
|
31
|
+
- Deploy or roll back the CLI, broker, 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 or request.
|
|
32
|
+
- On either the new or rolled-back version, first confirm the same task through `status`/`route`. Keep the quiet window and evidence intact if any check fails; do not mix component versions or rewrite domain facts.
|
|
34
33
|
|
|
35
34
|
## Model Policy
|
|
36
35
|
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
## 当前宿主
|
|
16
16
|
|
|
17
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 绑定。
|
|
19
18
|
- prepare 校验当前任务、模型策略与宿主 preflight。启动和完成记录关联实际 parent/child 身份,失败不得写成成功。
|
|
20
19
|
- 客户端特有的 preflight、事件来源与停止证据校验由客户端适配器实现;公共总控只调用统一能力。不得在公共流程中新增客户端 ID 分支。
|
|
21
20
|
|
|
@@ -28,9 +27,9 @@
|
|
|
28
27
|
|
|
29
28
|
## Current-only 切换与回滚
|
|
30
29
|
|
|
31
|
-
- 切换前进入静默窗口:等待在途编排 intent
|
|
32
|
-
- CLI、broker
|
|
33
|
-
-
|
|
30
|
+
- 切换前进入静默窗口:等待在途编排 intent 返回,并保留 worktree、task/run/orchestration 状态、receipts 与 audit。结果未知时先用本地 `status`/`route` 判断 domain 是否已提交,再按现有 recover/advance 语义处理;不重放旧 broker request。
|
|
31
|
+
- CLI、broker 与协议必须作为同一版本单元部署或回退。停止旧 container/broker 后执行 rebuild 与 `ai sandbox start --recreate <task-ref>`,以新的 control generation 恢复执行;旧 token 和 request 不得复用。
|
|
32
|
+
- 新版本或回滚版本都必须先核对同一任务的 `status`/`route`。任一检查失败时继续保持静默并保留证据,不局部混用组件或改写 domain 事实。
|
|
34
33
|
|
|
35
34
|
## 模型策略
|
|
36
35
|
|
|
@@ -24,12 +24,13 @@ Task short ids let mobile-style SKILL invocations replace the full 22-char
|
|
|
24
24
|
|------------|-----------------------------------------------------------------------|---------------------------------------------------------------|
|
|
25
25
|
| alloc | `create-task`, `import-issue`, `import-codescan`, `import-dependabot` | Assigns lowest free `NN` in the registry. |
|
|
26
26
|
| resolve | Lifecycle SKILLs (`analyze-task`, `plan-task`, `code-task`, …) | Looks up `NN` → full task id. Does not allocate. |
|
|
27
|
-
| release |
|
|
27
|
+
| release | Successful sandbox cleanup; also `cancel-task`, `block-task`, `close-codescan`, and `close-dependabot` | Removes the registry entry. |
|
|
28
28
|
| re-alloc | `restore-task` | Re-allocates a (possibly new) `NN` in the registry. |
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
Completion retains the short id so a sandbox still associated with the
|
|
31
|
+
completed task can resolve it before cleanup. The id is released only after
|
|
32
|
+
managed sandbox cleanup confirms that the container was deleted successfully.
|
|
33
|
+
Other lifecycle intents release ids at the times listed in the table.
|
|
33
34
|
|
|
34
35
|
## Configuration
|
|
35
36
|
|
|
@@ -52,10 +53,13 @@ archiving all active tasks first (the registry key width depends on it).
|
|
|
52
53
|
| Entrypoint | Hit | Miss |
|
|
53
54
|
|-------------------------------------------------------------|----------------------|------------------------------------------------------|
|
|
54
55
|
| SKILL parameter resolver (lifecycle SKILLs) | resolve to full id | **strict error** — short id not found / invalid |
|
|
55
|
-
| `ai sandbox exec <N>` / `ai sandbox create <N>` | resolve to
|
|
56
|
+
| `ai sandbox exec <N>` / `ai sandbox create <N>` | resolve to an active task id, then read `branch` from task.md | **strict error** — no ls-index fallback, no literal-branch fallback; hint the user to pass a short id / `TASK-id` / branch name |
|
|
57
|
+
| `ai sandbox rm <N>` | resolve the retained mapping, then read active or completed identity by full task id | **strict error** — stop cleanup when the short id is missing |
|
|
56
58
|
|
|
57
|
-
`list --verify` is strictly read-only:
|
|
58
|
-
|
|
59
|
+
`list --verify` is strictly read-only: every active task must have a mapping;
|
|
60
|
+
registry mappings may point to a valid active task or to a
|
|
61
|
+
`completed/{taskId}/task.md` whose identity matches and whose status is
|
|
62
|
+
`completed`. Other mappings are reported as orphans; no state is modified.
|
|
59
63
|
|
|
60
64
|
## SKILL parameter resolver
|
|
61
65
|
|
|
@@ -94,11 +98,13 @@ Short ids are pure local state, persisted only in the registry
|
|
|
94
98
|
- Created on demand by the first `alloc`; an absent file is treated as an empty
|
|
95
99
|
registry.
|
|
96
100
|
- Short ids are assigned only by an explicit `alloc` (`create-task` /
|
|
97
|
-
`import-*` / `restore-task`); `resolve`
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
`import-*` / `restore-task`); `resolve` and `list` are read-only and do not
|
|
102
|
+
perform implicit cleanup.
|
|
103
|
+
- `alloc` may clean up stale entries that point neither to an active task nor
|
|
104
|
+
to valid completed task content. Completed mappings stay until managed
|
|
105
|
+
sandbox cleanup succeeds.
|
|
106
|
+
- Task operations may resolve a completed mapping and then reject it at their
|
|
107
|
+
own state gate; `ai sandbox create <N>` still accepts active tasks only.
|
|
102
108
|
|
|
103
109
|
`resolve(<N>)` workflow: ① validate arg matches `^[0-9]+$` →
|
|
104
110
|
② strip leading zeros and take the numeric value `n`; classify as reserved
|
|
@@ -19,11 +19,12 @@
|
|
|
19
19
|
|-----------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------|
|
|
20
20
|
| alloc | `create-task`、`import-issue`、`import-codescan`、`import-dependabot` | 分配最小可用 `NN`,写入注册表。 |
|
|
21
21
|
| resolve | 生命周期 SKILL(`analyze-task` / `plan-task` / `code-task` / `review-*` / `commit` / …) | `NN` → 完整 task id 查询,不分配。 |
|
|
22
|
-
| release | `
|
|
22
|
+
| release | sandbox cleanup 成功;以及 `cancel-task`、`block-task`、`close-codescan`、`close-dependabot` | 从注册表移除。 |
|
|
23
23
|
| re-alloc | `restore-task` | 重新分配(可能与历史不同),写入注册表。 |
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
complete 保留短号,供 completed task 与仍关联的 sandbox 在 cleanup 前继续解析。
|
|
26
|
+
受管 sandbox cleanup 确认容器已删除且清理成功后,才释放对应 completed task 的短号。
|
|
27
|
+
其他 lifecycle intent 继续按表中时机释放短号。
|
|
27
28
|
|
|
28
29
|
## 配置
|
|
29
30
|
|
|
@@ -45,9 +46,12 @@
|
|
|
45
46
|
| 入口 | 注册表命中 | 注册表未命中 |
|
|
46
47
|
|-----------------------------------------------------------|----------------------|--------------------------------------------------------|
|
|
47
48
|
| SKILL 入参解析器(生命周期 SKILL) | 解析为完整 task id | **严格报错** —— 短号不存在 / 格式错误 |
|
|
48
|
-
| `ai sandbox exec <N>` / `ai sandbox create <N>` |
|
|
49
|
+
| `ai sandbox exec <N>` / `ai sandbox create <N>` | 解析为 active task id 后查 task.md 取 `branch` | **严格报错** —— 不再回退到 ls 行号或字面分支名;提示用任务短号 / `TASK-id` / 分支名 |
|
|
50
|
+
| `ai sandbox rm <N>` | 解析保留映射,再按完整 task id 读取 active 或 completed 身份 | **严格报错** —— 短号不存在时停止清理 |
|
|
49
51
|
|
|
50
|
-
`list --verify`
|
|
52
|
+
`list --verify` 严格只读:active task 必须有映射;注册表映射可指向有效 active task
|
|
53
|
+
或 `completed/{taskId}/task.md` 中身份匹配且状态为 completed 的 task。其他映射作为 orphan
|
|
54
|
+
报告,不修改任何状态。
|
|
51
55
|
|
|
52
56
|
## SKILL 入参解析
|
|
53
57
|
|
|
@@ -78,8 +82,9 @@ fi
|
|
|
78
82
|
- key 是零填充到 `task.shortIdLength` 位的字符串,value 是完整 `TASK-…` task id
|
|
79
83
|
- 自动 git ignore(active 工作区整体 ignore;无需新增 ignore 条目)
|
|
80
84
|
- 首次 `alloc` 时按需自动创建;不存在时按空注册表处理
|
|
81
|
-
- 短号只由显式 `alloc`(`create-task` / `import-*` / `restore-task`)分配;`resolve`
|
|
82
|
-
-
|
|
85
|
+
- 短号只由显式 `alloc`(`create-task` / `import-*` / `restore-task`)分配;`resolve` 与 `list` 只读,不做隐式清理
|
|
86
|
+
- alloc 可清理既非 active、也没有有效 completed task 内容的 stale entry;completed mapping 保留至受管 sandbox cleanup 成功
|
|
87
|
+
- task 操作可按 completed mapping 解析后由各自状态门禁拒绝;`ai sandbox create <N>` 仍只接受 active task
|
|
83
88
|
|
|
84
89
|
`resolve(<N>)` 工作流:① 校验入参匹配 `^[0-9]+$` → ② 去前导零取
|
|
85
90
|
数值 `n`,按 `n` 是否 `== 0`(保留)/ `> 10^shortIdLength - 1`(超容量)/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: complete-task
|
|
3
3
|
description: >
|
|
4
|
-
Mark a task as completed
|
|
5
|
-
Use when a task's work is done and verified and you
|
|
4
|
+
Mark a task as completed.
|
|
5
|
+
Use when a task's work is done and verified and you need to finalize the task.
|
|
6
6
|
Only invoke this skill automatically when the conversation includes a resolvable task reference.
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -144,6 +144,14 @@ When a valid `platform_issue_identity` exists, execute in this exact order:
|
|
|
144
144
|
1. Run `agent-infra-internal platform-issue sync {task-id} --agent {standard-agent-token} --requirements --fields`.
|
|
145
145
|
2. Write the business summary to a temporary file and run `agent-infra-internal platform-comment stage-summary {task-id} --body-file {path}`. This stores the body and SHA-256 in the task's durable staging record; it must not publish the summary comment directly.
|
|
146
146
|
|
|
147
|
+
The summary body records only delivery results, the associated PR and actual merge facts, verification commands with their scope/results/uncovered areas, and known todo items supported by current task evidence. Include the related commit when it helps explain verification scope. Do not infer unsupported facts; list unfinished work as a todo. This is an Issue closeout summary, not a PR comment.
|
|
148
|
+
|
|
149
|
+
Use the active skill's language by default: the Chinese skill produces a Chinese summary, and the English skill produces an English summary. Keep the same field structure in both languages.
|
|
150
|
+
|
|
151
|
+
Before staging, check the body: remove candidate or pending-closeout placeholders; omit the local short id, unrelated window/model/receipt operation details, and the full TASK-ID already shown by the outer comment. Do not remove or rewrite platform-managed comment markers or recovery metadata. Keep paths when they help explain product behavior. The summary does not predict the Complete Task finalization result or claim that the task is completed or archived. The task target state is `completed`; do not append Archive Tasks by default. The formal task comment and receipt carry the lifecycle terminal status and warnings.
|
|
152
|
+
|
|
153
|
+
Use `stage-summary` to stage the body while the task is active. Do not assume the command remains available after the task reaches completed. If the finalization result shows that a published delivery fact needs correction, first confirm that an existing supported comment-sync entry point accepts the current task state. If none does, report that limitation in the final response; do not add an entry point or change the call sequence.
|
|
154
|
+
|
|
147
155
|
When the ledger contains a valid `PRC-N` post-review exemption, the summary body must mirror the ruling reason, commit scope, human identity, and time from task.md, and state that this is a human override rather than an automatic verification success. If a matching workflow warning exists, also mirror its original failure code/message. If no warning exists yet, state only that the ruling is recorded and final gate verification is pending; do not claim that the exemption has passed. The same `--kind summary` intent remains the sole owner of the summary marker.
|
|
148
156
|
|
|
149
157
|
Do not sync the task comment here; it requires the terminal task.md written by lifecycle. Do not set a `status:` label; platform automation clears status labels after the Issue closes.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: complete-task
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
标记任务完成。
|
|
5
|
+
当任务工作已完成并验证、需要提交任务收尾时使用。
|
|
6
6
|
仅当对话包含可解析的任务引用时才可自动调用本技能。
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -143,6 +143,14 @@ Please satisfy the hard prerequisite first, then retry complete-task.
|
|
|
143
143
|
1. 调用 `agent-infra-internal platform-issue sync {task-id} --agent {standard-agent-token} --requirements --fields`。
|
|
144
144
|
2. 把业务摘要写入临时文件,并调用 `agent-infra-internal platform-comment stage-summary {task-id} --body-file {path}`。该命令把正文和 SHA-256 写入 task 目录的 durable staging record;本步骤不得直接发布 summary 评论。
|
|
145
145
|
|
|
146
|
+
摘要正文只记录当前任务证据能够支持的交付结果、关联 PR 与实际合入事实、验证命令及其范围/结果/未覆盖部分,以及已知待办;需要说明验证范围时可注明对应提交。没有证据支持的事实不推断,未完成事项写入待办。这是 Issue 收尾摘要,不是 PR 留言。
|
|
147
|
+
|
|
148
|
+
摘要默认使用当前技能的语言:中文技能生成中文摘要,英文技能生成英文摘要。中英文摘要保持相同字段结构。
|
|
149
|
+
|
|
150
|
+
在暂存前核对正文:删除候选和待收尾占位;不写本地短号、无关窗口/模型/receipt 操作流水或外层已有的完整 TASK-ID。不要删除或改写平台维护的评论 marker 和恢复 metadata;与产品行为有关的路径可按需保留。摘要不预测 Complete Task 的 finalization 终态,也不声称任务已完成或已归档。任务目标状态止于 `completed`,不默认追加 Archive Tasks;生命周期终态及 warning 由正式 task 评论和 receipt 承担。
|
|
151
|
+
|
|
152
|
+
`stage-summary` 用于任务仍处于 active 时暂存摘要。任务进入 completed 后,不要假定该命令仍可用。若 finalization 结果表明已发布的交付事实需要更正,先确认现有受支持的评论同步入口允许在当前状态执行;不支持时,在最终汇报中如实说明限制,不新增入口或改变调用时序。
|
|
153
|
+
|
|
146
154
|
若账本含合法的 `PRC-N` post-review 豁免,摘要正文必须镜像 task.md 中的裁决理由、提交范围、人工身份与时间,并明确这是人工覆盖而非自动校验成功。已有匹配 workflow warning 时,同时镜像其原始 failure code/message;尚无 warning 时只写“裁决已记录、最终门禁待验证”,不得提前宣称豁免已通过。summary marker 仍由同一 `--kind summary` intent 唯一维护。
|
|
147
155
|
|
|
148
156
|
不要在本步骤同步 task 评论;它依赖 lifecycle 写入后的完整终态 task.md。不要设置 `status:` label,平台自动化应在 Issue 关闭后清理状态标签。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{"source":"platform-pr-inspect","diff":"three-dot-find-renames","metrics":["numstat-lines","git-blob-bytes"],"publish":["pr-summary","user-response"]}
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
|
-
After a PR is created or uniquely reused, use the authoritative `base.sha` and `head.sha` returned by `platform-pr inspect` to measure the complete PR diff. Do not substitute the last commit or current worktree state. The task-intent digest covers only the semantic section from `# Task`/`# 任务` through `## Context`/`## 上下文`, so lifecycle logs, receipts, and version metadata do not stale a sidecar for the same head.
|
|
7
|
+
After a PR is created or uniquely reused, use the authoritative `base.sha` and `head.sha` returned by `platform-pr inspect` to measure the complete PR diff. Do not substitute the last commit or current worktree state. The complete PR keeps the existing three-dot, merge-base, patch, and sidecar identity. The task-intent digest covers only the semantic section from `# Task`/`# 任务` through `## Context`/`## 上下文`, so lifecycle logs, receipts, and version metadata do not stale a sidecar for the same head.
|
|
8
8
|
|
|
9
9
|
## Evidence Commands
|
|
10
10
|
|
|
@@ -24,7 +24,15 @@ agent-infra-internal platform-pr change-report {task-id} \
|
|
|
24
24
|
|
|
25
25
|
The core validates the task-intent digest, PR identity, complete patch SHA, totals, and the ordered checks `target-alignment`, `change-composition`, `compatibility-policy`, `legacy-path-cleanup`, `redundancy`, and `scope-discipline`. A `needs-review` check routes to `review-code`; `formalReview` remains `false`.
|
|
26
26
|
|
|
27
|
-
When generating the candidate,
|
|
27
|
+
When generating the candidate, use the six existing `rationale` fields and `Evidence[]` as readable analysis for each logical change group; do not add group fields. For each group, explain its goal and acceptance-criteria alignment, added and removed behavior, necessity, redundancy or duplicate implementation, scope, related tests, and whether the tests are sufficient. Cite paths, line ranges, and concise evidence for the conclusions. Each check must still state its actual finding: `target-alignment` explains the goal relationship, `change-composition` summarizes responsibilities and behavior changes, `compatibility-policy` states the compatibility result, `legacy-path-cleanup` states how legacy paths were handled, `redundancy` states the redundancy result, and `scope-discipline` states the scope judgment. Do not reuse generic boilerplate; mark unsupported conclusions as unconfirmed. Use the deployed skill's language. Chinese skill deployments must use Chinese. The canonical renderer displays every item in each `Evidence[]`.
|
|
28
|
+
|
|
29
|
+
Explain applicable production-code, test, and documentation changes for each group. Keep source line changes, execution/call-count changes, and workflow unification distinct. Diff line counts support text changes only; they do not prove fewer executions or a unified workflow. Execution behavior and test adequacy need their own direct evidence. State gaps where evidence is missing. Green CI does not prove necessity or absence of redundancy.
|
|
30
|
+
|
|
31
|
+
## Complete PR and Per-Round Increment
|
|
32
|
+
|
|
33
|
+
Complete-PR statistics always use the current authoritative snapshot's `base.sha...head.sha` and the existing three-dot mechanical report. The report may separately describe the increment from the last valid canonical sidecar's `identity.head.sha` to the current snapshot's `head.sha`. Before rebuilding the report, validate the prior sidecar with the existing `readPrChangeReport`; use its head only when repository/resource identity matches the current PR. Compare the two trees for the increment with `git diff --find-renames --numstat {previous-head} {current-head}`, `--name-status`, and `--summary`. Do not substitute a merge-base diff for these endpoints or add increment totals to complete-PR totals.
|
|
34
|
+
|
|
35
|
+
If there is no prior sidecar, it is invalid, or its PR identity differs, state that the previous head is unconfirmed. Do not infer per-round numbers from the complete PR or fabricate zeroes. If both SHAs are identical, state that the head did not change and omit extra statistics. After a rebase, compare the old and new head trees directly; this describes tree changes, not commit counts or execution counts. A missing/invalid prior sidecar does not block the complete-PR report.
|
|
28
36
|
|
|
29
37
|
The script calculates both `numstat` line counts and exact Git blob byte sizes between the merge base and head. Bytes provide a stable unit, cover binary files, and reveal same-line compaction; submodules are not blobs and contribute zero bytes. Added files have zero old bytes, deleted files have zero new bytes, pure renames have zero net bytes, and copies count only the new destination content.
|
|
30
38
|
|
|
@@ -41,6 +49,8 @@ Using the repository's actual structure, assign every file to exactly one closes
|
|
|
41
49
|
- Documentation
|
|
42
50
|
- Configuration, dependencies, and other files
|
|
43
51
|
|
|
52
|
+
The canonical summary's fixed categories separate `docs/` and Markdown/MDX/reStructuredText documents from other files. More specific runtime-code, test, template, and `.agents/` categories take precedence; remaining paths go to Other.
|
|
53
|
+
|
|
44
54
|
The table must contain component, file count, added lines, deleted lines, net lines, old bytes, new bytes, and net bytes, followed by a total row. Byte columns describe the blob content size of files involved in this change, not disk usage for the entire repository or worktree. Use exact integers; optional KiB/MiB values may aid readability but must not replace byte counts.
|
|
45
55
|
|
|
46
56
|
Category line and byte totals must each reconcile exactly with the script's `totals`. Then, for text files only, list the largest files by line change and absolute net-byte change, distinguishing source, tests, and template mirrors. Keep binary files in the category totals only; do not rank or interpret their content. Show renames as path changes without inferring a "pure rename" label.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{"source":"platform-pr-inspect","diff":"three-dot-find-renames","metrics":["numstat-lines","git-blob-bytes"],"publish":["pr-summary","user-response"]}
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
|
-
PR 创建或复用成功后,使用 `platform-pr inspect` 返回的权威 `base.sha` 与 `head.sha` 统计完整 PR 差异,不以最后一个 commit
|
|
7
|
+
PR 创建或复用成功后,使用 `platform-pr inspect` 返回的权威 `base.sha` 与 `head.sha` 统计完整 PR 差异,不以最后一个 commit 或工作树状态代替。完整 PR 继续使用下文现有 three-dot、merge-base、patch 与 sidecar 身份。任务意图 digest 只覆盖 `# 任务`/`# Task` 到 `## 上下文`/`## Context` 的语义区间,因此生命周期日志、收据和版本元数据不会使同一 head 的 sidecar 失效。
|
|
8
8
|
|
|
9
9
|
## 证据命令
|
|
10
10
|
|
|
@@ -24,7 +24,15 @@ agent-infra-internal platform-pr change-report {task-id} \
|
|
|
24
24
|
|
|
25
25
|
core 校验任务意图 digest、PR identity、完整 patch SHA、统计合计和固定顺序的 `target-alignment`、`change-composition`、`compatibility-policy`、`legacy-path-cleanup`、`redundancy`、`scope-discipline`。任一项为 `needs-review` 时路由到 `review-code`,`formalReview` 仍为 `false`。
|
|
26
26
|
|
|
27
|
-
生成 candidate
|
|
27
|
+
生成 candidate 时,按逻辑变化组用六项 `rationale` 和 `Evidence[]` 给出可读分析,不新增组级字段。每组说明其目标/验收对应、行为增加与减少、必要性、冗余或重复实现、范围、对应测试及测试是否充分;列出支撑结论的路径、行号和简短证据。六项检查各自仍须写实际结论:`target-alignment` 说明目标对应关系,`change-composition` 概括职责和行为变化,`compatibility-policy` 说明兼容策略,`legacy-path-cleanup` 说明旧路径处理,`redundancy` 说明重复/冗余结果,`scope-discipline` 说明范围判断。不得复用泛化套话;证据不足的维度明确标记未确认,不推断结论。证据详情遵循当前已部署技能的语言,中文技能必须使用中文。canonical renderer 会完整展示每项 `Evidence[]`。
|
|
28
|
+
|
|
29
|
+
每组解释覆盖生产代码、测试和文档中适用的变化,并区分源码行数变化、执行/调用次数变化和流程统一。diff 行数仅支持代码文本增减;不能据此推断执行次数减少或流程统一。执行次数、流程行为和测试充分性须有各自直接证据;没有证据时明确说明缺口。CI 全绿不能代替必要性或冗余判断。
|
|
30
|
+
|
|
31
|
+
## 完整 PR 与本轮增量
|
|
32
|
+
|
|
33
|
+
完整 PR 统计始终使用当前权威 snapshot 的 `base.sha...head.sha` 和现有 three-dot mechanical report。报告可另外描述最近一次有效 canonical sidecar 的 `identity.head.sha` 到当前 snapshot `head.sha` 的本轮增量。重建报告前,使用现有 `readPrChangeReport` 校验先前 sidecar;仅当其 repository/resource identity 与当前 PR 一致时,才把旧 head 作为增量起点。用 `git diff --find-renames --numstat {previous-head} {current-head}`、`--name-status` 和 `--summary` 的两个端点比较本轮树差异,不使用 merge-base 代替这个范围,不把增量 totals 加到完整 PR totals。
|
|
34
|
+
|
|
35
|
+
第一次没有旧 sidecar、旧报告无效或 PR identity 不符时,写明“上一 head 不可确认”,不得由完整 PR 统计推算本轮数字,也不得填造零值。两个 SHA 相同则说明 head 未变化,不额外生成统计。rebase 后仍按旧 head 与新 head 两个树端点比较;结果描述树差异,不代表提交数或执行次数变化。旧 sidecar 缺失/无效不阻断完整 PR 报告生成。
|
|
28
36
|
|
|
29
37
|
脚本在 merge base 与 head 之间同时计算 `numstat` 行数和 Git blob 的精确字节数。字节比字符具有稳定口径,也能覆盖二进制文件和同行内缩减;子模块不是 blob,按 0 字节贡献核算。新增文件的旧字节数为 0,删除文件的新字节数为 0,纯 rename 的净字节数为 0,copy 只计新增目标内容。
|
|
30
38
|
|
|
@@ -41,6 +49,8 @@ core 校验任务意图 digest、PR identity、完整 patch SHA、统计合计
|
|
|
41
49
|
- 文档
|
|
42
50
|
- 配置、依赖与其他
|
|
43
51
|
|
|
52
|
+
canonical 汇总中的固定分类把 `docs/`、Markdown/MDX/reStructuredText 文档与其他文件分别统计;更具体的运行时代码、测试、模板及 `.agents/` 分类优先。其余文件进入“其他”。
|
|
53
|
+
|
|
44
54
|
输出表格必须包含“部分、文件数、新增行、删除行、净增行、旧字节、新字节、净字节”,并有合计行。字节列表示本次变更涉及文件的 blob 内容体积,不表示整个仓库或工作树的磁盘占用。使用精确整数;可附加 KiB/MiB 便于阅读,但不能替代字节数。
|
|
45
55
|
|
|
46
56
|
各类别的行数和字节数之和必须分别与脚本的 `totals` 一致。随后仅针对文本文件,分别列出行数变化和绝对净字节变化最大的文件,并区分源码、测试和模板镜像;二进制文件只保留在分类合计中,不参与代表性文件排序或内容判断。rename 只按路径变化展示,不推断“纯 rename”。
|
|
@@ -63,6 +63,8 @@ agent-infra-internal platform-release-notes context \
|
|
|
63
63
|
|
|
64
64
|
Use `pullRequests` and `commits` from Step 3. Each commit's `authors` contains only that commit's platform authors; `pullRequestNumbers` records the platform's PR associations. The skill does not interpret raw platform fields or email rules. A PR's `closingIssues` contains its actual closing Issues; never infer a relationship from matching PR and Issue numbers.
|
|
65
65
|
|
|
66
|
+
Release notes describe changes actually delivered between the tags. For commits without an associated PR, inspect the actual diff using the `oid` from the typed context to determine whether it records a delivered change or release workflow mechanics. A commit made up only of version metadata updates, generated release artifacts, or other mechanical release preparation/publication work is outside the release's change set and contributor counts. When a commit also contains an independent product, documentation, or maintenance change, retain that change and its authors. If the diff does not establish the commit's purpose, keep it and describe the actual change. Use this same filtered commit set for the classification in Step 6 and contributor counts in Step 7.
|
|
67
|
+
|
|
66
68
|
### 5. Collect Related Issues
|
|
67
69
|
|
|
68
70
|
Use each PR's `closingIssues` from Step 3. Do not parse platform-specific reference syntax in this generic skill.
|
|
@@ -112,7 +114,7 @@ If no historical release notes exist, use the following default Markdown format:
|
|
|
112
114
|
5. **Contributor collection**:
|
|
113
115
|
- **Data sources**:
|
|
114
116
|
- PR authors from the typed context
|
|
115
|
-
- Commit co-authors from
|
|
117
|
+
- Commit co-authors from `authors` of commits retained as release changes in Step 4
|
|
116
118
|
- Issue reporters from typed `closingIssues[].author`
|
|
117
119
|
- **Contribution count**: `PR count + co-authored commit count` for the same identity, merged across both sources
|
|
118
120
|
- **`@login` mapping**: follow the identity safety boundary in `.agents/rules/release-commands.md`
|
|
@@ -63,6 +63,8 @@ agent-infra-internal platform-release-notes context \
|
|
|
63
63
|
|
|
64
64
|
使用步骤 3 返回的 `pullRequests` 与 `commits`。每个 commit 的 `authors` 只包含该 commit 的平台作者;`pullRequestNumbers` 表示平台记录的 PR 关联。技能不读取平台原始字段或邮箱规则。PR 的 `closingIssues` 是该 PR 实际关闭的 Issue,不按 PR/Issue 编号推断关联。
|
|
65
65
|
|
|
66
|
+
发布说明记录标签间实际交付的变更。对于没有关联 PR 的 commit,依据 typed context 中的 `oid` 检查实际 diff,判断它记录的是版本交付还是发布流程本身。仅包含版本元数据更新、生成发布产物或其他版本准备/发布机械操作的 commit 不属于本版本变更,也不计入贡献者;同一 commit 若还包含独立的产品、文档或维护改动,则保留这些改动及其作者。diff 无法说明提交用途时,保留该 commit,并根据实际改动描述。步骤 6 的变更分类和步骤 7 的贡献者统计使用同一筛选结果。
|
|
67
|
+
|
|
66
68
|
### 5. 收集关联 Issue
|
|
67
69
|
|
|
68
70
|
使用步骤 3 中每个 PR 的 `closingIssues`,不在通用技能中解析平台专有引用语法。
|
|
@@ -112,7 +114,7 @@ agent-infra-internal platform-release-notes context \
|
|
|
112
114
|
5. **贡献者搜集**:
|
|
113
115
|
- **数据源**:
|
|
114
116
|
- PR author:来自 `.agents/rules/release-commands.md` 中已合并 PR 查询规则
|
|
115
|
-
- Commit co-authors:来自步骤 3 typed context
|
|
117
|
+
- Commit co-authors:来自步骤 3 typed context 中按步骤 4 纳入版本变更的 commit `authors`
|
|
116
118
|
- Issue reporters:来自步骤 3 typed context 的 `closingIssues[].author`
|
|
117
119
|
- **贡献数定义**:`该人的 PR 数 + 该人作为 co-author 的 commit 数`(同一身份跨来源合并计数)
|
|
118
120
|
- **`@login` 映射**:遵循 `.agents/rules/release-commands.md` 的身份安全边界
|