kiro-spec-engine 1.47.6 → 1.47.7

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.
@@ -0,0 +1,41 @@
1
+ # 331-poc 适配持续推进路线图(KSE 侧)
2
+
3
+ > 范围:`kiro-spec-engine` 侧围绕 331-poc handoff 的持续适配工作,不包含 331 业务实现本身。
4
+
5
+ ## 已完成(本轮)
6
+
7
+ 1. 新增 handoff 自动化命令:
8
+ - `kse auto handoff plan`
9
+ - `kse auto handoff queue`
10
+ 2. 将 handoff manifest 解析为可执行阶段计划(precheck/spec validation/execution/observability)。
11
+ 3. 将 handoff manifest 生成 close-loop-batch 目标队列(支持 dry-run、append、known-gaps 开关)。
12
+ 4. 补齐单测覆盖(plan/queue/dry-run 分支)。
13
+ 5. 更新命令参考与中英文文档入口。
14
+
15
+ ## 下一阶段(P1)
16
+
17
+ 1. 新增 `kse auto handoff run`:
18
+ - 一条命令串行执行 `plan -> queue -> close-loop-batch -> observability`。
19
+ - 支持 `--dry-run` 与失败自动中断。
20
+ 2. 新增 handoff 结果归档:
21
+ - 输出 `.kiro/reports/handoff-runs/<session>.json`。
22
+ - 汇总每个 spec 的校验状态与阻塞项。
23
+ 3. 增加 handoff 门禁策略:
24
+ - `--min-spec-success-rate`
25
+ - `--max-risk-level`
26
+ - `--require-ontology-validation`
27
+
28
+ ## 中期增强(P2)
29
+
30
+ 1. 将 handoff 批次映射为主从 agent 编排输入:
31
+ - 根据 spec 依赖自动分批并发。
32
+ 2. 引入 template 差异检测:
33
+ - 对比 handoff 模板与本地模板库差异。
34
+ 3. 增加跨轮次回归分析:
35
+ - 对比本轮与上轮 handoff 的质量、风险与收敛速度。
36
+
37
+ ## 长期目标(P3)
38
+
39
+ 1. 抽象成通用“外部项目 handoff 适配框架”(不只 331-poc)。
40
+ 2. 与 release evidence 合并,形成可发布的治理报表。
41
+ 3. 将 handoff 质量指标纳入 `kse auto governance close-loop` 的默认评估维度。
@@ -0,0 +1,106 @@
1
+ # 331-poc 双轨协同对接手册
2
+
3
+ > 目的:在 `kiro-spec-engine` 侧承接 331-poc 的深度补全成果,完成模板接入、ontology 校验、主从编排与闭环验收。
4
+
5
+ ## 1. 输入契约(来自 331-poc)
6
+
7
+ 每一轮从 `E:\workspace\331-poc` 接收以下产物:
8
+
9
+ 1. 完成态 Spec(requirements/design/tasks + `custom/scene.yaml` + `custom/scene-package.json`)。
10
+ 2. 模板导出目录:`.kiro/templates/exports/<template-name>/`。
11
+ 3. 交接包:`docs/handoffs/handoff-manifest.json` + 证据文档。
12
+
13
+ 若输入不满足以上三类,KSE 侧不进入接入批次。
14
+
15
+ ## 2. KSE 侧接入流程
16
+
17
+ ## 2.0 一键化入口(推荐)
18
+
19
+ 先基于 handoff manifest 生成计划和队列,再执行批次:
20
+
21
+ ```bash
22
+ npx kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/reports/handoff-plan.json --json
23
+ npx kse auto handoff queue --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/auto/handoff-goals.lines --json
24
+ npx kse auto close-loop-batch .kiro/auto/handoff-goals.lines --format lines --batch-autonomous --continue-on-error --json
25
+ ```
26
+
27
+ ## 2.1 批次 1:输入预检
28
+
29
+ ```bash
30
+ npx kse status --verbose
31
+ npx kse doctor --docs
32
+ ```
33
+
34
+ 核对 `handoff-manifest.json`:
35
+ 1. `specs[]` 非空。
36
+ 2. `templates[]` 非空。
37
+ 3. `ontology_validation` 有最近一次执行记录。
38
+
39
+ ## 2.2 批次 2:scene 包验证与 ontology 回归
40
+
41
+ 对每个 handoff spec 执行:
42
+
43
+ ```bash
44
+ npx kse scene package-validate --spec <spec-name> --spec-package custom/scene-package.json --strict --json
45
+ npx kse scene ontology validate --package .kiro/specs/<spec-name>/custom --json
46
+ npx kse scene ontology impact --package .kiro/specs/<spec-name>/custom --ref <ref> --max-depth 2 --json
47
+ npx kse scene ontology path --package .kiro/specs/<spec-name>/custom --from <from-ref> --to <to-ref> --json
48
+ ```
49
+
50
+ ## 2.3 批次 3:模板层接入
51
+
52
+ 对于 scene package 模板链路(如果本轮包含 scene-package 模板):
53
+
54
+ ```bash
55
+ npx kse scene package-publish --spec <spec-name> --out-dir .kiro/templates/scene-packages --json --force
56
+ npx kse scene package-registry --template-dir .kiro/templates/scene-packages --strict --json
57
+ npx kse scene package-gate-template --out .kiro/templates/scene-package-gate-policy.json --profile three-layer --force --json
58
+ npx kse scene package-gate --registry .kiro/templates/scene-packages/registry.json --policy .kiro/templates/scene-package-gate-policy.json --strict --json
59
+ ```
60
+
61
+ 说明:Spec 模板(`templates create-from-spec` 导出)与 scene package 模板是两条链路,可并行维护,但推荐在同一交接批次一并校验。
62
+
63
+ ## 2.4 批次 4:主从编排闭环验证
64
+
65
+ 基于本轮 spec 组合,执行自动闭环 dry-run/正式 run:
66
+
67
+ ```bash
68
+ npx kse auto close-loop --specs "<spec-a>,<spec-b>,<spec-c>" --dry-run --json
69
+ npx kse auto close-loop --specs "<spec-a>,<spec-b>,<spec-c>" --json
70
+ npx kse auto observability snapshot --json
71
+ ```
72
+
73
+ 判定标准:
74
+ 1. 编排成功率满足门槛。
75
+ 2. 不出现高风险未处置项。
76
+ 3. observability 快照可追踪到本轮变更。
77
+
78
+ ## 3. KSE 侧当前需持续适配的点
79
+
80
+ 1. 对接自动化:把 331 handoff manifest 解析为可执行批次计划。
81
+ 2. ontology 深化:将“业务规则/决策逻辑”映射为可量化 gate 指标。
82
+ 3. 多 spec 主从调度:按依赖图自动分批并控制并行度。
83
+ 4. 发布治理:把 handoff 批次结果写入统一 release evidence。
84
+
85
+ ## 4. 角色划分
86
+
87
+ 1. 331-poc Agent:交付业务事实、完成态 Spec、模板和证据。
88
+ 2. KSE Agent:执行接入验证、治理门禁、主从闭环与发布收口。
89
+ 3. Master Agent:维护跨仓上下文与批次节奏。
90
+
91
+ ## 5. 失败处理
92
+
93
+ 1. 输入缺失:退回 331-poc,标注缺失项,不进入 KSE 接入。
94
+ 2. ontology 校验失败:先修 ref/lineage,再跑 gate。
95
+ 3. 主从编排失败:降并发、缩批次、保留快照后重试。
96
+
97
+ ## 6. 最终交付口径
98
+
99
+ 每个接入批次结束后,KSE 侧必须输出:
100
+ 1. 接入批次执行记录(命令、结果、失败点)。
101
+ 2. gate 与 observability 快照。
102
+ 3. 下一批次建议(可直接执行)。
103
+
104
+ ## 7. 持续推进参考
105
+
106
+ KSE 侧后续增强计划见:`docs/331-poc-adaptation-roadmap.md`
package/docs/README.md CHANGED
@@ -90,6 +90,8 @@ Detailed technical documentation:
90
90
  - **[Multi-Repository Management Guide](multi-repo-management-guide.md)** - Managing multiple Git repositories
91
91
  - **[Value Observability Guide](value-observability-guide.md)** - KPI snapshot, baseline, trend, and gate evidence workflow
92
92
  - **[Scene Runtime Guide](scene-runtime-guide.md)** - Scene template engine, quality pipeline, ontology, and Moqui ERP integration
93
+ - **[331-poc Dual-Track Integration Guide](331-poc-dual-track-integration-guide.md)** - Handoff contract and integration playbook between 331-poc and kse
94
+ - **[331-poc Adaptation Roadmap](331-poc-adaptation-roadmap.md)** - Ongoing KSE-side adaptation backlog and rollout phases
93
95
  - **[Multi-Agent Coordination Guide](multi-agent-coordination-guide.md)** - Multi-agent parallel coordination for concurrent development
94
96
  - **[Troubleshooting](troubleshooting.md)** - Solutions to common problems
95
97
  - **[FAQ](faq.md)** - Answers to frequently asked questions
@@ -184,6 +186,8 @@ Detailed technical documentation:
184
186
 
185
187
  ### Scene Runtime
186
188
  - [Scene Runtime Guide](scene-runtime-guide.md)
189
+ - [331-poc Dual-Track Integration Guide](331-poc-dual-track-integration-guide.md)
190
+ - [331-poc Adaptation Roadmap](331-poc-adaptation-roadmap.md)
187
191
  - [Scene Template Engine](command-reference.md#scene-template-engine)
188
192
  - [Scene Quality Pipeline](command-reference.md#scene-template-quality-pipeline)
189
193
  - [Scene Ontology](command-reference.md#scene-ontology-enhancement)
@@ -508,6 +508,12 @@ kse auto schema check --json
508
508
  kse auto schema migrate --json # dry-run by default
509
509
  kse auto schema migrate --apply --json # apply schema_version migration
510
510
  kse auto schema migrate --only close-loop-session,batch-session --apply --json
511
+
512
+ # Dual-track handoff integration (e.g., 331-poc -> kse)
513
+ kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --json
514
+ kse auto handoff plan --manifest ../331-poc/docs/handoffs/handoff-manifest.json --strict --out .kiro/reports/handoff-plan.json --json
515
+ kse auto handoff queue --manifest ../331-poc/docs/handoffs/handoff-manifest.json --out .kiro/auto/handoff-goals.lines --json
516
+ kse auto close-loop-batch .kiro/auto/handoff-goals.lines --format lines --batch-autonomous --continue-on-error --json
511
517
  ```
512
518
 
513
519
  DoD-related options:
@@ -738,6 +744,10 @@ Autonomous archive schema compatibility:
738
744
  - `kse auto schema migrate [--only <scopes>] [--target-version <version>] [--apply] [--json]`: migrate/backfill `schema_version` across autonomous archives.
739
745
  - Default mode is dry-run; use `--apply` to persist changes.
740
746
 
747
+ Dual-track handoff integration:
748
+ - `kse auto handoff plan --manifest <path> [--out <path>] [--strict] [--strict-warnings] [--json]`: parse handoff manifest (source project, specs, templates, known gaps) and generate an executable KSE integration phase plan.
749
+ - `kse auto handoff queue --manifest <path> [--out <path>] [--append] [--no-include-known-gaps] [--dry-run] [--json]`: generate close-loop batch goal queue from handoff manifest and optionally persist line-based queue file (default `.kiro/auto/handoff-goals.lines`).
750
+
741
751
  Recommended `.kiro/config/orchestrator.json`:
742
752
 
743
753
  ```json
package/docs/zh/README.md CHANGED
@@ -171,6 +171,18 @@
171
171
  - Ontology 图、Action Abstraction、Data Lineage、Agent Hints
172
172
  - Moqui ERP 连接、发现、模板提取
173
173
 
174
+ ### [331-poc 双轨协同对接手册](../331-poc-dual-track-integration-guide.md)
175
+ **331-poc 与 kse 协同执行指南** - 交接契约、接入命令链、主从闭环验收
176
+ - 331 深化成果输入约束
177
+ - KSE 侧模板/ontology/gate 验证流程
178
+ - 双仓协同失败回退策略
179
+
180
+ ### [331-poc 适配路线图](../331-poc-adaptation-roadmap.md)
181
+ **KSE 侧持续适配清单** - 已完成、下一阶段、中期增强与长期目标
182
+ - handoff 自动化命令演进
183
+ - 主从编排与门禁增强
184
+ - 跨轮次回归与发布治理集成
185
+
174
186
  ### [Value 可观测指南](value-observability-guide.md)
175
187
  **KPI 量化交付指南** - 快照、基线、趋势、门禁证据
176
188
  - 周度 KPI 快照生成
@@ -12,6 +12,7 @@ const chalk = require('chalk');
12
12
 
13
13
  const AUTO_ARCHIVE_SCHEMA_VERSION = '1.0';
14
14
  const AUTO_ARCHIVE_SCHEMA_SUPPORTED_VERSIONS = new Set([AUTO_ARCHIVE_SCHEMA_VERSION]);
15
+ const AUTO_HANDOFF_DEFAULT_QUEUE_FILE = '.kiro/auto/handoff-goals.lines';
15
16
 
16
17
  /**
17
18
  * Register auto commands
@@ -1349,6 +1350,86 @@ function registerAutoCommands(program) {
1349
1350
  }
1350
1351
  });
1351
1352
 
1353
+ const autoHandoff = auto
1354
+ .command('handoff')
1355
+ .description('Plan and stage dual-track handoff integration workflows');
1356
+
1357
+ autoHandoff
1358
+ .command('plan')
1359
+ .description('Build an executable KSE integration plan from a handoff manifest JSON')
1360
+ .requiredOption('--manifest <path>', 'Path to handoff-manifest.json')
1361
+ .option('--out <path>', 'Write generated integration plan JSON to file')
1362
+ .option('--strict', 'Fail when manifest validation contains errors')
1363
+ .option('--strict-warnings', 'Fail when manifest validation contains warnings')
1364
+ .option('--json', 'Output machine-readable JSON')
1365
+ .action(async (options) => {
1366
+ try {
1367
+ const result = await buildAutoHandoffPlan(process.cwd(), options);
1368
+ if (options.out) {
1369
+ await maybeWriteOutput(result, options.out, process.cwd());
1370
+ }
1371
+ if (options.json) {
1372
+ console.log(JSON.stringify(result, null, 2));
1373
+ return;
1374
+ }
1375
+ console.log(chalk.blue('Auto handoff integration plan:'));
1376
+ console.log(chalk.gray(` Manifest: ${result.manifest_path}`));
1377
+ console.log(chalk.gray(` Source project: ${result.source_project || 'unknown'}`));
1378
+ console.log(chalk.gray(` Specs: ${result.handoff.spec_count}`));
1379
+ console.log(chalk.gray(` Templates: ${result.handoff.template_count}`));
1380
+ console.log(chalk.gray(` Validation errors: ${result.validation.errors.length}`));
1381
+ console.log(chalk.gray(` Validation warnings: ${result.validation.warnings.length}`));
1382
+ console.log(chalk.gray(` Phases: ${result.phases.length}`));
1383
+ if (result.output_file) {
1384
+ console.log(chalk.gray(` Output: ${result.output_file}`));
1385
+ }
1386
+ } catch (error) {
1387
+ if (options.json) {
1388
+ console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
1389
+ } else {
1390
+ console.error(chalk.red(`Error: ${error.message}`));
1391
+ }
1392
+ process.exit(1);
1393
+ }
1394
+ });
1395
+
1396
+ autoHandoff
1397
+ .command('queue')
1398
+ .description('Generate close-loop queue goals from a handoff manifest JSON')
1399
+ .requiredOption('--manifest <path>', 'Path to handoff-manifest.json')
1400
+ .option('--out <path>', `Queue output file (default: ${AUTO_HANDOFF_DEFAULT_QUEUE_FILE})`, AUTO_HANDOFF_DEFAULT_QUEUE_FILE)
1401
+ .option('--append', 'Append generated goals to existing queue file')
1402
+ .option('--no-include-known-gaps', 'Exclude known_gaps entries from generated queue goals')
1403
+ .option('--dry-run', 'Preview generated queue goals without writing file')
1404
+ .option('--json', 'Output machine-readable JSON')
1405
+ .action(async (options) => {
1406
+ try {
1407
+ const result = await buildAutoHandoffQueue(process.cwd(), options);
1408
+ if (!result.dry_run) {
1409
+ await writeAutoHandoffQueueFile(process.cwd(), result, options);
1410
+ }
1411
+ if (options.json) {
1412
+ console.log(JSON.stringify(result, null, 2));
1413
+ return;
1414
+ }
1415
+ console.log(chalk.blue('Auto handoff queue generated:'));
1416
+ console.log(chalk.gray(` Manifest: ${result.manifest_path}`));
1417
+ console.log(chalk.gray(` Goals: ${result.goal_count}`));
1418
+ console.log(chalk.gray(` Include known gaps: ${result.include_known_gaps ? 'yes' : 'no'}`));
1419
+ console.log(chalk.gray(` Mode: ${result.dry_run ? 'dry-run' : (result.append ? 'append' : 'overwrite')}`));
1420
+ if (result.output_file) {
1421
+ console.log(chalk.gray(` Queue file: ${result.output_file}`));
1422
+ }
1423
+ } catch (error) {
1424
+ if (options.json) {
1425
+ console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
1426
+ } else {
1427
+ console.error(chalk.red(`Error: ${error.message}`));
1428
+ }
1429
+ process.exit(1);
1430
+ }
1431
+ });
1432
+
1352
1433
  const autoSchema = auto
1353
1434
  .command('schema')
1354
1435
  .description('Check and migrate autonomous archive schema compatibility');
@@ -5763,6 +5844,418 @@ async function maybeWriteTextOutput(result, content, outCandidate, projectPath)
5763
5844
  result.output_file = outputPath;
5764
5845
  }
5765
5846
 
5847
+ function normalizeAutoHandoffManifestPath(projectPath, manifestCandidate) {
5848
+ const candidate = typeof manifestCandidate === 'string'
5849
+ ? manifestCandidate.trim()
5850
+ : '';
5851
+ if (!candidate) {
5852
+ throw new Error('handoff manifest path is required');
5853
+ }
5854
+ return path.isAbsolute(candidate)
5855
+ ? candidate
5856
+ : path.join(projectPath, candidate);
5857
+ }
5858
+
5859
+ function toAutoHandoffCliPath(projectPath, absolutePath) {
5860
+ const relative = path.relative(projectPath, absolutePath);
5861
+ if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
5862
+ return relative.split(path.sep).join('/');
5863
+ }
5864
+ return absolutePath;
5865
+ }
5866
+
5867
+ function quoteCliArg(value) {
5868
+ const raw = `${value || ''}`;
5869
+ if (raw.length === 0) {
5870
+ return '""';
5871
+ }
5872
+ if (!/[\s"'`]/.test(raw)) {
5873
+ return raw;
5874
+ }
5875
+ return `"${raw.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
5876
+ }
5877
+
5878
+ function normalizeHandoffText(value) {
5879
+ if (typeof value === 'string') {
5880
+ const normalized = value.trim();
5881
+ return normalized.length > 0 ? normalized : null;
5882
+ }
5883
+ if (typeof value === 'number' && Number.isFinite(value)) {
5884
+ return `${value}`;
5885
+ }
5886
+ return null;
5887
+ }
5888
+
5889
+ function readHandoffPathValue(input, keyPath) {
5890
+ if (!input || typeof input !== 'object') {
5891
+ return null;
5892
+ }
5893
+ const parts = String(keyPath || '')
5894
+ .split('.')
5895
+ .map(part => part.trim())
5896
+ .filter(Boolean);
5897
+ if (parts.length === 0) {
5898
+ return null;
5899
+ }
5900
+ let cursor = input;
5901
+ for (const part of parts) {
5902
+ if (!cursor || typeof cursor !== 'object' || !(part in cursor)) {
5903
+ return null;
5904
+ }
5905
+ cursor = cursor[part];
5906
+ }
5907
+ return cursor;
5908
+ }
5909
+
5910
+ function normalizeHandoffIdentifier(entry, fieldCandidates = []) {
5911
+ const directText = normalizeHandoffText(entry);
5912
+ if (directText) {
5913
+ return directText;
5914
+ }
5915
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
5916
+ return null;
5917
+ }
5918
+
5919
+ for (const field of fieldCandidates) {
5920
+ const value = readHandoffPathValue(entry, field);
5921
+ const normalized = normalizeHandoffText(value);
5922
+ if (normalized) {
5923
+ return normalized;
5924
+ }
5925
+ }
5926
+
5927
+ return null;
5928
+ }
5929
+
5930
+ function collectUniqueIdentifiers(rawEntries, fieldCandidates, label) {
5931
+ const warnings = [];
5932
+ if (rawEntries === undefined || rawEntries === null) {
5933
+ return { values: [], warnings };
5934
+ }
5935
+ if (!Array.isArray(rawEntries)) {
5936
+ return {
5937
+ values: [],
5938
+ warnings: [`${label} must be an array`]
5939
+ };
5940
+ }
5941
+
5942
+ const values = [];
5943
+ const seen = new Set();
5944
+ rawEntries.forEach((entry, index) => {
5945
+ const normalized = normalizeHandoffIdentifier(entry, fieldCandidates);
5946
+ if (!normalized) {
5947
+ warnings.push(`${label}[${index}] is invalid and was ignored`);
5948
+ return;
5949
+ }
5950
+ if (seen.has(normalized)) {
5951
+ return;
5952
+ }
5953
+ seen.add(normalized);
5954
+ values.push(normalized);
5955
+ });
5956
+
5957
+ return { values, warnings };
5958
+ }
5959
+
5960
+ function collectKnownGaps(rawKnownGaps) {
5961
+ const warnings = [];
5962
+ if (rawKnownGaps === undefined || rawKnownGaps === null) {
5963
+ return { gaps: [], warnings };
5964
+ }
5965
+ if (!Array.isArray(rawKnownGaps)) {
5966
+ return {
5967
+ gaps: [],
5968
+ warnings: ['known_gaps must be an array']
5969
+ };
5970
+ }
5971
+
5972
+ const gaps = [];
5973
+ rawKnownGaps.forEach((entry, index) => {
5974
+ const normalized = normalizeHandoffIdentifier(entry, [
5975
+ 'gap',
5976
+ 'title',
5977
+ 'description',
5978
+ 'message',
5979
+ 'name',
5980
+ 'id'
5981
+ ]);
5982
+ if (!normalized) {
5983
+ warnings.push(`known_gaps[${index}] is invalid and was ignored`);
5984
+ return;
5985
+ }
5986
+ gaps.push(normalized);
5987
+ });
5988
+ return { gaps, warnings };
5989
+ }
5990
+
5991
+ function normalizeAutoHandoffManifest(payload = {}) {
5992
+ const validationErrors = [];
5993
+ const validationWarnings = [];
5994
+
5995
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
5996
+ throw new Error('handoff manifest must be a JSON object');
5997
+ }
5998
+
5999
+ const sourceProject = normalizeHandoffText(payload.source_project);
6000
+ if (!sourceProject) {
6001
+ validationWarnings.push('source_project is missing');
6002
+ }
6003
+
6004
+ const timestamp = normalizeHandoffText(payload.timestamp);
6005
+ if (!timestamp) {
6006
+ validationWarnings.push('timestamp is missing');
6007
+ }
6008
+
6009
+ const specsCollected = collectUniqueIdentifiers(
6010
+ payload.specs,
6011
+ ['name', 'spec', 'spec_name', 'spec_id', 'id', 'spec.name', 'spec.id'],
6012
+ 'specs'
6013
+ );
6014
+ validationWarnings.push(...specsCollected.warnings);
6015
+ if (specsCollected.values.length === 0) {
6016
+ validationErrors.push('specs must include at least one valid spec identifier');
6017
+ }
6018
+
6019
+ const templatesCollected = collectUniqueIdentifiers(
6020
+ payload.templates,
6021
+ ['name', 'template', 'template_name', 'id', 'template.id', 'template.name'],
6022
+ 'templates'
6023
+ );
6024
+ validationWarnings.push(...templatesCollected.warnings);
6025
+ if (templatesCollected.values.length === 0) {
6026
+ validationWarnings.push('templates is empty');
6027
+ }
6028
+
6029
+ const knownGapCollected = collectKnownGaps(payload.known_gaps);
6030
+ validationWarnings.push(...knownGapCollected.warnings);
6031
+
6032
+ const ontologyValidation = payload.ontology_validation && typeof payload.ontology_validation === 'object'
6033
+ ? payload.ontology_validation
6034
+ : null;
6035
+ if (!ontologyValidation) {
6036
+ validationWarnings.push('ontology_validation is missing');
6037
+ }
6038
+
6039
+ const nextBatch = payload.next_batch && typeof payload.next_batch === 'object'
6040
+ ? payload.next_batch
6041
+ : null;
6042
+
6043
+ return {
6044
+ source_project: sourceProject,
6045
+ timestamp,
6046
+ specs: specsCollected.values,
6047
+ templates: templatesCollected.values,
6048
+ known_gaps: knownGapCollected.gaps,
6049
+ ontology_validation: ontologyValidation,
6050
+ next_batch: nextBatch,
6051
+ validation: {
6052
+ errors: validationErrors,
6053
+ warnings: validationWarnings
6054
+ }
6055
+ };
6056
+ }
6057
+
6058
+ async function loadAutoHandoffManifest(projectPath, manifestCandidate) {
6059
+ const manifestPath = normalizeAutoHandoffManifestPath(projectPath, manifestCandidate);
6060
+ if (!await fs.pathExists(manifestPath)) {
6061
+ throw new Error(`handoff manifest not found: ${manifestPath}`);
6062
+ }
6063
+
6064
+ let payload;
6065
+ try {
6066
+ payload = await fs.readJson(manifestPath);
6067
+ } catch (error) {
6068
+ throw new Error(`invalid handoff manifest JSON: ${manifestPath} (${error.message})`);
6069
+ }
6070
+
6071
+ const normalized = normalizeAutoHandoffManifest(payload);
6072
+ return {
6073
+ manifest_path: manifestPath,
6074
+ manifest_file: toAutoHandoffCliPath(projectPath, manifestPath),
6075
+ ...normalized
6076
+ };
6077
+ }
6078
+
6079
+ function buildAutoHandoffPhaseCommands(projectPath, manifestPath, specs = []) {
6080
+ const manifestCli = quoteCliArg(toAutoHandoffCliPath(projectPath, manifestPath));
6081
+ const phases = [];
6082
+
6083
+ phases.push({
6084
+ id: 'precheck',
6085
+ title: 'Precheck',
6086
+ goal: 'Validate handoff manifest integrity and repository readiness',
6087
+ commands: [
6088
+ `kse auto handoff plan --manifest ${manifestCli} --json`,
6089
+ 'kse auto governance stats --json'
6090
+ ]
6091
+ });
6092
+
6093
+ const specCommands = [];
6094
+ for (const specName of specs) {
6095
+ const specArg = quoteCliArg(specName);
6096
+ const specPackagePath = quoteCliArg(`.kiro/specs/${specName}/custom`);
6097
+ specCommands.push(`kse auto spec status ${specArg} --json`);
6098
+ specCommands.push(`kse auto spec instructions ${specArg} --json`);
6099
+ specCommands.push(`kse scene package-validate --spec ${specArg} --spec-package custom/scene-package.json --strict --json`);
6100
+ specCommands.push(`kse scene ontology validate --package ${specPackagePath} --json`);
6101
+ }
6102
+ phases.push({
6103
+ id: 'spec-validation',
6104
+ title: 'Spec Validation',
6105
+ goal: 'Validate spec docs, tasks, scene package contract, and ontology consistency',
6106
+ commands: specCommands
6107
+ });
6108
+
6109
+ const queueCli = quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE);
6110
+ phases.push({
6111
+ id: 'execution',
6112
+ title: 'Autonomous Execution',
6113
+ goal: 'Generate queue goals and run autonomous close-loop batch integration',
6114
+ commands: [
6115
+ `kse auto handoff queue --manifest ${manifestCli} --out ${queueCli} --json`,
6116
+ `kse auto close-loop-batch ${queueCli} --format lines --batch-autonomous --continue-on-error --json`
6117
+ ]
6118
+ });
6119
+
6120
+ phases.push({
6121
+ id: 'observability',
6122
+ title: 'Observability and Governance',
6123
+ goal: 'Snapshot integration evidence and plan remaining governance actions',
6124
+ commands: [
6125
+ 'kse auto observability snapshot --json',
6126
+ 'kse auto governance maintain --session-keep 50 --batch-session-keep 50 --controller-session-keep 50 --json'
6127
+ ]
6128
+ });
6129
+
6130
+ return phases;
6131
+ }
6132
+
6133
+ async function buildAutoHandoffPlan(projectPath, options = {}) {
6134
+ const handoff = await loadAutoHandoffManifest(projectPath, options.manifest);
6135
+ const validationErrors = Array.isArray(handoff.validation.errors) ? handoff.validation.errors : [];
6136
+ const validationWarnings = Array.isArray(handoff.validation.warnings) ? handoff.validation.warnings : [];
6137
+
6138
+ if (options.strict && validationErrors.length > 0) {
6139
+ throw new Error(`handoff plan validation failed: ${validationErrors.join('; ')}`);
6140
+ }
6141
+ if (options.strictWarnings && validationWarnings.length > 0) {
6142
+ throw new Error(`handoff plan validation warnings: ${validationWarnings.join('; ')}`);
6143
+ }
6144
+
6145
+ const phases = buildAutoHandoffPhaseCommands(projectPath, handoff.manifest_path, handoff.specs);
6146
+ return {
6147
+ mode: 'auto-handoff-plan',
6148
+ generated_at: new Date().toISOString(),
6149
+ manifest_path: handoff.manifest_path,
6150
+ source_project: handoff.source_project,
6151
+ handoff: {
6152
+ timestamp: handoff.timestamp,
6153
+ spec_count: handoff.specs.length,
6154
+ template_count: handoff.templates.length,
6155
+ known_gap_count: handoff.known_gaps.length,
6156
+ specs: handoff.specs,
6157
+ templates: handoff.templates,
6158
+ known_gaps: handoff.known_gaps,
6159
+ ontology_validation: handoff.ontology_validation,
6160
+ next_batch: handoff.next_batch
6161
+ },
6162
+ validation: {
6163
+ is_valid: validationErrors.length === 0,
6164
+ errors: validationErrors,
6165
+ warnings: validationWarnings
6166
+ },
6167
+ phases,
6168
+ recommendations: [
6169
+ `kse auto handoff queue --manifest ${quoteCliArg(handoff.manifest_file)} --out ${quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --json`,
6170
+ `kse auto close-loop-batch ${quoteCliArg(AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --format lines --batch-autonomous --continue-on-error --json`
6171
+ ]
6172
+ };
6173
+ }
6174
+
6175
+ function buildAutoHandoffQueueGoals(handoff, options = {}) {
6176
+ const includeKnownGaps = options.includeKnownGaps !== false;
6177
+ const goals = [];
6178
+ const seen = new Set();
6179
+ const pushGoal = value => {
6180
+ const normalized = normalizeHandoffText(value);
6181
+ if (!normalized || seen.has(normalized)) {
6182
+ return;
6183
+ }
6184
+ seen.add(normalized);
6185
+ goals.push(normalized);
6186
+ };
6187
+
6188
+ for (const specName of handoff.specs) {
6189
+ pushGoal(`integrate handoff spec ${specName} with scene package validation, ontology consistency checks, and close-loop completion`);
6190
+ }
6191
+
6192
+ for (const templateName of handoff.templates) {
6193
+ pushGoal(`validate handoff template ${templateName} for template registry compatibility and release readiness`);
6194
+ }
6195
+
6196
+ if (includeKnownGaps) {
6197
+ for (const gap of handoff.known_gaps) {
6198
+ pushGoal(`remediate handoff known gap: ${gap}`);
6199
+ }
6200
+ }
6201
+
6202
+ pushGoal('generate unified observability snapshot and governance follow-up recommendations for this handoff batch');
6203
+ return goals;
6204
+ }
6205
+
6206
+ async function buildAutoHandoffQueue(projectPath, options = {}) {
6207
+ const handoff = await loadAutoHandoffManifest(projectPath, options.manifest);
6208
+ const validationErrors = Array.isArray(handoff.validation.errors) ? handoff.validation.errors : [];
6209
+ if (validationErrors.length > 0) {
6210
+ throw new Error(`handoff queue validation failed: ${validationErrors.join('; ')}`);
6211
+ }
6212
+
6213
+ const includeKnownGaps = options.includeKnownGaps !== false;
6214
+ const goals = buildAutoHandoffQueueGoals(handoff, { includeKnownGaps });
6215
+ if (goals.length === 0) {
6216
+ throw new Error('handoff queue produced no goals');
6217
+ }
6218
+
6219
+ return {
6220
+ mode: 'auto-handoff-queue',
6221
+ generated_at: new Date().toISOString(),
6222
+ manifest_path: handoff.manifest_path,
6223
+ dry_run: Boolean(options.dryRun),
6224
+ append: Boolean(options.append),
6225
+ include_known_gaps: includeKnownGaps,
6226
+ goal_count: goals.length,
6227
+ goals,
6228
+ validation: {
6229
+ errors: handoff.validation.errors,
6230
+ warnings: handoff.validation.warnings
6231
+ },
6232
+ recommendations: [
6233
+ `kse auto close-loop-batch ${quoteCliArg(options.out || AUTO_HANDOFF_DEFAULT_QUEUE_FILE)} --format lines --batch-autonomous --continue-on-error --json`
6234
+ ]
6235
+ };
6236
+ }
6237
+
6238
+ async function writeAutoHandoffQueueFile(projectPath, queueResult, options = {}) {
6239
+ const outCandidate = typeof options.out === 'string' && options.out.trim().length > 0
6240
+ ? options.out.trim()
6241
+ : AUTO_HANDOFF_DEFAULT_QUEUE_FILE;
6242
+ const outputPath = path.isAbsolute(outCandidate)
6243
+ ? outCandidate
6244
+ : path.join(projectPath, outCandidate);
6245
+ await fs.ensureDir(path.dirname(outputPath));
6246
+
6247
+ const content = `${queueResult.goals.join('\n')}\n`;
6248
+ if (options.append && await fs.pathExists(outputPath)) {
6249
+ const existing = await fs.readFile(outputPath, 'utf8');
6250
+ const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
6251
+ await fs.appendFile(outputPath, `${separator}${content}`, 'utf8');
6252
+ } else {
6253
+ await fs.writeFile(outputPath, content, 'utf8');
6254
+ }
6255
+
6256
+ queueResult.output_file = outputPath;
6257
+ }
6258
+
5766
6259
  function buildProgramKpiSnapshot(summary) {
5767
6260
  const results = Array.isArray(summary && summary.results) ? summary.results : [];
5768
6261
  const totalGoals = Number(summary && summary.total_goals) || results.length || 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.47.6",
3
+ "version": "1.47.7",
4
4
  "description": "kiro-spec-engine (kse) - A CLI tool and npm package for spec-driven development with AI coding assistants. NOT the Kiro IDE desktop application.",
5
5
  "main": "index.js",
6
6
  "bin": {