create-yss-spec 3.5.4 → 3.5.6

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 (105) hide show
  1. package/README.md +10 -0
  2. package/cli-core.lock.json +130 -0
  3. package/package.json +7 -3
  4. package/resources/upgrade-skill/SKILL.md +26 -0
  5. package/resources/upgrade-skill/protocol.md +40 -0
  6. package/resources/upgrade-skill/references/cli-families.md +14 -0
  7. package/src/cli/help.js +1 -0
  8. package/src/cli/index.js +12 -0
  9. package/src/filesystem/transaction-runner.js +27 -0
  10. package/src/template/asset-runtime.js +9 -1
  11. package/src/template/instance-runtime.js +4 -2
  12. package/src/template/verification-runtime.js +9 -1
  13. package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
  14. package/template/.agents/skills/yss-harness-upgrade/SKILL.md +26 -0
  15. package/template/.agents/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  16. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  17. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +15 -19
  18. package/template/.agents/skills/yss-stage-decision/SKILL.md +3 -0
  19. package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  20. package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  21. package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  22. package/template/.codex/skills/yss-harness-upgrade/SKILL.md +26 -0
  23. package/template/.codex/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  24. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  25. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +15 -19
  26. package/template/.codex/skills/yss-stage-decision/SKILL.md +3 -0
  27. package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  28. package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  29. package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  30. package/template/.cursor/skills/yss-harness-upgrade/SKILL.md +26 -0
  31. package/template/.cursor/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  32. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  33. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +15 -19
  34. package/template/.cursor/skills/yss-stage-decision/SKILL.md +3 -0
  35. package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  36. package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  37. package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  38. package/template/.pi/skills/yss-harness-upgrade/SKILL.md +26 -0
  39. package/template/.pi/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  40. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  41. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +15 -19
  42. package/template/.pi/skills/yss-stage-decision/SKILL.md +3 -0
  43. package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  44. package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  45. package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  46. package/template/.template-spec/agents/yss-skill-registry.yaml +9 -1
  47. package/template/.template-spec/process/contract-reading.md +1 -1
  48. package/template/.template-spec/process/document-writing.md +12 -0
  49. package/template/.template-spec/process/harness-upgrade.md +40 -0
  50. package/template/.template-spec/process/lifecycle-artifact-map.md +2 -2
  51. package/template/.template-spec/process/lifecycle-registry.yaml +3 -1
  52. package/template/.template-spec/process/schemas/approval-record.schema.json +23 -0
  53. package/template/.template-spec/process/stage-tracking.md +2 -2
  54. package/template/.template-spec/process/structured-assets.md +35 -0
  55. package/template/.template-spec/process/templates/lifecycle-checkpoint-template.json +48 -0
  56. package/template/.template-spec/templates/approval-record-template.json +23 -0
  57. package/template/scripts/check-plan-stage-coverage +12 -0
  58. package/template/scripts/contract +6 -0
  59. package/template/scripts/lib/approval-record.mjs +4 -3
  60. package/template/scripts/lib/asset-command.mjs +35 -0
  61. package/template/scripts/lib/asset-migration.mjs +101 -0
  62. package/template/scripts/lib/asset-transactions.mjs +235 -0
  63. package/template/scripts/lib/checkpoint-boundary.mjs +3 -0
  64. package/template/scripts/lib/contract-views.mjs +7 -1
  65. package/template/scripts/lib/json-schema.mjs +43 -4
  66. package/template/scripts/lib/lifecycle-context-query.mjs +7 -0
  67. package/template/scripts/lib/lifecycle-presentation.mjs +77 -0
  68. package/template/scripts/lib/lifecycle-status.mjs +16 -1
  69. package/template/scripts/lib/plan-stage-coverage.mjs +40 -0
  70. package/template/scripts/lib/reading-view-bundle.mjs +1 -0
  71. package/template/scripts/lib/reading-view-markdown.mjs +8 -6
  72. package/template/scripts/lib/stage-tracking-migration.mjs +11 -3
  73. package/template/scripts/lib/structured-assets.mjs +81 -0
  74. package/template/scripts/lib/task-package.mjs +4 -1
  75. package/template/scripts/lifecycle-status +6 -3
  76. package/template/scripts/verify-lifecycle-checkpoint +8 -5
  77. package/template/skills-lock.json +21 -9
  78. package/template.manifest.json +2 -1
  79. package/template.snapshot.json +5 -5
  80. package/vendor/cli-core/build.mjs +302 -0
  81. package/vendor/cli-core/bundle.mjs +198 -0
  82. package/vendor/cli-core/cli.mjs +182 -0
  83. package/vendor/cli-core/command-runner.mjs +107 -0
  84. package/vendor/cli-core/engine.mjs +239 -0
  85. package/vendor/cli-core/family.mjs +28 -0
  86. package/vendor/cli-core/gitignore.mjs +15 -0
  87. package/vendor/cli-core/identity.mjs +274 -0
  88. package/vendor/cli-core/io.mjs +153 -0
  89. package/vendor/cli-core/layout-migration.mjs +64 -0
  90. package/vendor/cli-core/legacy.mjs +19 -0
  91. package/vendor/cli-core/migrate.mjs +237 -0
  92. package/vendor/cli-core/migration-adapter.mjs +19 -0
  93. package/vendor/cli-core/package.json +13 -0
  94. package/vendor/cli-core/scaffold.mjs +117 -0
  95. package/vendor/cli-core/spec-migration-adapter.mjs +83 -0
  96. package/vendor/cli-core/tests/build.test.mjs +40 -0
  97. package/vendor/cli-core/tests/cli.test.mjs +1065 -0
  98. package/vendor/cli-core/tests/migration-version.test.mjs +8 -0
  99. package/vendor/cli-core/tests/package-contract.mjs +89 -0
  100. package/vendor/cli-core/transaction.mjs +529 -0
  101. package/vendor/cli-core/update.mjs +79 -0
  102. package/vendor/cli-core/vendor/NOTICES.md +6 -0
  103. package/vendor/cli-core/vendor/yaml.LICENSE +13 -0
  104. package/vendor/cli-core/vendor/yaml.mjs +6311 -0
  105. package/vendor/cli-core/verification.mjs +131 -0
@@ -6,6 +6,7 @@ import { readFileSync } from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { parseDocument } from '../vendor/yaml.mjs';
8
8
  import { safeTrackingPath, trackingDrift } from './stage-tracking.mjs';
9
+ import { loadLifecyclePresenter } from './lifecycle-presentation.mjs';
9
10
 
10
11
  const sha = bytes => `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
11
12
  function parse(bytes) {
@@ -52,7 +53,7 @@ export function lifecycleStatus({ root, checkpointRef, taskPackageRef }) {
52
53
  if (value.next_work_unit && !workUnit) issue('unknown-work-unit', `未知下一工作单元: ${value.next_work_unit}`, registryRef, '按注册表核对下一工作单元');
53
54
  if (workUnit && workUnit.id !== 'work-unit.entry-triage' && !contract.value.work_unit_routes?.[workUnit.id]) issue('missing-route', `缺少执行路由: ${workUnit.id}`, contractRef, '修复当前合同路由后重验');
54
55
  check('stage-and-route', diagnostics.some(x => ['unknown-stage', 'unknown-work-unit', 'missing-route'].includes(x.code)) ? 'failed' : 'passed', [registryRef, contractRef], '仅检查身份与路由存在,不计算完整 frontier');
55
- if (value.context_reconciliation?.status === 'blocked') issue('context-blocked', 'Context reconciliation blocked', checkpointRef, '修复词汇对账并执行适用校验');
56
+ if (value.context_reconciliation?.status === 'blocked') issue('context-blocked', '术语对账受阻', checkpointRef, '修复词汇对账并执行适用校验');
56
57
  check('context-reconciliation', value.context_reconciliation?.status === 'blocked' ? 'failed' : 'not-checked', [value.context_reconciliation?.ref].filter(Boolean), '读取登记状态;当前对账内容和摘要仍须由原验证器核验');
57
58
  for (const [id, gate] of Object.entries(value.gates ?? {})) {
58
59
  if (['blocked', 'stale'].includes(gate?.status)) issue('gate-blocked', `${id}: ${gate.status}`, checkpointRef, '核对受影响依据和当前批准');
@@ -142,7 +143,21 @@ export function lifecycleStatus({ root, checkpointRef, taskPackageRef }) {
142
143
  }
143
144
  const implementationArtifact=value.artifacts?.['artifact.vertical-slice-ticket'];
144
145
  const implementationCoverage=setRef && implementationArtifact?.ref?.endsWith('.md')?summarizeBusinessImplementation({root,setRef,sliceRefs:[implementationArtifact.ref]}):null;
146
+ const presenter = loadLifecyclePresenter(root);
147
+ const statusNames = { routing: '正在确定下一步', running: '正在推进', blocked: '受阻', 'paused-human-gate': '等待会签', completed: '记录为已完成,仍需核验' };
148
+ const readable = text => presenter.text(text, {trace: false}).replace(/: (blocked|stale)$/, (_, status) => status === 'blocked' ? ':受阻' : ':依据已过期,需重新核验');
149
+ const presentation = {
150
+ read_only: true, execution_allowed: false, approval_validity: 'not-checked',
151
+ stage: value.stage ? presenter.label(value.stage, {trace: false}) : '未登记',
152
+ checkpoint_status: Object.hasOwn(statusNames, value.status) ? statusNames[value.status] : `未识别状态(${value.status ?? '未登记'})`,
153
+ work_unit: value.next_work_unit ? presenter.label(value.next_work_unit, {trace: false}) : '未登记',
154
+ owner: presenter.text(owner, {trace: false}), blockers: blockers.map(readable),
155
+ expected_output: workUnit ? readable(workUnit.public_output ?? workUnit.output ?? '产出说明未登记') : null,
156
+ next_action: readable(next_action), sources: presenter.sources, warnings: presenter.warnings,
157
+ names: presenter.catalog({stage: value.stage, work_unit: value.next_work_unit, owner, gates: value.gates, artifacts: value.artifacts}),
158
+ };
145
159
  return {
160
+ presentation,
146
161
  decomposition: {business, implementation: {status:implementationArtifact?'recorded':'not-recorded',recorded_status:implementationArtifact?.status??null,ref:implementationArtifact?.ref??null,coverage:implementationCoverage}, readiness:{status:'not-evaluated',reason:'阶段工作完成不替代批准与实现就绪校验'}},
147
162
  schema_version: 1, read_only: true, stage: value.stage, checkpoint_status: value.status,
148
163
  work_unit: value.next_work_unit ?? null, owner, blockers, next_action,
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs';
2
+ import { parseMarkdown } from '../vendor/markdown.mjs';
3
+ import { plain } from './plan-spec-markdown.mjs';
4
+ import { parseAsset, byteDigest } from './structured-assets.mjs';
5
+ import { safeFile } from './governance-io.mjs';
6
+
7
+ function tables(source) {
8
+ let heading='';const result=[];
9
+ for(const node of parseMarkdown(source).children) {
10
+ if(node.type==='heading')heading=plain(node);
11
+ if(node.type!=='table')continue;
12
+ const headers=node.children[0].children.map(plain);
13
+ for(const row of node.children.slice(1))result.push({heading,headers,cells:row.children.map(plain),line:row.position.start.line});
14
+ }
15
+ return result;
16
+ }
17
+ /** Coverage is deterministic; equivalence of rewritten business statements requires independent review. */
18
+ export function planStageCoverage(root,{plan_ref,decisions_ref,package_ref,mapping_ref}) {
19
+ const bytes=ref=>fs.readFileSync(safeFile(root,ref));
20
+ const plan=bytes(plan_ref),decisions=bytes(decisions_ref),pkgBytes=bytes(package_ref),pkg=parseAsset(pkgBytes,package_ref);
21
+ const mapping=mapping_ref?parseAsset(bytes(mapping_ref),mapping_ref):{non_goals:[]};
22
+ const sourceBindings={plan:{ref:plan_ref,digest:byteDigest(plan)},decisions:{ref:decisions_ref,digest:byteDigest(decisions)}};
23
+ if(mapping_ref && (mapping.plan_digest!==sourceBindings.plan.digest||mapping.package_digest!==byteDigest(pkgBytes)))throw new Error('PLAN_COVERAGE_MAPPING_STALE');
24
+ const sourceDecisions=tables(decisions.toString()).filter(r=>/^D[0-9]+$/.test(r.cells[0])&&r.headers.includes('决定'));
25
+ const sourceNonGoals=tables(plan.toString()).filter(r=>r.headers.some(x=>/MVP.*非目标/.test(x))&&/^非目标/.test(r.cells[r.headers.findIndex(x=>/MVP.*非目标/.test(x))]||''));
26
+ if(!sourceDecisions.length||!sourceNonGoals.length)throw new Error('PLAN_COVERAGE_UNASSESSED: supported decision/non-goal tables were not found');
27
+ if(new Set(sourceDecisions.map(r=>r.cells[0])).size!==sourceDecisions.length)throw new Error('PLAN_COVERAGE_DUPLICATE_SOURCE_ID');
28
+ const rows=sourceDecisions.map(row=>{
29
+ const id=row.cells[0],targets=(pkg.confirmed_decisions||[]).filter(item=>new RegExp(`^decision\\.plan-${id.toLowerCase()}(?:-|$)`).test(item.id));
30
+ return {source_ref:decisions_ref,source_id:id,source_line:row.line,source_text:row.cells.join(' | '),targets:targets.map(x=>({id:x.id,statement:x.statement})),status:targets.length===1?'mapped':'missing-or-ambiguous',semantic_review:'required'};
31
+ });
32
+ const nonGoals=sourceNonGoals.map(row=>{
33
+ const sourceText=row.cells[0],entry=(mapping.non_goals||[]).find(x=>x.source_text===sourceText&&x.source_line===row.line);
34
+ const target=Number.isInteger(entry?.target_index)?pkg.non_goals?.[entry.target_index]:undefined;
35
+ return {source_ref:plan_ref,source_line:row.line,source_text:sourceText,target_index:entry?.target_index??null,target_text:target??null,status:typeof target==='string'&&target===entry.target_text?'mapped':'unmapped',semantic_review:'required'};
36
+ });
37
+ const known=new Set(sourceDecisions.map(r=>r.cells[0].toLowerCase()));
38
+ const undeclared=(pkg.confirmed_decisions||[]).filter(r=>/^decision\.plan-d\d+(?:-|$)/.test(r.id)&&!known.has(r.id.match(/^decision\.plan-(d\d+)/)[1])).map(r=>r.id);
39
+ return {schema_version:1,kind:'plan-stage-coverage',sources:sourceBindings,subject:{ref:package_ref,digest:byteDigest(pkgBytes)},...(mapping_ref?{mapping:{ref:mapping_ref,digest:byteDigest(bytes(mapping_ref))}}:{}),decisions:rows,non_goals:nonGoals,undeclared_decision_ids:undeclared,coverage_status:rows.every(r=>r.status==='mapped')&&nonGoals.every(r=>r.status==='mapped')&&!undeclared.length?'complete':'incomplete',semantic_review:'required',execution_authorization:'not-granted'};
40
+ }
@@ -50,6 +50,7 @@ function build(root,checkpoint){
50
50
  for(const asset of assets){
51
51
  bindReferences(parseSliceYaml(bytes(root,asset.ref)));
52
52
  const view=viewContract(asset.ref,{root,kind:asset.kind});
53
+ bindReferences(view.presentation);
53
54
  diagnostics.push(...view.blockers.map(reason=>({ref:asset.ref,reason})));
54
55
  const name=asset.kind==='checkpoint'?'status':asset.kind;
55
56
  outputs[`${loc.directory}/${name}.review.md`]=view.markdown;
@@ -14,14 +14,15 @@ const scalar=value=>value===null?'null':String(value);
14
14
  function textLines(value,indent=''){
15
15
  return scalar(value).split(/\r?\n/).map((line,index)=>`${index?'\n'+indent+'> ':''}${escapeMarkdown(line)}`).join('');
16
16
  }
17
- export function renderReadingValue(value,depth=0){
18
- if(value===null||typeof value!=='object')return textLines(value);
17
+ export function renderReadingValue(value,depth=0,names={}){
18
+ const named=value=>typeof value==='string'&&Object.hasOwn(names,value)?`${names[value].name}(${value})`:value;
19
+ if(value===null||typeof value!=='object')return textLines(named(value));
19
20
  const entries=Object.entries(value);
20
21
  if(!entries.length)return Array.isArray(value)?'[](来源明确为空)':'{}(来源明确为空)';
21
22
  return entries.map(([key,child])=>{
22
- if(Array.isArray(value)&&(child===null||typeof child!=='object'))return '- '+textLines(child).replace(/\n/g,'\n ');
23
- const label=Array.isArray(value)?`第 ${Number(key)+1} 项`:escapeMarkdown(readingLabels[key]||key);
24
- const rendered=renderReadingValue(child,depth+1).split('\n');
23
+ if(Array.isArray(value)&&(child===null||typeof child!=='object'))return '- '+textLines(named(child)).replace(/\n/g,'\n ');
24
+ const label=Array.isArray(value)?`第 ${Number(key)+1} 项`:escapeMarkdown(Object.hasOwn(readingLabels,key)?readingLabels[key]:named(key));
25
+ const rendered=renderReadingValue(child,depth+1,names).split('\n');
25
26
  return child!==null&&typeof child==='object'&&Object.keys(child).length?`- **${label}**:\n${rendered.map(line=>' '+line).join('\n')}`:`- **${label}**:${rendered[0]}${rendered.slice(1).map(line=>'\n '+line).join('')}`;
26
27
  }).join('\n');
27
28
  }
@@ -32,8 +33,9 @@ export function renderReadingMarkdown(view){
32
33
  '本页为来源快照;当前有效性需重新检查。批准有效性未核验;本视图不授予执行权限。',
33
34
  `检查范围:${view.checks.map(escapeMarkdown).join(';')}`];
34
35
  for(const [key,value]of Object.entries(view.content))if(value!==undefined){
35
- lines.push(`## ${readingLabels[key]||escapeMarkdown(key)}`,key==='正文'&&typeof value==='string'?value:renderReadingValue(value));
36
+ lines.push(`## ${readingLabels[key]||escapeMarkdown(key)}`,key==='正文'&&typeof value==='string'?value:renderReadingValue(value,0,view.presentation?.names));
36
37
  }
38
+ if(view.presentation?.warnings.length)lines.push('## 名称来源提示',...view.presentation.warnings.map(escapeMarkdown));
37
39
  if(view.blockers.length)lines.push('## 阻断与未决检查',...view.blockers.map(item=>`- ${escapeMarkdown(item)}`));
38
40
  return lines.join('\n\n')+'\n';
39
41
  }
@@ -1,4 +1,6 @@
1
1
  import {finalizeReading} from './reading-view-bundle.mjs';
2
+ import {runAssetTransaction,assertAssetTransactionIdle,assertCurrentAssetReference} from './asset-transactions.mjs';
3
+ import {parseAsset} from './structured-assets.mjs';
2
4
  import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync, statSync, openSync, closeSync } from 'node:fs';
3
5
  import path from 'node:path';
4
6
  import { parseDocument, stringify } from '../vendor/yaml.mjs';
@@ -27,17 +29,20 @@ function definition(item, checkpointRef) {
27
29
  return `---\n${stringify(fields)}---\n# ${item.title}\n\n负责人:${item.owner}\n\n${item.scope}\n\n## 验收\n\n${item.acceptance.map(x => `- ${x}`).join('\n')}\n\n当前进度、依赖与完成证据以 ${checkpointRef} 中的 ${item.id} 为准。\n`;
28
30
  }
29
31
  export function checkTracking(root, checkpointRef) {
32
+ assertAssetTransactionIdle(root);assertCurrentAssetReference(root,checkpointRef);
30
33
  identity(root);
31
34
  const feature = featureFrom(checkpointRef);
32
35
  const config = trackerConfig(root);
33
36
  if (!existsSync(safeTrackingPath(root, checkpointRef))) return { status: 'missing-checkpoint', feature_id: feature, enabled: config.lifecycle_tracking_version === 1 };
34
- const checkpoint = parseYaml(readTracking(root, checkpointRef));
37
+ const checkpoint = parseAsset(readTracking(root, checkpointRef),checkpointRef);
35
38
  try {
36
39
  const result = assertStageTracking(checkpoint, { root, checkpointRef });
37
40
  return { ...result, feature_id: checkpoint.feature_id || feature, enabled: config.lifecycle_tracking_version === 1, migration_required: !checkpoint.stage_tracking };
38
41
  } catch (error) { return { status: 'blocked', feature_id: feature, enabled: config.lifecycle_tracking_version === 1, error: error.message, migration_required: !checkpoint.stage_tracking }; }
39
42
  }
40
43
  export function planTracking(root, { checkpoint_ref, items = [], refresh = false } = {}) {
44
+ assertAssetTransactionIdle(root);assertCurrentAssetReference(root,checkpoint_ref);
45
+ if (!checkpoint_ref?.endsWith('.json')) throw new Error('ASSET_MIGRATION_REQUIRED: stage-tracking writes require an explicit JSON checkpoint');
41
46
  root = path.resolve(root); identity(root);
42
47
  const feature = featureFrom(checkpoint_ref);
43
48
  trackerConfig(root);
@@ -50,9 +55,10 @@ export function planTracking(root, { checkpoint_ref, items = [], refresh = false
50
55
  for (const ref of ['yss-project.yaml', 'CONTEXT.md', TRACKER_REF, '.template-spec/process/lifecycle-registry.yaml', '.template-spec/process/schemas/stage-tracking.schema.json', '.template-spec/process/harness-profile.yaml', checkpoint_ref]) watch(ref);
51
56
  const exists = observed[checkpoint_ref] !== null;
52
57
  let checkpoint;
53
- if (exists) checkpoint = parseYaml(readTracking(root, checkpoint_ref));
58
+ if (exists) checkpoint = parseAsset(readTracking(root, checkpoint_ref), checkpoint_ref);
54
59
  else {
55
- const template = '.template-spec/process/templates/lifecycle-checkpoint-template.yaml'; watch(template);
60
+ const preferred = '.template-spec/process/templates/lifecycle-checkpoint-template.json';
61
+ const template = existsSync(safeTrackingPath(root,preferred))?preferred:'.template-spec/process/templates/lifecycle-checkpoint-template.yaml'; watch(template);
56
62
  checkpoint = parseYaml(readTracking(root, template));
57
63
  checkpoint.feature_id = feature;
58
64
  checkpoint.mode = 'orchestrate'; checkpoint.status = 'running';
@@ -141,6 +147,7 @@ function applyTrackingSources(root, plan, { afterWrite } = {}) {
141
147
  const backups = plan.changes.map(c => ({ ref: c.ref, before: c.before, bytes: c.before ? readFileSync(safeTrackingPath(root, c.ref)).toString('base64') : null }));
142
148
  writeFileSync(path.join(transactionPath, 'backup.json'), json(backups), { flag: 'wx' });
143
149
  writeFileSync(path.join(transactionPath, 'plan.json'), json(plan), { flag: 'wx' });
150
+ return runAssetTransaction(root,plan.changes,()=>{
144
151
  const written = [], temporary = new Set();
145
152
  try {
146
153
  for (const [ref, before] of Object.entries(plan.observed)) if (JSON.stringify(descriptor(root, ref)) !== JSON.stringify(before)) throw new Error('tracking-concurrent-change');
@@ -175,6 +182,7 @@ function applyTrackingSources(root, plan, { afterWrite } = {}) {
175
182
  writeFileSync(path.join(transactionPath, 'failure.json'), json({ error: error.message, recovery_conflicts: conflicts }));
176
183
  throw new Error(`${error.message}; tracking-rollback-${conflicts.length ? `conflict:${conflicts.join(',')}` : 'complete'}`);
177
184
  }
185
+ });
178
186
  }
179
187
 
180
188
  // Finalize after the source transaction has committed; presentation failure never rolls it back.
@@ -0,0 +1,81 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { createHash } from 'node:crypto';
5
+ import { parseDocument, YAMLMap, YAMLSeq } from '../vendor/yaml.mjs';
6
+ import { validateJsonSchema } from './json-schema.mjs';
7
+
8
+ export const TOOL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
9
+ export const ASSET_KINDS = Object.freeze(['checkpoint', 'domain-strategy', 'stage-decision-package', 'task-package', 'approval-record']);
10
+ export const byteDigest = bytes => `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
11
+ export const canonicalValue = value => Array.isArray(value) ? value.map(canonicalValue) : value && typeof value === 'object' ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonicalValue(value[key])])) : value;
12
+ export const semanticDigest = value => byteDigest(JSON.stringify(canonicalValue(value)));
13
+ export const serializeAsset = value => `${JSON.stringify(canonicalValue(value), null, 2)}\n`;
14
+ const isMap = node => node instanceof YAMLMap;
15
+ const isSeq = node => node instanceof YAMLSeq;
16
+ const isScalar = node => node && Object.hasOwn(node, 'value');
17
+
18
+ export function assetComments(bytes) {
19
+ const doc = parseDocument(String(bytes), { uniqueKeys: true }), result = [];
20
+ function visit(node, location) {
21
+ if (!node || typeof node !== 'object') return;
22
+ for (const key of ['comment', 'commentBefore']) if (node[key]) result.push({ location, text: node[key] });
23
+ if (node.contents) visit(node.contents, '<root>');
24
+ if (isMap(node)) for (const pair of node.items) { visit(pair.key, `${location}.${pair.key?.value}`); visit(pair.value, `${location}.${pair.key?.value}`); }
25
+ if (isSeq(node)) node.items.forEach((item, index) => visit(item, `${location}[${index}]`));
26
+ }
27
+ visit(doc, '<document>'); return result;
28
+ }
29
+
30
+ /** JSON syntax is strict; the YAML AST supplies duplicate-key and lossless-type checks. */
31
+ export function parseAsset(bytes, file = 'asset.yaml') {
32
+ const source = Buffer.isBuffer(bytes) ? new TextDecoder('utf-8', { fatal: true }).decode(bytes) : String(bytes);
33
+ if (/\.json$/i.test(file)) JSON.parse(source);
34
+ const doc = parseDocument(source, { uniqueKeys: true, maxAliasCount: 0, intAsBigInt: true });
35
+ if (doc.errors.length || doc.warnings.length) throw new TypeError(`ASSET_PARSE: ${file}: ${(doc.errors[0] || doc.warnings[0]).message}`);
36
+ function visit(node) {
37
+ if (!node) return;
38
+ if (node.anchor || node.tag) throw new TypeError(`ASSET_TYPE: anchors/tags are unsupported: ${file}`);
39
+ if (isMap(node)) for (const pair of node.items) {
40
+ if (!isScalar(pair.key) || typeof pair.key.value !== 'string') throw new TypeError(`ASSET_TYPE: keys must be strings: ${file}`);
41
+ visit(pair.value);
42
+ }
43
+ else if (isSeq(node)) node.items.forEach(visit);
44
+ else if (isScalar(node)) {
45
+ if (typeof node.value === 'bigint') {
46
+ const number = Number(node.value);
47
+ if (!Number.isSafeInteger(number)) throw new TypeError(`ASSET_TYPE: unsafe integer: ${file}`);
48
+ node.value = number;
49
+ }
50
+ if (typeof node.value === 'number' && !Number.isFinite(node.value)) throw new TypeError(`ASSET_TYPE: non-finite number: ${file}`);
51
+ if (typeof node.value === 'number' && Number.isInteger(node.value) && !Number.isSafeInteger(node.value)) throw new TypeError(`ASSET_TYPE: unsafe integer: ${file}`);
52
+ } else throw new TypeError(`ASSET_TYPE: aliases are unsupported: ${file}`);
53
+ }
54
+ visit(doc.contents);
55
+ const value = doc.toJS({ maxAliasCount: 0 });
56
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError(`ASSET_TYPE: root must be an object: ${file}`);
57
+ return value;
58
+ }
59
+
60
+ export function assetSchema(kind, value, root = TOOL_ROOT) {
61
+ if (!ASSET_KINDS.includes(kind)) throw new TypeError(`ASSET_KIND: ${kind}`);
62
+ if (['domain-strategy', 'stage-decision-package'].includes(kind)) {
63
+ if (![2, 3].includes(value.schema_version)) throw new TypeError('migration-required: unsupported strategy schema_version');
64
+ const authoring = path.join(root, `.agents/skills/yss-stage-decision/references/${kind}.schema.json`);
65
+ return fs.existsSync(authoring) ? authoring : path.join(root, `.template-spec/process/schemas/${kind}-reading.schema.json`);
66
+ }
67
+ const name = { checkpoint: 'lifecycle-checkpoint', 'task-package': 'digital-human-task-package', 'approval-record': value.kind === 'review-bundle' ? 'review-bundle' : 'approval-record' }[kind];
68
+ return path.join(root, `.template-spec/process/schemas/${name}.schema.json`);
69
+ }
70
+
71
+ export function validateAssetStructure(value, kind, { schemaRoot = TOOL_ROOT } = {}) {
72
+ const schemaPath = assetSchema(kind, value, schemaRoot);
73
+ validateJsonSchema(value, schemaPath, { cwd: schemaRoot });
74
+ return { kind, schema_version: value.schema_version, schema: path.relative(schemaRoot, schemaPath), semantic_digest: semanticDigest(value), execution_authorization: 'not-evaluated' };
75
+ }
76
+
77
+ export function readAsset(file, kind, options = {}) {
78
+ const bytes = fs.readFileSync(file), value = parseAsset(bytes, file);
79
+ validateAssetStructure(value, kind, options);
80
+ return { value, bytes, digest: byteDigest(bytes) };
81
+ }
@@ -1,4 +1,6 @@
1
1
  import { validateReadOnlyIntake } from './read-only-intake.mjs';
2
+ import { parseAsset } from './structured-assets.mjs';
3
+ import { assertAssetTransactionIdle } from './asset-transactions.mjs';
2
4
  import { assertTrackingEntry } from './stage-tracking.mjs';
3
5
  import { normalizeSliceContract } from './slice-contract.mjs';
4
6
  import { assertSliceV3TaskPackage } from './slice-task-package.mjs';
@@ -56,7 +58,7 @@ function assertReadableEvidenceRef(value, field) {
56
58
  export function loadTaskPackage(filePath) {
57
59
  const sourcePath = path.resolve(filePath);
58
60
  if (!existsSync(sourcePath)) fail(`任务包不存在: ${filePath}`);
59
- const value = parseYaml(readFileSync(sourcePath, "utf8"), "任务包");
61
+ const value = parseAsset(readFileSync(sourcePath), sourcePath);
60
62
  if (!value || typeof value !== "object" || Array.isArray(value)) fail("任务包必须是对象");
61
63
  return { value, sourcePath };
62
64
  }
@@ -226,6 +228,7 @@ function validateTaskPackageInternal(value, { rolesDoc, lifecycleDoc, root = ROO
226
228
  }
227
229
 
228
230
  export function validateTaskPackage(value, options = {}) {
231
+ assertAssetTransactionIdle(ROOT);
229
232
  return validateTaskPackageInternal(value, {...options, history:false});
230
233
  }
231
234
 
@@ -3,17 +3,20 @@ import { parseArgs } from 'node:util';
3
3
  import path from 'node:path';
4
4
  import { checkProjectGovernance } from './lib/project-governance.mjs';
5
5
  import { lifecycleStatus } from './lib/lifecycle-status.mjs';
6
+ import { renderLifecycleStatus } from './lib/lifecycle-presentation.mjs';
6
7
 
7
8
  try {
8
- const { values } = parseArgs({ strict: true, options: { root: { type: 'string' }, checkpoint: { type: 'string' }, task: {type: 'string'}, preflight: {type:'boolean'}, help:{type:'boolean'} } });
9
- if(values.help)process.stdout.write('用法: scripts/lifecycle-status --root <项目> [--checkpoint <根相对路径>] [--task <任务包>] [--preflight]\n普通查询不执行预检;--preflight 复验当前证据,仅提供恢复动作,不批准或派发。普通查询退出码保持 0/1;预检 0=通过、1=失败、2=输入或能力异常。\n');
9
+ const { values } = parseArgs({ strict: true, options: { root: { type: 'string' }, checkpoint: { type: 'string' }, task: {type: 'string'}, preflight: {type:'boolean'}, help:{type:'boolean'}, format: {type:'string', default:'json'} } });
10
+ if (!['json', 'text'].includes(values.format)) throw Error('--format 仅支持 json 或 text');
11
+ if (values.preflight && values.format === 'text') throw Error('--preflight 使用 JSON 诊断;中文状态阅读请单独调用 --format text');
12
+ if(values.help)process.stdout.write('用法: scripts/lifecycle-status --root <项目> [--checkpoint <根相对路径>] [--task <任务包>] [--preflight] [--format json|text]\n普通查询不执行预检;--preflight 复验当前证据,仅提供恢复动作,不批准或派发。普通查询退出码保持 0/1;预检 0=通过、1=失败、2=输入或能力异常。\n');
10
13
  else if(values.preflight) {
11
14
  if(!values.checkpoint&&!values.task)throw new Error('--preflight 必须指定 --checkpoint 或 --task');
12
15
  const {report,exitCode}=checkProjectGovernance({root:path.resolve(values.root??'.'),checkpointRef:values.checkpoint,taskRef:values.task,recovery:true});
13
16
  process.stdout.write(JSON.stringify(report,null,2)+'\n');process.exitCode=exitCode;
14
17
  } else {
15
18
  const result = lifecycleStatus({ root: path.resolve(values.root ?? '.'), checkpointRef: values.checkpoint, taskPackageRef: values.task });
16
- process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
19
+ process.stdout.write(values.format === 'text' ? renderLifecycleStatus(result) : `${JSON.stringify(result, null, 2)}\n`);
17
20
  if (result.blockers.length) process.exitCode = 1;
18
21
  }
19
22
  } catch (error) {
@@ -12,6 +12,8 @@ import { assertCheckpointApprovals, assertCheckpointUserDecisions } from "./lib/
12
12
  import { loadExecutionScope, assertScopeWorkUnit, verifyDeliveryTerminal } from './lib/lifecycle-execution-scope.mjs';
13
13
  import { assertPlanCheckpoint } from "./lib/plan-spec-entry.mjs";
14
14
  import { assertCheckpointBoundary } from './lib/checkpoint-boundary.mjs';
15
+ import { parseAsset, validateAssetStructure } from './lib/structured-assets.mjs';
16
+ import { assertCurrentAssetReference } from './lib/asset-transactions.mjs';
15
17
 
16
18
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
17
19
  const schemaPath = path.join(root, ".template-spec/process/schemas/lifecycle-checkpoint.schema.json");
@@ -37,11 +39,12 @@ try {
37
39
  else {
38
40
  if (positionals.length !== 1) throw new TypeError("用法: scripts/verify-lifecycle-checkpoint <checkpoint.yaml|checkpoint.json>");
39
41
  const sourcePath = path.resolve(positionals[0]);
40
- const document = parseDocument(readFileSync(sourcePath, "utf8"), { maxAliasCount: 0, uniqueKeys: true });
41
- if (document.errors.length) throw new TypeError(document.errors[0].message);
42
- const value = document.toJS({ maxAliasCount: 0 });
43
- const result = spawnSync("python3", ["-c", validator, schemaPath,values.history?'history':'current'], { cwd: root, encoding: "utf8", input: JSON.stringify(value) });
44
- if (result.status !== 0) throw new TypeError(`${result.stdout}${result.stderr}`.trim());
42
+ if(!values.history)assertCurrentAssetReference(root,path.relative(root,sourcePath));
43
+ const value = parseAsset(readFileSync(sourcePath), sourcePath);
44
+ if (values.history) {
45
+ const result = spawnSync("python3", ["-c", validator, schemaPath,'history'], { cwd: root, encoding: "utf8", input: JSON.stringify(value) });
46
+ if (result.status !== 0) throw new TypeError(`${result.stdout}${result.stderr}`.trim());
47
+ } else validateAssetStructure(value, 'checkpoint');
45
48
  if(values.history) process.stdout.write('历史兼容结构检查完成;不证明当前批准或流转,execution_authorization=not-evaluated\n');
46
49
  else {
47
50
  assertCheckpointBoundary(value,{root});
@@ -18,7 +18,7 @@
18
18
  "revision": "c4d958f18cc947cb91cbe1bd48783fa4601cc18f"
19
19
  },
20
20
  "iloveZzz/yss-harness-design-agent": {
21
- "revision": "d754260a597df6bc342f2411ccfd5bcf94a307e9"
21
+ "revision": "fbe4084276a8fe9a8f59e58481f2913133346eaf"
22
22
  },
23
23
  "ayghri/i-have-adhd": {
24
24
  "revision": "7b9069b39972e269e61bd95c2f66ebb90cac6a02"
@@ -392,7 +392,7 @@
392
392
  "sourceType": "git-submodule",
393
393
  "skillPath": ".agents/skills/prototype-review/SKILL.md",
394
394
  "effectiveHash": "e1b3c28881d315870d40c90d2d0dcfa9a4bb6b080744257e2ca57ed98d146c41",
395
- "sourceRevision": "d754260a597df6bc342f2411ccfd5bcf94a307e9",
395
+ "sourceRevision": "fbe4084276a8fe9a8f59e58481f2913133346eaf",
396
396
  "upstreamHash": "9c0b042760f7d741487e62eaccf55b5079a1991e5489286560e278c9642a09b0",
397
397
  "adaptationRef": ".template-source/agents/strategic-design-skills-integration.md",
398
398
  "targets": [
@@ -644,7 +644,7 @@
644
644
  "sourceType": "git-submodule",
645
645
  "skillPath": ".agents/skills/yss-design-system/SKILL.md",
646
646
  "effectiveHash": "497f2ca2201be1c0f4faeb7a356d80e94885cb0752f3aa4dad01d10450b75773",
647
- "sourceRevision": "d754260a597df6bc342f2411ccfd5bcf94a307e9",
647
+ "sourceRevision": "fbe4084276a8fe9a8f59e58481f2913133346eaf",
648
648
  "upstreamHash": "3884a6b9e0a836c2c783bbe51d7737fee874662ea1964a7df545c2f1ababf4e3",
649
649
  "adaptationRef": ".template-source/agents/strategic-design-skills-integration.md",
650
650
  "targets": [
@@ -753,6 +753,18 @@
753
753
  ".pi/skills"
754
754
  ]
755
755
  },
756
+ "yss-harness-upgrade": {
757
+ "source": "project",
758
+ "sourceType": "local",
759
+ "skillPath": ".agents/skills/yss-harness-upgrade/SKILL.md",
760
+ "effectiveHash": "1ca31a3a701cfd560116f6c3d355357b080889c58a9468d2bad39051a2545262",
761
+ "targets": [
762
+ ".agents/skills",
763
+ ".codex/skills",
764
+ ".cursor/skills",
765
+ ".pi/skills"
766
+ ]
767
+ },
756
768
  "yss-hook": {
757
769
  "source": "project",
758
770
  "sourceType": "local",
@@ -769,7 +781,7 @@
769
781
  "source": "project",
770
782
  "sourceType": "local",
771
783
  "skillPath": ".agents/skills/yss-implementation-contract-compiler/SKILL.md",
772
- "effectiveHash": "c957b2230c8db7d1724cf1c592d9d4661e561eabefd8a4a612bdc903b1ad377f",
784
+ "effectiveHash": "ef7778cad116b9bc1a59290ad99cdacc9ddc85c26ff608d5e9db3ca0451adfc2",
773
785
  "targets": [
774
786
  ".agents/skills",
775
787
  ".codex/skills",
@@ -829,7 +841,7 @@
829
841
  "source": "project",
830
842
  "sourceType": "local",
831
843
  "skillPath": ".agents/skills/yss-product-lifecycle/SKILL.md",
832
- "effectiveHash": "3b53105bc8e5ab7c49e3482ba1a4cad8e344512e560424bd2c66ca334bed4414",
844
+ "effectiveHash": "19fc768b7e60c7efdbdca3ffb2494a3067eac1c1c93c0cb5a2df4022b9d27be0",
833
845
  "targets": [
834
846
  ".agents/skills",
835
847
  ".codex/skills",
@@ -842,7 +854,7 @@
842
854
  "sourceType": "git-submodule",
843
855
  "skillPath": ".agents/skills/yss-prototype-stage/SKILL.md",
844
856
  "effectiveHash": "c88b1b84ae99ba90cd9bca8b3c6b9c258d0dd41968689910556695b8729c51a4",
845
- "sourceRevision": "d754260a597df6bc342f2411ccfd5bcf94a307e9",
857
+ "sourceRevision": "fbe4084276a8fe9a8f59e58481f2913133346eaf",
846
858
  "upstreamHash": "0e82e8c0f6b5446dbe48aec8df092574ab895be303efe33d8fc8457bce98d02e",
847
859
  "adaptationRef": ".template-source/agents/strategic-design-skills-integration.md",
848
860
  "targets": [
@@ -919,9 +931,9 @@
919
931
  "source": "iloveZzz/yss-harness-design-agent",
920
932
  "sourceType": "git-submodule",
921
933
  "skillPath": ".agents/skills/yss-stage-decision/SKILL.md",
922
- "effectiveHash": "cc741295a158d3166e9b0ef351538c50f2921f52d808da31782c401b6d7d873a",
923
- "sourceRevision": "d754260a597df6bc342f2411ccfd5bcf94a307e9",
924
- "upstreamHash": "9e06107a37c2251b454bde7e258f2e462ba5fe766f9fabe9e7f76419b60f5c98",
934
+ "effectiveHash": "fa738fba485549181534419336eb266506d282b2cb8cf720c3375f62b5d70444",
935
+ "sourceRevision": "fbe4084276a8fe9a8f59e58481f2913133346eaf",
936
+ "upstreamHash": "77fa3075f90966821881998ff5fa9d674672bd2568e70b8d877402efeb360453",
925
937
  "adaptationRef": ".template-source/agents/strategic-design-skills-integration.md",
926
938
  "targets": [
927
939
  ".agents/skills",
@@ -95,7 +95,8 @@
95
95
  "scripts/lib/template-verification-runner.mjs",
96
96
  "scripts/lib/verification-selection.mjs",
97
97
  "scripts/lib/verification-report.mjs",
98
- "scripts/lib/maintenance-counterexample.mjs"
98
+ "scripts/lib/maintenance-counterexample.mjs",
99
+ "scripts/sync-harness-upgrade"
99
100
  ],
100
101
  "excludePathSuffixes": [
101
102
  "-scenarios"
@@ -3,10 +3,10 @@
3
3
  "templateName": "yss-spec-project-template",
4
4
  "templateSource": "github:iloveZzz/yss-spec-project-template",
5
5
  "templateRepository": "https://github.com/iloveZzz/yss-spec-project-template.git",
6
- "requestedRef": "8cc9764ca3d69069dc4fba3765c6c3c038d317a8",
7
- "templateCommit": "8cc9764ca3d69069dc4fba3765c6c3c038d317a8",
6
+ "requestedRef": "dd58fc3929b3958579ac8a426f06f087682f2f6c",
7
+ "templateCommit": "dd58fc3929b3958579ac8a426f06f087682f2f6c",
8
8
  "sourceState": "committed",
9
- "manifestHash": "b8e3fc9b7eecd3155ec4515afb151d344191410a8b3f18305f107a54ce1b337a",
9
+ "manifestHash": "544e8ae01c49ef451a784237b06772137876f8e8d6b5334094827e2ca2fe0f13",
10
10
  "encodedPaths": {
11
11
  ".agents/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/.gitignore": ".agents/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/__yss_dotfile__.gitignore",
12
12
  ".codex/skills/product-design/.npmignore": ".codex/skills/product-design/__yss_dotfile__.npmignore",
@@ -16,6 +16,6 @@
16
16
  ".gitignore": "__yss_dotfile__.gitignore",
17
17
  ".pi/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/.gitignore": ".pi/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/__yss_dotfile__.gitignore"
18
18
  },
19
- "snapshotHash": "c055086fadfc1a00c0770b5d6f0a49fea5b9f268175beb3ab6ba86440474b86f",
20
- "generatedAt": "2026-10-01T01:02:49.512Z"
19
+ "snapshotHash": "e97773b2461a3bb7a35551a1f97371af689b94f281bbdb7a635e687d5239b405",
20
+ "generatedAt": "2026-10-01T15:17:35.092Z"
21
21
  }