@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.
Files changed (115) hide show
  1. package/dist/bin/internal-cli.js +2 -7
  2. package/dist/lib/agent-clients/adapter.js +0 -3
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
  6. package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
  7. package/dist/lib/agent-clients/adapters/codex.js +0 -2
  8. package/dist/lib/internal/cli-route-inventory.js +0 -1
  9. package/dist/lib/internal/task-finalization.js +7 -5
  10. package/dist/lib/internal/task-lifecycle.js +3 -1
  11. package/dist/lib/internal/task-operation-registry.js +1 -3
  12. package/dist/lib/internal/task-validate.js +14 -124
  13. package/dist/lib/platform/pr-change-report.js +10 -3
  14. package/dist/lib/platform/release-notes.js +2 -2
  15. package/dist/lib/process-data/index.js +15 -4
  16. package/dist/lib/sandbox/commands/control-status.js +0 -1
  17. package/dist/lib/sandbox/commands/create.js +5 -4
  18. package/dist/lib/sandbox/config.js +12 -0
  19. package/dist/lib/sandbox/control/audit.js +1 -2
  20. package/dist/lib/sandbox/control/client.js +12 -47
  21. package/dist/lib/sandbox/control/executor.js +6 -10
  22. package/dist/lib/sandbox/control/host-recovery.js +89 -27
  23. package/dist/lib/sandbox/control/lifecycle.js +22 -37
  24. package/dist/lib/sandbox/control/protocol.js +8 -36
  25. package/dist/lib/sandbox/control/server.js +2 -21
  26. package/dist/lib/sandbox/environment.js +7 -8
  27. package/dist/lib/sandbox/mounts.js +71 -59
  28. package/dist/lib/sandbox/recovery.js +2 -3
  29. package/dist/lib/sandbox/removal.js +19 -4
  30. package/dist/lib/sandbox/workspace-identity.js +30 -15
  31. package/dist/lib/sandbox/workspace-view.js +41 -4
  32. package/dist/lib/task/control-authority.js +39 -17
  33. package/dist/lib/task/control-recovery.js +1 -2
  34. package/dist/lib/task/finalization.js +13 -2
  35. package/dist/lib/task/lifecycle.js +122 -35
  36. package/dist/lib/task/short-id.js +2 -2
  37. package/dist/lifecycle-build-manifest.json +3 -3
  38. package/lib/agent-clients/adapter.ts +0 -27
  39. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
  40. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
  41. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
  42. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
  43. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
  44. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
  45. package/lib/agent-clients/adapters/codex.ts +0 -2
  46. package/lib/internal/cli-route-inventory.ts +0 -1
  47. package/lib/internal/task-finalization.ts +8 -6
  48. package/lib/internal/task-lifecycle.ts +3 -1
  49. package/lib/internal/task-operation-registry.ts +1 -3
  50. package/lib/internal/task-validate.ts +13 -127
  51. package/lib/platform/pr-change-report.ts +9 -3
  52. package/lib/platform/release-notes.ts +2 -2
  53. package/lib/process-data/index.ts +15 -4
  54. package/lib/sandbox/commands/control-status.ts +0 -1
  55. package/lib/sandbox/commands/create.ts +5 -4
  56. package/lib/sandbox/config.ts +13 -0
  57. package/lib/sandbox/control/audit.ts +1 -2
  58. package/lib/sandbox/control/client.ts +13 -61
  59. package/lib/sandbox/control/executor.ts +7 -13
  60. package/lib/sandbox/control/host-recovery.ts +94 -21
  61. package/lib/sandbox/control/lifecycle.ts +22 -37
  62. package/lib/sandbox/control/protocol.ts +9 -45
  63. package/lib/sandbox/control/server.ts +2 -17
  64. package/lib/sandbox/environment.ts +7 -8
  65. package/lib/sandbox/mounts.ts +92 -75
  66. package/lib/sandbox/recovery.ts +2 -3
  67. package/lib/sandbox/removal.ts +16 -4
  68. package/lib/sandbox/workspace-identity.ts +36 -13
  69. package/lib/sandbox/workspace-view.ts +40 -5
  70. package/lib/task/control-authority.ts +40 -26
  71. package/lib/task/control-recovery.ts +2 -3
  72. package/lib/task/finalization.ts +15 -2
  73. package/lib/task/lifecycle.ts +135 -33
  74. package/lib/task/short-id.ts +5 -2
  75. package/package.json +1 -1
  76. package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
  77. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
  78. package/templates/.agents/rules/task-short-id.en.md +18 -12
  79. package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
  80. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
  81. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
  82. package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
  83. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
  84. package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
  85. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
  86. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
  87. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
  88. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
  89. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
  90. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  91. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  92. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  93. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  94. package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
  95. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
  96. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
  97. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
  98. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
  99. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
  100. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
  101. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
  102. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
  103. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
  104. package/dist/lib/runtime/agent-runtime.js +0 -33
  105. package/dist/lib/sandbox/control/agent-operation.js +0 -34
  106. package/dist/lib/task/runtime-paths.js +0 -10
  107. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
  108. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
  109. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
  110. package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
  111. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
  112. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
  113. package/lib/runtime/agent-runtime.ts +0 -41
  114. package/lib/sandbox/control/agent-operation.ts +0 -49
  115. package/lib/task/runtime-paths.ts +0 -12
@@ -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 = assertRuntimeMoveSafe();
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 = assertRuntimeMoveSafe();
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 { fs.rmdirSync(activeTaskPath); }
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,
@@ -464,7 +464,10 @@ function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
464
464
  return map;
465
465
  }
466
466
 
467
- function inspectShortIdRegistry(repoRoot: string): ShortIdRegistryInspection {
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, configuredShortIdLength(repoRoot));
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.13",
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, stop the wrapper/controller, and preserve the worktree, task/run/orchestration state, receipts, registration, and audit. For an unknown result, inspect local `status`/`route` before using existing recover/advance semantics; do not replay an old broker request.
32
- - Deploy or roll back the CLI, broker, controller adapter, and protocol as one versioned unit. After stopping the old container/broker, rebuild and run `ai sandbox start --recreate <task-ref>` to resume with a new control generation; never reuse an old token, proof, or request.
33
- - On either the new or rolled-back version, first confirm the same task through `status`/`route`, then require a successful typed controller `open`→`verify`. Keep the quiet window and evidence intact if any check fails; do not mix component versions or rewrite domain facts.
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 返回,停止 wrapper/controller,并保留 worktree、task/run/orchestration 状态、receipts、registration 与 audit。结果未知时先用本地 `status`/`route` 判断 domain 是否已提交,再按现有 recover/advance 语义处理;旧 broker request 不重放。
32
- - CLI、broker、controller adapter 和协议必须作为同一版本单元部署或回退。停止旧 container/broker 后执行 rebuild 与 `ai sandbox start --recreate <task-ref>`,以新 control generation 恢复执行;旧 token、proof 和 request 不得复用。
33
- - 新版本或回退版本都必须先核对同一任务的 `status`/`route`,再要求 controller typed `open`→`verify` 成功。任一检查失败时继续保持静默并保留证据,不局部混用组件或改写 domain 事实。
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 | `complete-task`, `cancel-task`, `block-task`, `close-codescan`, `close-dependabot` | Removes the registry entry. |
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
- Short ids are valid only while a task lives in `.agents/workspace/active/`.
31
- Once it is moved to `completed/`, `blocked/`, or `archive/`, the `NN` slot is
32
- freed and may be reused by a new task.
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 full 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 |
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: it reports discrepancies between the
58
- active dir and the registry, but never writes.
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` / `list` / `release` never allocate —
98
- they only clean up stale entries pointing at non-active tasks.
99
- - After archive (complete-task / cancel-task / block-task / close-*) the
100
- registry entry is deleted immediately and the short id may be reused; archived
101
- tasks are referenced by their full `TASK-…` id.
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 | `complete-task`、`cancel-task`、`block-task`、`close-codescan`、`close-dependabot` | 从注册表移除。 |
22
+ | release | sandbox cleanup 成功;以及 `cancel-task`、`block-task`、`close-codescan`、`close-dependabot` | 从注册表移除。 |
23
23
  | re-alloc | `restore-task` | 重新分配(可能与历史不同),写入注册表。 |
24
24
 
25
- 短号仅在任务处于 `.agents/workspace/active/` 期间有效;任务移动到
26
- `completed/` / `blocked/` / `archive/` 后短号立即释放,可被新任务复用。
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>` | 解析为完整 task id 后查 task.md 取 `branch` | **严格报错** —— 不再回退到 ls 行号或字面分支名;提示用任务短号 / `TASK-id` / 分支名 |
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` 严格只读:报告 active 目录 / 注册表 两者差异,但不修改任何状态。
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` / `list` / `release` 不分配,仅在执行时自动清理指向非 active 任务的 stale entry
82
- - 归档(complete-task / cancel-task / block-task / close-*)后注册表 entry 立即删除,短号可被新任务复用;归档后引用任务一律用完整 `TASK-…` id
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 and archive it.
5
- Use when a task's work is done and verified and you want to close and archive it.
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, each check's `rationale` must state its actual finding: `target-alignment` explains the goal relationship, `change-composition` summarizes where new code concentrates responsibilities, `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 phrases such as "within scope" across checks; each check must provide at least one directly relevant path, line range, and concise `detail`, using the deployed skill's language. Chinese skill deployments must use Chinese. These high-level rationales and evidence are rendered in the canonical summary.
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 或工作树状态代替。任务意图 digest 只覆盖 `# 任务`/`# Task` 到 `## 上下文`/`## Context` 的语义区间,因此生命周期日志、收据和版本元数据不会使同一 head 的 sidecar 失效。
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 时,六项 `rationale` 必须分别写出实际检查结论:`target-alignment` 说明目标对应关系,`change-composition` 概括新增代码集中承担的职责,`compatibility-policy` 说明兼容策略,`legacy-path-cleanup` 说明旧路径处理,`redundancy` 说明冗余检查结果,`scope-discipline` 说明范围判断。不得六项复用“符合范围”等泛化套话;每项至少提供一条直接支持结论的路径、行号和简短 `detail`,并遵循当前已部署技能的语言。中文技能必须使用中文。这些高层理由和证据会直接展示在 canonical 摘要中。
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 the typed context's commit `authors`
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 的 commit `authors`
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` 的身份安全边界