ai-delivery-workflow 0.5.1 → 0.7.0
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/docs/CLI-PARAMETER-REFERENCE.zh-CN.md +1 -0
- package/docs/FILE-REFERENCE.zh-CN.md +3 -3
- package/docs/MAINTENANCE-VERIFICATION.zh-CN.md +2 -2
- package/docs/PROJECT-MANUAL.zh-CN.md +10 -1
- package/docs/agents/workflow-manager-current-state.md +1 -1
- package/lib/communication-confirmation.mjs +177 -0
- package/lib/project-bootstrap.mjs +350 -3
- package/lib/project-installer.mjs +25 -1
- package/package.json +1 -1
- package/skills/ai-delivery-assemble-release/SKILL.md +4 -0
- package/skills/ai-delivery-bootstrap/SKILL.md +6 -0
- package/skills/ai-delivery-checkpoint-task/SKILL.md +4 -0
- package/skills/ai-delivery-checkpoint-task/references/checkpoint-contract.md +2 -0
- package/skills/ai-delivery-close-version/SKILL.md +15 -10
- package/skills/ai-delivery-close-version/references/version-closeout-contract.md +2 -1
- package/skills/ai-delivery-define-product/SKILL.md +8 -3
- package/skills/ai-delivery-deploy-production/SKILL.md +2 -0
- package/skills/ai-delivery-design-architecture/SKILL.md +12 -5
- package/skills/ai-delivery-design-architecture/assets/architecture-template/architecture-gate.yaml +2 -0
- package/skills/ai-delivery-design-architecture/assets/architecture-template/prototype-architecture-validation.yaml +2 -0
- package/skills/ai-delivery-design-architecture/references/architecture-contract.md +4 -0
- package/skills/ai-delivery-design-experience/SKILL.md +7 -3
- package/skills/ai-delivery-design-experience/assets/experience-template/product-prototype-reconciliation.yaml +2 -0
- package/skills/ai-delivery-design-experience/assets/experience-template/ui-handoff.yaml +3 -0
- package/skills/ai-delivery-design-experience/references/experience-contract.md +2 -2
- package/skills/ai-delivery-design-tests/SKILL.md +6 -0
- package/skills/ai-delivery-develop-iteration/SKILL.md +8 -1
- package/skills/ai-delivery-develop-iteration/assets/development-template/candidate-manifest.yaml +2 -0
- package/skills/ai-delivery-evolve-workflow/SKILL.md +2 -0
- package/skills/ai-delivery-execute-work-package/SKILL.md +6 -2
- package/skills/ai-delivery-manage-standards/SKILL.md +2 -0
- package/skills/ai-delivery-orchestrate/SKILL.md +4 -0
- package/skills/ai-delivery-orchestrate/assets/codebase-template/AGENTS.md +14 -0
- package/skills/ai-delivery-orchestrate/assets/codebase-template/CODEBASE_GUIDE.md +124 -0
- package/skills/ai-delivery-orchestrate/assets/codebase-template/docs/CODEBASE_INDEX.md +87 -0
- package/skills/ai-delivery-orchestrate/assets/communication-template/confirmation-examples.yaml +67 -0
- package/skills/ai-delivery-orchestrate/assets/communication-template/confirmation-summary.yaml +42 -0
- package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/iterations/ITER-0001/02-solution-design/product-impact-assessment.yaml +42 -0
- package/skills/ai-delivery-orchestrate/assets/project-template/.workflow/tools/bootstrap/bootstrap.mjs +7 -2
- package/skills/ai-delivery-orchestrate/references/codebase-document-contract.md +93 -0
- package/skills/ai-delivery-orchestrate/references/communication-confirmation-contract.md +69 -0
- package/skills/ai-delivery-orchestrate/references/product-impact-assessment-contract.md +87 -0
- package/skills/ai-delivery-orchestrate/scripts/confirmation-state.mjs +30 -0
- package/skills/ai-delivery-orchestrate-release/SKILL.md +5 -1
- package/skills/ai-delivery-plan-iteration/SKILL.md +8 -1
- package/skills/ai-delivery-prepare-platform/SKILL.md +3 -1
- package/skills/ai-delivery-prepare-release/SKILL.md +4 -0
- package/skills/ai-delivery-review-change/SKILL.md +5 -1
- package/skills/ai-delivery-validate-artifacts/SKILL.md +12 -7
- package/skills/ai-delivery-validate-artifacts/references/artifact-contract.md +2 -1
- package/skills/ai-delivery-verify-candidate/SKILL.md +7 -2
- package/skills/ai-delivery-verify-candidate/references/candidate-verification-contract.md +1 -1
- package/skills/ai-delivery-verify-production/SKILL.md +4 -0
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
stableValue,
|
|
9
9
|
withFormalStateReadLock,
|
|
10
10
|
} from "./delivery-state.mjs";
|
|
11
|
+
import { readConfirmationSummary } from "./communication-confirmation.mjs";
|
|
11
12
|
import { inspectGitRepository, readWorkspaceConfiguration } from "./workspace.mjs";
|
|
12
13
|
|
|
13
14
|
const IGNORED_DIRECTORIES = new Set([
|
|
@@ -32,6 +33,50 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
32
33
|
"target",
|
|
33
34
|
]);
|
|
34
35
|
const VALID_NEXT_ACTION_OWNERS = new Set(["agent", "user", "external"]);
|
|
36
|
+
const CODEBASE_DOCUMENTATION_FILES = [
|
|
37
|
+
{ path: "AGENTS.md", kind: "agent-entrypoint" },
|
|
38
|
+
{ path: "CODEBASE_GUIDE.md", kind: "maintainer-guide" },
|
|
39
|
+
{ path: "docs/CODEBASE_INDEX.md", kind: "navigation-index" },
|
|
40
|
+
];
|
|
41
|
+
const CODEBASE_DOCUMENTATION_STATUS = new Set(["observed", "approved", "stale"]);
|
|
42
|
+
|
|
43
|
+
export function inspectConfirmationSummaries(root) {
|
|
44
|
+
const base = path.join(root, ".workflow", "iterations");
|
|
45
|
+
const summaries = [];
|
|
46
|
+
const errors = [];
|
|
47
|
+
if (!fs.existsSync(base)) return { status: "absent", summaries, errors };
|
|
48
|
+
const visit = (directory) => {
|
|
49
|
+
let entries;
|
|
50
|
+
try { entries = fs.readdirSync(directory, { withFileTypes: true }); } catch (error) {
|
|
51
|
+
errors.push(`${toPosix(path.relative(root, directory))}: ${error.message}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const absolute = path.join(directory, entry.name);
|
|
56
|
+
if (entry.isDirectory()) visit(absolute);
|
|
57
|
+
else if (entry.isFile() && entry.name === "confirmation-summary.yaml") {
|
|
58
|
+
try {
|
|
59
|
+
const result = readConfirmationSummary(absolute);
|
|
60
|
+
summaries.push({
|
|
61
|
+
path: toPosix(path.relative(root, absolute)),
|
|
62
|
+
checksum: sha256File(absolute),
|
|
63
|
+
summary_id: result.summary.summary_id,
|
|
64
|
+
version: result.summary.version,
|
|
65
|
+
frontier: result.validation.frontier,
|
|
66
|
+
blocked_proposals: result.validation.blocked_proposals,
|
|
67
|
+
valid: result.validation.valid,
|
|
68
|
+
errors: result.validation.errors,
|
|
69
|
+
});
|
|
70
|
+
if (!result.validation.valid) errors.push(...result.validation.errors.map((item) => `${toPosix(path.relative(root, absolute))}: ${item}`));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errors.push(`${toPosix(path.relative(root, absolute))}: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
visit(base);
|
|
78
|
+
return { status: errors.length ? "invalid" : summaries.length ? "valid" : "absent", summaries, errors };
|
|
79
|
+
}
|
|
35
80
|
|
|
36
81
|
const SOURCE_EXTENSIONS = new Map([
|
|
37
82
|
[".c", "C"],
|
|
@@ -255,6 +300,282 @@ function scanProjectFiles(root, maxFiles = 10_000) {
|
|
|
255
300
|
};
|
|
256
301
|
}
|
|
257
302
|
|
|
303
|
+
function hasSensitiveContent(text) {
|
|
304
|
+
return /BEGIN (?:RSA|OPENSSH|EC|DSA) PRIVATE KEY|(?:AKIA|ASIA)[A-Z0-9]{16}|(?:ghp|github_pat)_[A-Za-z0-9_]{20,}/.test(text)
|
|
305
|
+
|| /(?:password|secret|token|api[_-]?key)\s*[:=]\s*["'][^"']{8,}["']/i.test(text);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function documentationMetadata(text) {
|
|
309
|
+
const status = text.match(/(?:^|\|)\s*(?:状态|项目状态)\s*\|\s*`?([^|\n`]+)`?/m)?.[1]?.trim().toLowerCase() || null;
|
|
310
|
+
const sourceCommit = text.match(/(?:适用代码提交|对应代码提交|source_commit)\s*\|\s*`?([^|\n`]+)`?/m)?.[1]?.trim()
|
|
311
|
+
|| text.match(/`source_commit`\s*\|\s*`?([^|\n`]+)`?/m)?.[1]?.trim()
|
|
312
|
+
|| null;
|
|
313
|
+
const lastReviewed = text.match(/(?:最后核对|last_reviewed)\s*\|\s*`?([^|\n`]+)`?/m)?.[1]?.trim()
|
|
314
|
+
|| text.match(/`last_reviewed`\s*\|\s*`?([^|\n`]+)`?/m)?.[1]?.trim()
|
|
315
|
+
|| null;
|
|
316
|
+
return { status, source_commit: sourceCommit, last_reviewed: lastReviewed };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function documentationPathEntries(text) {
|
|
320
|
+
const entries = [];
|
|
321
|
+
let header = null;
|
|
322
|
+
for (const [index, line] of text.split(/\r?\n/).entries()) {
|
|
323
|
+
if (!line.trim().startsWith("|")) continue;
|
|
324
|
+
const cells = line.split("|").slice(1, -1).map((cell) => cell.trim());
|
|
325
|
+
if (!cells.length) continue;
|
|
326
|
+
if (cells.some((cell) => /^(路径|文件)$/.test(cell))) {
|
|
327
|
+
header = cells;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!header || cells.every((cell) => /^:?-{3,}:?$/.test(cell))) continue;
|
|
331
|
+
const pathIndex = header.findIndex((cell) => /^(路径|文件)$/.test(cell));
|
|
332
|
+
if (pathIndex < 0 || !cells[pathIndex]) continue;
|
|
333
|
+
entries.push({ value: cells[pathIndex].replaceAll("`", "").trim(), line: index + 1 });
|
|
334
|
+
}
|
|
335
|
+
return entries;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function documentationLinks(text) {
|
|
339
|
+
return [...text.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)].map((match) => match[1].trim());
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const INDEX_FIELD_ALIASES = new Map([
|
|
343
|
+
["路径", "path"], ["文件", "path"], ["职责", "purpose"], ["用途", "purpose"],
|
|
344
|
+
["主要入口", "entrypoints"], ["入口", "entrypoints"], ["关联测试", "tests"], ["测试/验证", "tests"],
|
|
345
|
+
["关键依赖或被依赖模块", "dependencies"], ["允许依赖", "dependencies"], ["依赖", "dependencies"],
|
|
346
|
+
["风险", "risk"], ["生成或外部维护", "generated"], ["生成", "generated"],
|
|
347
|
+
["维护责任", "owner"], ["维护状态", "status"], ["状态", "status"],
|
|
348
|
+
["适用代码提交", "source_commit"], ["source_commit", "source_commit"],
|
|
349
|
+
["最后核对", "last_reviewed"], ["last_reviewed", "last_reviewed"],
|
|
350
|
+
["正式依据", "references"], ["references", "references"],
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
function markdownCells(line) {
|
|
354
|
+
return line.split("|").slice(1, -1).map((cell) => cell.trim().replaceAll("`", ""));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function projectIndexRows(text) {
|
|
358
|
+
const rows = [];
|
|
359
|
+
let section = null;
|
|
360
|
+
let header = null;
|
|
361
|
+
for (const [lineIndex, line] of text.split(/\r?\n/).entries()) {
|
|
362
|
+
const heading = line.match(/^#{2,6}\s+(.+?)\s*#*$/);
|
|
363
|
+
if (heading) {
|
|
364
|
+
section = heading[1].trim();
|
|
365
|
+
header = null;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (!line.trim().startsWith("|")) {
|
|
369
|
+
if (line.trim()) header = null;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const cells = markdownCells(line);
|
|
373
|
+
if (!cells.length || cells.every((cell) => /^:?-{3,}:?$/.test(cell))) continue;
|
|
374
|
+
if (cells.some((cell) => INDEX_FIELD_ALIASES.has(cell))) {
|
|
375
|
+
header = cells;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (!header) continue;
|
|
379
|
+
const row = { section, source_line: lineIndex + 1 };
|
|
380
|
+
header.forEach((name, index) => {
|
|
381
|
+
const key = INDEX_FIELD_ALIASES.get(name);
|
|
382
|
+
if (key && cells[index] !== undefined && cells[index] !== "") row[key] = cells[index];
|
|
383
|
+
});
|
|
384
|
+
if (row.path && !/^<.*>$/.test(row.path) && row.path !== "-") rows.push(row);
|
|
385
|
+
}
|
|
386
|
+
return rows;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function projectCodebaseIndex(codeRoot, outputFile = null) {
|
|
390
|
+
const root = path.resolve(codeRoot);
|
|
391
|
+
const indexFile = path.join(root, "docs", "CODEBASE_INDEX.md");
|
|
392
|
+
if (!fs.existsSync(indexFile)) throw new Error("docs/CODEBASE_INDEX.md does not exist");
|
|
393
|
+
const documentation = inspectCodebaseDocumentation(root, inspectGitRepository(root));
|
|
394
|
+
if (documentation.invalid.length) {
|
|
395
|
+
throw new Error(`CODEBASE_INDEX.md is invalid:\n${documentation.invalid.map((item) => `${item.path}: ${item.reason}`).join("\n")}`);
|
|
396
|
+
}
|
|
397
|
+
const bytes = fs.readFileSync(indexFile);
|
|
398
|
+
const text = bytes.toString("utf8");
|
|
399
|
+
const metadata = documentation.files.find((file) => file.path === "docs/CODEBASE_INDEX.md") || documentationMetadata(text);
|
|
400
|
+
const rows = projectIndexRows(text);
|
|
401
|
+
if (!rows.length) throw new Error("CODEBASE_INDEX.md contains no concrete index rows");
|
|
402
|
+
// The code repository is a nested child of the workflow workspace. Keep the
|
|
403
|
+
// derived projection beside it, never inside the code repository itself.
|
|
404
|
+
const defaultWorkspaceRoot = path.dirname(root);
|
|
405
|
+
const destination = path.resolve(outputFile || path.join(defaultWorkspaceRoot, ".workflow", "delivery", "bootstrap", "codebase-index.yaml"));
|
|
406
|
+
const relative = path.relative(root, destination);
|
|
407
|
+
if (!relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
408
|
+
throw new Error("machine projection must be written outside the code repository");
|
|
409
|
+
}
|
|
410
|
+
const projection = {
|
|
411
|
+
schema_version: 1,
|
|
412
|
+
projection_type: "codebase-index",
|
|
413
|
+
source_of_truth: "docs/CODEBASE_INDEX.md",
|
|
414
|
+
source_checksum: sha256File(indexFile),
|
|
415
|
+
source_commit: metadata.source_commit || null,
|
|
416
|
+
last_reviewed: metadata.last_reviewed || null,
|
|
417
|
+
status: documentation.status,
|
|
418
|
+
generated_at: new Date().toISOString(),
|
|
419
|
+
entries: rows,
|
|
420
|
+
};
|
|
421
|
+
atomicWrite(destination, stringifyYaml(projection, { lineWidth: 0 }));
|
|
422
|
+
return { output: toPosix(destination), source: "docs/CODEBASE_INDEX.md", source_checksum: projection.source_checksum, status: projection.status, entry_count: rows.length };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function inspectCodebaseDocumentation(root, git = inspectGitRepository(root)) {
|
|
426
|
+
const files = [];
|
|
427
|
+
const errors = [];
|
|
428
|
+
const warnings = [];
|
|
429
|
+
const missing = [];
|
|
430
|
+
const invalid = [];
|
|
431
|
+
const stale = [];
|
|
432
|
+
const statuses = [];
|
|
433
|
+
if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
|
|
434
|
+
return {
|
|
435
|
+
status: "missing",
|
|
436
|
+
files: [],
|
|
437
|
+
missing: CODEBASE_DOCUMENTATION_FILES.map((item) => item.path),
|
|
438
|
+
invalid: [],
|
|
439
|
+
stale: [],
|
|
440
|
+
sensitive_file_count: 0,
|
|
441
|
+
errors: [],
|
|
442
|
+
warnings: ["代码仓库目录不存在,无法发现项目说明书和索引。"],
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
for (const expected of CODEBASE_DOCUMENTATION_FILES) {
|
|
447
|
+
const absolute = path.join(root, expected.path);
|
|
448
|
+
if (!fs.existsSync(absolute)) {
|
|
449
|
+
missing.push(expected.path);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
let stat;
|
|
453
|
+
try {
|
|
454
|
+
stat = fs.lstatSync(absolute);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
invalid.push({ path: expected.path, reason: `无法读取文件:${error.code || error.message}` });
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (stat.isSymbolicLink()) {
|
|
460
|
+
invalid.push({ path: expected.path, reason: "说明文件不能是符号链接" });
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (!stat.isFile()) {
|
|
464
|
+
invalid.push({ path: expected.path, reason: "说明路径必须是普通文件" });
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
let text;
|
|
468
|
+
try {
|
|
469
|
+
text = fs.readFileSync(absolute, "utf8");
|
|
470
|
+
} catch (error) {
|
|
471
|
+
invalid.push({ path: expected.path, reason: `无法读取文件:${error.code || error.message}` });
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
const metadata = documentationMetadata(text);
|
|
475
|
+
const record = { path: expected.path, kind: expected.kind, ...metadata, bytes: stat.size };
|
|
476
|
+
files.push(record);
|
|
477
|
+
if (expected.kind !== "agent-entrypoint") {
|
|
478
|
+
if (!metadata.status) invalid.push({ path: expected.path, reason: "缺少 status/状态字段" });
|
|
479
|
+
if (!metadata.source_commit) invalid.push({ path: expected.path, reason: "缺少 source_commit/适用代码提交字段" });
|
|
480
|
+
if (!metadata.last_reviewed) invalid.push({ path: expected.path, reason: "缺少 last_reviewed/最后核对字段" });
|
|
481
|
+
}
|
|
482
|
+
if (metadata.status && !CODEBASE_DOCUMENTATION_STATUS.has(metadata.status)) {
|
|
483
|
+
invalid.push({ path: expected.path, reason: `status 无效:${metadata.status}` });
|
|
484
|
+
}
|
|
485
|
+
if (metadata.status) statuses.push(metadata.status);
|
|
486
|
+
if (hasSensitiveContent(text)) {
|
|
487
|
+
invalid.push({ path: expected.path, reason: "疑似包含凭据、私钥或敏感值" });
|
|
488
|
+
}
|
|
489
|
+
if (git?.head && metadata.source_commit && !/^<.*>$/.test(metadata.source_commit)
|
|
490
|
+
&& metadata.source_commit !== "-" && metadata.source_commit !== git.head) {
|
|
491
|
+
stale.push({ path: expected.path, reason: `source_commit ${metadata.source_commit} 与当前 HEAD ${git.head} 不一致` });
|
|
492
|
+
}
|
|
493
|
+
if (expected.kind === "maintainer-guide") {
|
|
494
|
+
for (const heading of ["项目概览", "快速开始", "仓库地图", "工程分层", "工程约定", "变更工作流", "风险和保护边界", "文档维护"]) {
|
|
495
|
+
if (!new RegExp(`^## .*${heading}`, "m").test(text)) {
|
|
496
|
+
invalid.push({ path: expected.path, reason: `缺少章节:${heading}` });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (expected.kind === "navigation-index") {
|
|
501
|
+
for (const field of ["路径", "职责", "主要入口", "关联测试", "风险", "状态", "source_commit", "last_reviewed"]) {
|
|
502
|
+
if (!text.includes(field)) invalid.push({ path: expected.path, reason: `缺少索引字段:${field}` });
|
|
503
|
+
}
|
|
504
|
+
for (const entry of documentationPathEntries(text)) {
|
|
505
|
+
const value = entry.value;
|
|
506
|
+
if (!value || value === "-" || /^<.*>$/.test(value) || value.includes("*")) continue;
|
|
507
|
+
if (path.isAbsolute(value) || value.split("/").includes("..")) {
|
|
508
|
+
invalid.push({ path: expected.path, reason: `第 ${entry.line} 行路径越界:${value}` });
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
const target = path.resolve(root, value);
|
|
512
|
+
const relative = path.relative(root, target);
|
|
513
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
514
|
+
invalid.push({ path: expected.path, reason: `第 ${entry.line} 行路径越界:${value}` });
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (!fs.existsSync(target)) {
|
|
518
|
+
invalid.push({ path: expected.path, reason: `第 ${entry.line} 行引用路径不存在:${value}` });
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (fs.lstatSync(target).isSymbolicLink()) {
|
|
522
|
+
invalid.push({ path: expected.path, reason: `第 ${entry.line} 行引用符号链接:${value}` });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const seenPaths = new Set();
|
|
527
|
+
for (const entry of documentationPathEntries(text)) {
|
|
528
|
+
if (/^<.*>$/.test(entry.value) || entry.value === "-" || entry.value.includes("*")) continue;
|
|
529
|
+
if (seenPaths.has(entry.value)) {
|
|
530
|
+
invalid.push({ path: expected.path, reason: `第 ${entry.line} 行重复索引路径:${entry.value}` });
|
|
531
|
+
}
|
|
532
|
+
seenPaths.add(entry.value);
|
|
533
|
+
}
|
|
534
|
+
for (const link of documentationLinks(text)) {
|
|
535
|
+
const targetValue = link.split(/[?#]/, 1)[0];
|
|
536
|
+
if (!targetValue || targetValue.startsWith("#") || /^[a-z][a-z0-9+.-]*:/i.test(targetValue)) continue;
|
|
537
|
+
if (path.isAbsolute(targetValue) || targetValue.split("/").includes("..")) {
|
|
538
|
+
invalid.push({ path: expected.path, reason: `Markdown 链接越界:${link}` });
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
const target = path.resolve(root, path.dirname(expected.path), targetValue);
|
|
542
|
+
const relative = path.relative(root, target);
|
|
543
|
+
if (relative.startsWith("..") || path.isAbsolute(relative) || !fs.existsSync(target)) {
|
|
544
|
+
invalid.push({ path: expected.path, reason: `Markdown 链接路径不存在:${link}` });
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (missing.length) warnings.push(`缺少代码仓库说明文件:${missing.join(", ")}`);
|
|
550
|
+
if (invalid.length) errors.push(...invalid.map((item) => `${item.path}: ${item.reason}`));
|
|
551
|
+
if (stale.length) warnings.push(...stale.map((item) => `${item.path}: ${item.reason}`));
|
|
552
|
+
const hasTemplatePlaceholders = files.some((file) => file.source_commit?.startsWith("<") || file.status === null);
|
|
553
|
+
if (new Set(statuses).size > 1) {
|
|
554
|
+
errors.push(`代码仓库说明文档状态冲突:${[...new Set(statuses)].join(", ")}`);
|
|
555
|
+
}
|
|
556
|
+
const status = invalid.length || errors.length
|
|
557
|
+
? "invalid"
|
|
558
|
+
: stale.length
|
|
559
|
+
? "stale"
|
|
560
|
+
: missing.length
|
|
561
|
+
? "missing"
|
|
562
|
+
: statuses.some((value) => value === "stale")
|
|
563
|
+
? "stale"
|
|
564
|
+
: hasTemplatePlaceholders || statuses.some((value) => value === "observed")
|
|
565
|
+
? "observed"
|
|
566
|
+
: "approved";
|
|
567
|
+
return {
|
|
568
|
+
status,
|
|
569
|
+
files,
|
|
570
|
+
missing,
|
|
571
|
+
invalid,
|
|
572
|
+
stale,
|
|
573
|
+
sensitive_file_count: invalid.filter((item) => item.reason.includes("敏感值")).length,
|
|
574
|
+
errors,
|
|
575
|
+
warnings,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
258
579
|
function gitState(root) {
|
|
259
580
|
const run = (args) => spawnSync("git", args, { cwd: root, encoding: "utf8", windowsHide: true });
|
|
260
581
|
const topLevel = run(["rev-parse", "--show-toplevel"]);
|
|
@@ -593,8 +914,19 @@ export function inspectProjectState(target) {
|
|
|
593
914
|
}
|
|
594
915
|
}
|
|
595
916
|
|
|
917
|
+
const codeGit = inspectGitRepository(codeRoot);
|
|
918
|
+
const documentation = inspectCodebaseDocumentation(codeRoot, codeGit);
|
|
919
|
+
const confirmations = inspectConfirmationSummaries(root);
|
|
920
|
+
if (confirmations.errors.length) errors.push(...confirmations.errors);
|
|
921
|
+
codebase.summary.documentation = documentation;
|
|
922
|
+
codebase.summary.confirmations = confirmations;
|
|
923
|
+
errors.push(...documentation.errors);
|
|
924
|
+
const documentationWarnings = documentation.warnings;
|
|
925
|
+
|
|
596
926
|
let state;
|
|
597
|
-
if (
|
|
927
|
+
if (errors.length) {
|
|
928
|
+
state = "workflow-inconsistent";
|
|
929
|
+
} else if (!installation.detected) {
|
|
598
930
|
state = codebase.summary.has_code ? "codebase-uninitialized" : "empty-uninitialized";
|
|
599
931
|
} else if (!installation.complete || errors.length) {
|
|
600
932
|
state = "workflow-inconsistent";
|
|
@@ -604,7 +936,6 @@ export function inspectProjectState(target) {
|
|
|
604
936
|
state = "workflow-ready";
|
|
605
937
|
}
|
|
606
938
|
const recommendation = recommendationFor(state, workflow, runtime);
|
|
607
|
-
const codeGit = inspectGitRepository(codeRoot);
|
|
608
939
|
const workflowGit = inspectGitRepository(root);
|
|
609
940
|
const syncWarnings = [];
|
|
610
941
|
if (workflowGit.sync_status === "uncommitted") {
|
|
@@ -625,11 +956,12 @@ export function inspectProjectState(target) {
|
|
|
625
956
|
workflow,
|
|
626
957
|
runtime,
|
|
627
958
|
codebase: codebase.summary,
|
|
959
|
+
confirmations,
|
|
628
960
|
git: codeGit,
|
|
629
961
|
workflow_git: workflowGit,
|
|
630
962
|
recommendation,
|
|
631
963
|
errors,
|
|
632
|
-
warnings: [...codebase.warnings, ...syncWarnings],
|
|
964
|
+
warnings: [...codebase.warnings, ...documentationWarnings, ...syncWarnings],
|
|
633
965
|
};
|
|
634
966
|
}
|
|
635
967
|
|
|
@@ -701,6 +1033,10 @@ function adoptionMarkdown(initialState, scan, result) {
|
|
|
701
1033
|
if (!artifactTypes.some((type) => /architecture.*baseline/.test(type))) gaps.push("缺少经确认的架构基线。");
|
|
702
1034
|
if (scan.summary.has_code && !scan.summary.categories.test) gaps.push("未发现可识别的自动化测试文件,需人工核对测试入口。");
|
|
703
1035
|
if (!result.git.repository) gaps.push("当前目录不是有效 Git 仓库,分支和提交身份不可用。");
|
|
1036
|
+
const documentation = result.codebase.documentation;
|
|
1037
|
+
if (documentation?.status === "missing") gaps.push("代码仓库说明书或导航索引缺失,需要在维护者确认后补齐。");
|
|
1038
|
+
if (documentation?.status === "invalid") gaps.push("代码仓库说明书或导航索引存在结构、路径或敏感内容问题,Bootstrap 已阻止将其视为可信上下文。");
|
|
1039
|
+
if (documentation?.status === "stale") gaps.push("代码仓库说明书或导航索引相对当前提交过期,需要更新 source_commit 并重新审阅。");
|
|
704
1040
|
return `# 存量项目接入评估
|
|
705
1041
|
|
|
706
1042
|
## 探测结论
|
|
@@ -712,6 +1048,14 @@ function adoptionMarkdown(initialState, scan, result) {
|
|
|
712
1048
|
- 检测技术:${scan.summary.technologies.join("、") || "未识别"}
|
|
713
1049
|
- 排除的敏感文件:${scan.summary.sensitive_file_count}
|
|
714
1050
|
|
|
1051
|
+
## 代码仓库说明书与导航
|
|
1052
|
+
|
|
1053
|
+
- 状态:\`${documentation?.status || "missing"}\`
|
|
1054
|
+
- 已发现:${(documentation?.files || []).filter((item) => item.status !== "missing").map((item) => item.path).join("、") || "无"}
|
|
1055
|
+
- 缺失:${(documentation?.missing || []).join("、") || "无"}
|
|
1056
|
+
- 失效:${(documentation?.invalid || []).map((item) => item.path).join("、") || "无"}
|
|
1057
|
+
- 过期:${(documentation?.stale || []).map((item) => item.path).join("、") || "无"}
|
|
1058
|
+
|
|
715
1059
|
## 上下文边界
|
|
716
1060
|
|
|
717
1061
|
本报告只描述代码和仓库的可观察现状。代码行为不是已批准产品需求,自动归纳内容均保持 \`draft\`,必须通过产品和架构流程核对后才能成为正式基线。
|
|
@@ -777,6 +1121,7 @@ function writeBootstrapArtifacts(root, initialState, result, sourceScan = null)
|
|
|
777
1121
|
detected_at: result.detected_at,
|
|
778
1122
|
repository: result.git,
|
|
779
1123
|
codebase: scan.summary,
|
|
1124
|
+
documentation: result.codebase.documentation,
|
|
780
1125
|
command_candidates: packageCommands(root, scan.files),
|
|
781
1126
|
constraints: [
|
|
782
1127
|
"Observed code is not an approved product requirement.",
|
|
@@ -833,6 +1178,8 @@ function writeBootstrapArtifacts(root, initialState, result, sourceScan = null)
|
|
|
833
1178
|
workflow: result.workflow,
|
|
834
1179
|
runtime: result.runtime,
|
|
835
1180
|
codebase: result.codebase,
|
|
1181
|
+
documentation: result.codebase.documentation,
|
|
1182
|
+
confirmations: result.confirmations,
|
|
836
1183
|
git: result.git,
|
|
837
1184
|
recommendation: result.recommendation,
|
|
838
1185
|
errors: result.errors,
|
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import { isSeq, parse as parseYaml, parseDocument, stringify as stringifyYaml } from "yaml";
|
|
9
9
|
import { assertPathInside, atomicWrite, filesUnder } from "./fs-utils.mjs";
|
|
10
10
|
import { inspectCodeGraph } from "./codegraph.mjs";
|
|
11
|
+
import { inspectCodebaseDocumentation, inspectConfirmationSummaries } from "./project-bootstrap.mjs";
|
|
11
12
|
import { executeDeliveryStateCommand } from "./delivery-state.mjs";
|
|
12
13
|
import { inspectHookConfig, managedHookConfigProjection, mergeHookConfig } from "./toml-hooks.mjs";
|
|
13
14
|
import {
|
|
@@ -522,7 +523,7 @@ function bootstrapRuntimeSources(packageRoot, sourceTemplate) {
|
|
|
522
523
|
relative: "bootstrap.mjs",
|
|
523
524
|
},
|
|
524
525
|
...[
|
|
525
|
-
"project-bootstrap.mjs", "workspace.mjs", "yaml-runtime.mjs", "delivery-state.mjs",
|
|
526
|
+
"project-bootstrap.mjs", "communication-confirmation.mjs", "workspace.mjs", "yaml-runtime.mjs", "delivery-state.mjs",
|
|
526
527
|
"history-anchor.mjs", "fs-utils.mjs",
|
|
527
528
|
].map((name) => ({
|
|
528
529
|
source: path.join(packageRoot, "lib", name),
|
|
@@ -1918,6 +1919,28 @@ export function doctorProject(target, options = {}) {
|
|
|
1918
1919
|
|
|
1919
1920
|
const workflowGit = gitTopLevel(target);
|
|
1920
1921
|
const codeGit = workspace ? gitTopLevel(workspace.codeRoot) : null;
|
|
1922
|
+
if (workspace) {
|
|
1923
|
+
const documentation = inspectCodebaseDocumentation(workspace.codeRoot);
|
|
1924
|
+
checks.push({
|
|
1925
|
+
ok: documentation.status !== "invalid",
|
|
1926
|
+
required: false,
|
|
1927
|
+
scope: "codebase-documentation",
|
|
1928
|
+
message: documentation.status === "missing"
|
|
1929
|
+
? "code repository handbook/index are not installed; Bootstrap or maintainers may add them"
|
|
1930
|
+
: `code repository handbook/index status: ${documentation.status}`,
|
|
1931
|
+
});
|
|
1932
|
+
const confirmations = inspectConfirmationSummaries(target);
|
|
1933
|
+
checks.push({
|
|
1934
|
+
ok: confirmations.status !== "invalid",
|
|
1935
|
+
required: false,
|
|
1936
|
+
scope: "communication-confirmation",
|
|
1937
|
+
message: confirmations.status === "absent"
|
|
1938
|
+
? "no communication confirmation summaries are present"
|
|
1939
|
+
: confirmations.status === "invalid"
|
|
1940
|
+
? `communication confirmation summaries are invalid: ${confirmations.errors.join("; ")}`
|
|
1941
|
+
: `${confirmations.summaries.length} communication confirmation summary(ies) validate; frontier=${confirmations.summaries.flatMap((item) => item.frontier).join(",") || "empty"}`,
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1921
1944
|
const workflowCommonGit = workflowGit ? gitCommonDirectory(target) : null;
|
|
1922
1945
|
const codeCommonGit = codeGit && workspace ? gitCommonDirectory(workspace.codeRoot) : null;
|
|
1923
1946
|
checks.push({
|
|
@@ -2075,6 +2098,7 @@ export function doctorProject(target, options = {}) {
|
|
|
2075
2098
|
const bootstrapFiles = [
|
|
2076
2099
|
"bootstrap.mjs",
|
|
2077
2100
|
"project-bootstrap.mjs",
|
|
2101
|
+
"communication-confirmation.mjs",
|
|
2078
2102
|
"workspace.mjs",
|
|
2079
2103
|
"yaml-runtime.mjs",
|
|
2080
2104
|
"delivery-state.mjs",
|
package/package.json
CHANGED
|
@@ -5,6 +5,10 @@ description: Calculate a selected release target's continuous cumulative version
|
|
|
5
5
|
|
|
6
6
|
# Assemble Release
|
|
7
7
|
|
|
8
|
+
Apply the shared confirmation protocol from `../ai-delivery-orchestrate/references/communication-confirmation-contract.md`; release-selection and material exceptions must be recorded in the release confirmation summary before they can affect the candidate.
|
|
9
|
+
|
|
10
|
+
When release material aggregation exposes a missing decision or exception, record it in the release confirmation summary and keep the candidate blocked until the owning artifact is updated.
|
|
11
|
+
|
|
8
12
|
Produce a deterministic release selection and auditable material package. Do not approve or deploy it.
|
|
9
13
|
|
|
10
14
|
## Select The Version Closure
|
|
@@ -5,6 +5,8 @@ description: Inspect a project before normal AI delivery execution, classify emp
|
|
|
5
5
|
|
|
6
6
|
# Bootstrap AI Delivery
|
|
7
7
|
|
|
8
|
+
When Bootstrap needs a business or maintainer decision, use `../ai-delivery-orchestrate/references/communication-confirmation-contract.md` to create the first frontier question. Keep repository observations evidence-backed and route business intent to `01-product-shaping`.
|
|
9
|
+
|
|
8
10
|
Establish enough verified project context to resume or begin work without relying on chat history.
|
|
9
11
|
|
|
10
12
|
## Dual-Repository Workspace
|
|
@@ -52,6 +54,10 @@ Treat repository scans as observations, not approved product or architecture tru
|
|
|
52
54
|
|
|
53
55
|
Do not replace existing product, architecture, test, or release baselines. Keep the project-state report and related observations outside the formal artifact registry while they remain working drafts, preserve every timestamped bootstrap run, and update only the `.workflow/delivery/bootstrap/latest.yaml` pointer.
|
|
54
56
|
|
|
57
|
+
### Codebase Handbook Discovery
|
|
58
|
+
|
|
59
|
+
For the configured nested code repository, inspect `AGENTS.md`, `CODEBASE_GUIDE.md`, and `docs/CODEBASE_INDEX.md` as read-only maintainer documents. The report records `observed`, `approved`, or `stale`; missing files are a warning, while malformed, path-invalid, symlink, or sensitive-content findings fail closed. Compare each declared `source_commit` with the code repository HEAD. When a structured consumer needs navigation, run the explicit `project-index` command to generate `.workflow/delivery/bootstrap/codebase-index.yaml` from the Markdown index and verify its source checksum before use. Never infer an approved rule from the generated inventory or projection, and never overwrite the code-repository documents during bootstrap or upgrade.
|
|
60
|
+
|
|
55
61
|
## Resume Boundary
|
|
56
62
|
|
|
57
63
|
Codex lifecycle Hooks provide audit and heartbeat data only. Versioned Evolution Hooks may return `pass`, `warn`, or an approved `block`, but they never select or complete tasks and never mutate formal workflow state. A bootstrap report may recommend recovery, but `$ai-delivery-checkpoint-task` remains authoritative for task order, dependency readiness, worktree reconciliation, and idempotent continuation. Route pending feedback and extension evaluation to `$ai-delivery-evolve-workflow` only at a task, version, or explicit review safe point.
|
|
@@ -5,6 +5,8 @@ description: Persist, checkpoint, finish, recover, and archive version-scoped AI
|
|
|
5
5
|
|
|
6
6
|
# Checkpoint Delivery Task
|
|
7
7
|
|
|
8
|
+
When a task is waiting on a user or external confirmation, persist the summary path, question ID, frontier round, and checksum from the shared communication protocol. Recovery remains zero-mutation until ownership transfers to `agent`.
|
|
9
|
+
|
|
8
10
|
Treat explicit task snapshots as authoritative. Treat Hook events as an audit trail and heartbeat, never as proof that a stage completed.
|
|
9
11
|
|
|
10
12
|
## Artifact Location
|
|
@@ -60,6 +62,8 @@ Checkpoint after every externally meaningful milestone, before a long command, a
|
|
|
60
62
|
|
|
61
63
|
Use shared shards and the newest shared checkpoint as the portable recovery source. If local runtime is absent and `next_action_owner` is `agent`, `recover` restores it from shared state. For `user` or `external`, report the wait state without hydration; after the input arrives, `activate` performs the atomic hydration and owner transfer. If local and shared revisions or semantic checksums differ, stop for reconciliation. Inspect both Git repositories before resuming. Report pending `.workflow/delivery/runtime/evolution/pending/` events, but defer their evaluation until the recovered task reaches a safe point.
|
|
62
64
|
|
|
65
|
+
Confirmation waits persist the summary path, question identity, frontier round, and an idempotent next action. `user` and `external` ownership remains strict zero-mutation wait until explicit input transfer.
|
|
66
|
+
|
|
63
67
|
Generate a compact recovery report from snapshots, checkpoints, Hook events, Git, and workspace state. Return active work and failed tasks in `retryable_task_ids` by default; use `--include-closed` only when completed or abandoned details are required for an explicit audit. For each iteration, report total, completed, closed, remaining, active, retryable, queued, dependency-ready, and waiting tasks plus one deterministic `recommended_task_id`. Prefer an interrupted or active task, then the lowest-sequence failed task requiring `activate --new-attempt --phase repair`, then the lowest-sequence queued task whose dependencies are all `completed`. Resume only idempotent steps. Re-read production and external systems before any deployment recovery; never replay production deployment automatically.
|
|
64
68
|
|
|
65
69
|
## Hook Boundary
|
|
@@ -77,6 +77,8 @@ For an installed dual-repository project, `task-state.mjs` resolves `code_branch
|
|
|
77
77
|
|
|
78
78
|
`next_action_owner` is required in persisted state and has exactly three values: `agent`, `user`, or `external`; explicit null, empty, boolean, numeric, or unknown values are invalid. `user` and `external` are zero-mutation wait states. `recover` may report shared wait state but must not hydrate local task or resume files for it. After the required input arrives, `activate` requires both `--next-action-owner agent` and a new idempotent `--next-action`; it may atomically hydrate a missing local runtime snapshot from the matching shared shard and transfer ownership in the same transition. Same-revision local and shared content must have identical semantic checksums. Evidence arrays are ordered sets: replaying an already recorded `--evidence` value does not append a duplicate.
|
|
79
79
|
|
|
80
|
+
When waiting on a confirmation frontier, persist the summary path, `question_id`, `frontier_round`, and summary checksum in checkpoint evidence. Repeated answers are idempotent; superseding a decision appends a new record and never overwrites prior confirmation.
|
|
81
|
+
|
|
80
82
|
Pass `--state-root` when worktrees must write to one shared primary state directory. Pass `--worktree` during init so the script writes a local `.workflow/delivery/runtime/active-task.json` marker pointing to that state root. Later `checkpoint`, `finish`, and `recover` commands automatically use the marker's shared state root.
|
|
81
83
|
|
|
82
84
|
Use `finish` only with `completed`, `blocked`, `interrupted`, `failed`, or `abandoned`. Closed statuses `completed`, `failed`, and `abandoned` are rejected by `checkpoint`; use `checkpoint` only for continuing or paused work.
|
|
@@ -5,30 +5,35 @@ description: Close an independently verified planned version without publishing
|
|
|
5
5
|
|
|
6
6
|
# Close Planned Version
|
|
7
7
|
|
|
8
|
+
When closeout finds an unresolved decision or exception, validate the current `confirmation-summary.yaml` through `../ai-delivery-orchestrate/scripts/confirmation-state.mjs`; link confirmed decisions to closeout evidence and keep release intent separate.
|
|
9
|
+
|
|
8
10
|
Finish one planned version and stop before release. Never start a release from this skill.
|
|
9
11
|
|
|
10
12
|
## Preflight
|
|
11
13
|
|
|
12
14
|
1. Recover or initialize the `06-version-closeout` task through `$ai-delivery-checkpoint-task`.
|
|
13
15
|
2. Run `node .workflow/tools/state/state.mjs verify --history`.
|
|
14
|
-
3. Require a passing immutable candidate, a completed Candidate Assurance receipt for that same candidate, closed version tasks, complete requirement/test/material traceability, and no unresolved blocking defect.
|
|
16
|
+
3. Require a passing immutable candidate, a completed Candidate Assurance receipt for that same candidate, a resolved or superseded product impact assessment bound to the candidate, closed version tasks, complete requirement/test/material traceability, and no unresolved blocking defect.
|
|
15
17
|
4. Read the preceding version lineage and Git policy. Require the candidate to descend from the current `line/<product-id>` head, except for the first version.
|
|
16
18
|
|
|
17
19
|
## Close The Version
|
|
18
20
|
|
|
19
21
|
1. Read `.workflow/config/workspace.yaml`; verify the candidate in the configured code repository and populate `repo_id`, `code_commit`, `source_path`, and SHA-256.
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
22
|
-
4.
|
|
23
|
-
5.
|
|
24
|
-
6.
|
|
25
|
-
7.
|
|
26
|
-
8.
|
|
27
|
-
9.
|
|
28
|
-
10.
|
|
22
|
+
2. Validate the candidate's codebase handbook and navigation index at the exact code commit. Record their status, checksums, owner, last review, and any stale/invalid findings in closeout evidence; require repair or explicit maintainer acceptance before marking documentation evidence complete.
|
|
23
|
+
3. Freeze the candidate commit and create code `version-ready/<version-id>/rN`. Never move or replace an existing Tag; increment `rN` only for a newly verified replacement candidate.
|
|
24
|
+
4. Fast-forward code `line/<product-id>` to the tagged commit. Never advance code `main` here.
|
|
25
|
+
5. Copy the closeout assets into `.workflow/iterations/<version-id>/06-version-closeout/`, populate the code Tag identity, and compute the final manifest SHA-256.
|
|
26
|
+
6. Register the immutable closeout semantic revision directly as `release-ready` with the formal state CLI, and complete `06-version-closeout` with that registered artifact as evidence. Do not register a draft and mutate its status in place.
|
|
27
|
+
7. Commit all version workflow materials and create immutable workflow `workflow-ready/<version-id>/rN` at that exact workflow commit. Record the workflow commit, workflow Tag, and manifest checksum in the lineage index without rewriting the tagged manifest.
|
|
28
|
+
8. Verify both Tags and the manifest checksum. Inspect the current development scope revision, then run `state.mjs transition --expected-scope-revision <revision> --event version-closed --iteration-id <version-id> --artifact-id <closeout-artifact-id>` to update the audited cumulative development identity. Never edit `latest_line_version_id` directly.
|
|
29
|
+
9. Run `state.mjs snapshot create --id <version-id> --reason version-boundary`, then `state.mjs verify --history --since-anchor`. The first trusted anchor performs a full-history audit before creation; later anchors validate the suffix from the previous anchor.
|
|
30
|
+
10. Archive tasks with `task-state.mjs archive-version --version-status release-ready --candidate-id <candidate-id> --evidence <closeout-artifact-id>`. The command must first obtain `snapshot references` from the formal-state CLI and fail before writing if the anchor, scope, or candidate is missing. Verify that the version manifest references checksummed task summaries, complete attempt values, scope, candidate, Gate decisions, and the version-boundary anchor; then checkpoint the Git, anchor, and archive evidence and finish the task.
|
|
31
|
+
11. Invoke `$ai-delivery-evolve-workflow evaluate --safe-point version --iteration-id <version-id>`. Record trial results and decisions, but never use an Evolution result as release intent.
|
|
29
32
|
|
|
30
33
|
## Stop Boundary
|
|
31
34
|
|
|
32
35
|
Report the release-ready version, code Tag, workflow Tag, manifest checksum, cumulative line head, archived task count, and next planned version. Stop and wait for either the next version iteration or an explicit user release request. Do not invoke release orchestration implicitly.
|
|
33
36
|
|
|
34
37
|
Read [version-closeout-contract.md](references/version-closeout-contract.md) before writing outputs.
|
|
38
|
+
|
|
39
|
+
Before closeout, validate the confirmation summary and ensure confirmed product, architecture, standard, and risk decisions have authority references and matching checksums. Deferred or open decisions remain visible in closeout evidence. Closing a version does not imply release intent.
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- planned version ID, product ID, predecessor version, and iteration plan;
|
|
6
6
|
- immutable candidate ID, commit, artifact digest, and passing verification decision;
|
|
7
|
+
- resolved or superseded `product-impact-assessment.yaml` bound to the candidate and the aligned product baseline;
|
|
7
8
|
- complete closed-task summary and unresolved-risk statement;
|
|
8
9
|
- current `line/<product-id>` head and applicable standards baseline.
|
|
9
10
|
|
|
@@ -17,4 +18,4 @@ After Candidate Assurance has completed for the same immutable candidate, the `r
|
|
|
17
18
|
|
|
18
19
|
## Failure rules
|
|
19
20
|
|
|
20
|
-
Block closeout for active tasks, mutable candidates, failed verification, missing materials, Git divergence, an existing tag resolving to another commit, or a candidate that does not include a synchronized production hotfix. Mark affected candidates `stale`; never edit a previously closed version.
|
|
21
|
+
Block closeout for active tasks, mutable candidates, failed verification, missing materials, an unresolved or checksum-drifted product impact assessment, Git divergence, an existing tag resolving to another commit, or a candidate that does not include a synchronized production hotfix. Mark affected candidates `stale`; never edit a previously closed version.
|