create-yss-spec 3.1.0 → 3.1.3

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 (139) hide show
  1. package/README.md +117 -91
  2. package/bin/create-yss-spec.js +11 -3
  3. package/package.json +9 -2
  4. package/src/api/_sync-service.js +191 -0
  5. package/src/api/index.js +22 -0
  6. package/src/api/project-diff.js +9 -0
  7. package/src/api/project-doctor.js +10 -0
  8. package/src/api/template-apply.js +10 -0
  9. package/src/api/template-plan.js +9 -0
  10. package/src/cli/args.js +60 -0
  11. package/src/cli/error-output.js +52 -0
  12. package/src/cli/flags.js +9 -0
  13. package/src/cli/help.js +95 -0
  14. package/src/cli/index.js +82 -0
  15. package/src/cli/plan-output.js +61 -0
  16. package/src/cli/prompts.js +91 -0
  17. package/src/cli/router.js +41 -0
  18. package/src/cli.js +5 -2253
  19. package/src/commands/attach.js +280 -0
  20. package/src/commands/diff.js +23 -0
  21. package/src/commands/doctor.js +417 -0
  22. package/src/commands/init.js +193 -0
  23. package/src/commands/sync.js +146 -0
  24. package/src/commands/update.js +14 -0
  25. package/src/contracts/apply-result-v1.json +45 -0
  26. package/src/contracts/customization-policy-v1.json +28 -0
  27. package/src/contracts/doctor-report-v1.json +29 -0
  28. package/src/contracts/error-envelope-v1.json +24 -0
  29. package/src/contracts/generator-policy-v1.json +24 -0
  30. package/src/contracts/ownership-policy-v1.json +40 -0
  31. package/src/contracts/plan-schema-v1.json +115 -0
  32. package/src/family-identity.js +365 -0
  33. package/src/family-runtime.js +23 -0
  34. package/src/filesystem/apply-plan.js +44 -0
  35. package/src/filesystem/copy-path.js +28 -0
  36. package/src/filesystem/path-utils.js +67 -0
  37. package/src/filesystem/transaction-runner.js +41 -0
  38. package/src/filesystem/transaction.js +153 -0
  39. package/src/git/submodule.js +115 -0
  40. package/src/git/worktree.js +69 -0
  41. package/src/template/attach-planner-runtime.js +51 -0
  42. package/src/template/attach-planner.js +160 -0
  43. package/src/template/instance-runtime.js +463 -0
  44. package/src/template/lifecycle-metadata.js +99 -0
  45. package/src/template/lifecycle-policy.js +129 -0
  46. package/src/template/lifecycle-runtime.js +90 -0
  47. package/src/template/migration-planner.js +95 -0
  48. package/src/template/migration-runtime.js +247 -0
  49. package/src/template/ownership-metadata.js +79 -0
  50. package/src/template/ownership-policy.js +149 -0
  51. package/src/template/ownership-runtime.js +109 -0
  52. package/src/template/plan-schema.js +36 -0
  53. package/src/template/sync-planner-runtime.js +57 -0
  54. package/src/template/sync-planner.js +206 -0
  55. package/src/template/verification-runtime.js +154 -0
  56. package/src/validation/identity.js +41 -0
  57. package/src/validation/metadata.js +105 -0
  58. package/src/validation/security.js +67 -0
  59. package/src/validation/snapshot.js +47 -0
  60. package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
  61. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  62. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  63. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  64. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  65. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +2 -0
  66. package/template/.agents/skills/yss-ui/SKILL.md +2 -0
  67. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  68. package/template/.claude/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  69. package/template/.claude/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  70. package/template/.claude/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  71. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +2 -0
  72. package/template/.claude/skills/yss-ui/SKILL.md +2 -0
  73. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  74. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  75. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  76. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  77. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +2 -0
  78. package/template/.codex/skills/yss-ui/SKILL.md +2 -0
  79. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  80. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  81. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  82. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  83. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +2 -0
  84. package/template/.cursor/skills/yss-ui/SKILL.md +2 -0
  85. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  86. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  87. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  88. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  89. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +2 -0
  90. package/template/.pi/skills/yss-ui/SKILL.md +2 -0
  91. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  92. package/template/.qoder/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  93. package/template/.qoder/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  94. package/template/.qoder/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  95. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +2 -0
  96. package/template/.qoder/skills/yss-ui/SKILL.md +2 -0
  97. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +2 -0
  98. package/template/.trae/skills/yss-implementation-contract-compiler/SKILL.md +2 -0
  99. package/template/.trae/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +6 -0
  100. package/template/.trae/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +2 -0
  101. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +2 -0
  102. package/template/.trae/skills/yss-ui/SKILL.md +2 -0
  103. package/template/CONTEXT.md +2 -0
  104. package/template/README.md +4 -0
  105. package/template/docs/agents/digital-human-roles.yaml +1 -0
  106. package/template/docs/process/frontend-backend-delivery.md +72 -0
  107. package/template/docs/process/implementation-repo-integration.md +2 -0
  108. package/template/docs/process/lifecycle-artifact-map.md +1 -0
  109. package/template/docs/process/lifecycle-registry-baseline.json +2 -1
  110. package/template/docs/process/lifecycle-registry.yaml +5 -0
  111. package/template/docs/process/schemas/backend-delivery-verification.schema.json +113 -0
  112. package/template/docs/process/schemas/backend-delivery.schema.json +301 -0
  113. package/template/docs/process/schemas/digital-human-task-package.schema.json +8 -0
  114. package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +200 -0
  115. package/template/docs/process/schemas/frontend-implementation-evidence.schema.json +3 -0
  116. package/template/docs/process/template-verification-profiles.yaml +6 -0
  117. package/template/docs/user-guide//346/210/230/346/234/257/350/256/276/350/256/241/345/255/220/351/241/271/347/233/256/347/224/250/346/210/267/346/211/213/345/206/214.md +20 -206
  118. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +63 -963
  119. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +11 -14
  120. package/template/docs/user-guide//350/256/276/345/244/207/345/200/237/347/224/250/350/264/257/347/251/277/346/241/210/344/276/213.md +127 -0
  121. package/template/scripts/backend-delivery +14 -0
  122. package/template/scripts/fixtures/backend-delivery/revision-server.mjs +11 -0
  123. package/template/scripts/instantiate-harness +69 -0
  124. package/template/scripts/lib/backend-delivery.mjs +164 -0
  125. package/template/scripts/lib/frontend-delivery-boundary.mjs +49 -0
  126. package/template/scripts/lib/frontend-delivery.mjs +73 -0
  127. package/template/scripts/lib/harness-execution-scope.mjs +52 -0
  128. package/template/scripts/lib/implementation-contract-compiler.mjs +12 -2
  129. package/template/scripts/lib/lifecycle-transition.mjs +7 -0
  130. package/template/scripts/lib/strategic-handoff-consumption.mjs +14 -7
  131. package/template/scripts/lib/strategic-handoff.mjs +1 -1
  132. package/template/scripts/lib/task-package.mjs +7 -0
  133. package/template/scripts/sync-strategic-handoff-tools +3 -1
  134. package/template/scripts/verify-frontend-delivery +9 -0
  135. package/template/scripts/verify-frontend-delivery-scenarios +131 -0
  136. package/template/scripts/verify-frontend-implementation-evidence +2 -0
  137. package/template/skills-lock.json +11 -11
  138. package/template.manifest.json +43 -1
  139. package/template.snapshot.json +5 -5
@@ -1,3 +1,4 @@
1
+ import { enforceHarnessSkillScope } from "./harness-execution-scope.mjs";
1
2
  import { createHash } from "node:crypto";
2
3
  import { readFileSync } from "node:fs";
3
4
  import path from "node:path";
@@ -5,6 +6,7 @@ import { parseDocument } from "../vendor/yaml.mjs";
5
6
  import { DEFAULT_REGISTRY, loadSkillRegistry } from "./skill-registry.mjs";
6
7
  import { ROOT } from "./skill-supply-chain.mjs";
7
8
  import { architectureDigest, assertArchitectureAgreement, validateArchitectureIdentity } from "./backend-architecture.mjs";
9
+ import { enforceFrontendDelivery } from "./frontend-delivery-boundary.mjs";
8
10
 
9
11
  export const DEFAULT_COMPILER_CONTRACT = path.join(
10
12
  ROOT,
@@ -61,9 +63,13 @@ export function compileImplementationContract({
61
63
  registryDigest,
62
64
  compilerContractDigest,
63
65
  architecture_identity,
64
- architecture_evidence
66
+ architecture_evidence,
67
+ root = ROOT,
68
+ slice_id,
69
+ frontend_delivery
65
70
  }) {
66
71
  assertV2(registry, compilerContract);
72
+ const deliveryInput = enforceFrontendDelivery({ slice_id, frontend_delivery }, { root });
67
73
  if (!Array.isArray(recipeIds) || !Array.isArray(requiredCapabilities) || !Array.isArray(conditions)) {
68
74
  fail("recipeIds、requiredCapabilities 与 conditions 必须是数组");
69
75
  }
@@ -156,10 +162,12 @@ export function compileImplementationContract({
156
162
  orderedSkills.push(skill);
157
163
  };
158
164
  for (const root of roots) visit(root);
165
+ enforceHarnessSkillScope(orderedSkills, registry, { root });
159
166
 
160
167
  return {
161
168
  schema_version: 2,
162
169
  status: "draft",
170
+ ...(deliveryInput.result === "inputs-verified" ? { slice_id: deliveryInput.slice_id, frontend_delivery: { acceptance_ref: deliveryInput.acceptance_ref, digest: deliveryInput.acceptance_digest } } : {}),
163
171
  ...(architecture_identity ? {
164
172
  architecture_identity: structuredClone(architecture_identity),
165
173
  architecture_identity_digest: architectureDigest(architecture_identity),
@@ -189,11 +197,13 @@ export function compileDefaultImplementationContract(input = {}) {
189
197
  });
190
198
  }
191
199
 
192
- export function evaluateContractFreshness(contract, { registry, compilerContract }) {
200
+ export function evaluateContractFreshness(contract, { registry, compilerContract, root = ROOT }) {
193
201
  assertV2(registry, compilerContract);
194
202
  if (contract?.schema_version !== 2) fail("Slice Implementation Contract schema v1 已停止支持;必须重新编译 v2 合同");
195
203
  const resolution = contract.resolution ?? contract;
196
204
  const reasons = [];
205
+ try { enforceFrontendDelivery(contract, { root }); }
206
+ catch { reasons.push("frontend-delivery-stale-or-unavailable"); }
197
207
  if (resolution.registry_digest !== digestDocument(registry)) reasons.push("registry-digest-changed");
198
208
  if (resolution.compiler_contract_digest !== digestDocument(compilerContract)) reasons.push("compiler-contract-digest-changed");
199
209
  if (resolution.architecture_identity) {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { parseDocument } from "../vendor/yaml.mjs";
3
3
  import { assertWorkUnitUserDecision, assertImplementationDecision } from "./user-decision.mjs";
4
+ import { enforceFrontendDelivery } from "./frontend-delivery-boundary.mjs";
4
5
 
5
6
  const IMPLEMENTATION_WORK_UNIT = "work-unit.slice-implementation";
6
7
  const TICKET_DECOMPOSITION_WORK_UNIT = "work-unit.ticket-decomposition";
@@ -112,6 +113,10 @@ function validateTicketReference(ref, trackerKind) {
112
113
  * by `validateWorkflowExecutionResult` before this function is called.
113
114
  */
114
115
  export function validateNextRoute(currentWorkUnit, nextRoute, decisionState, options = {}) {
116
+ if (["work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT, IMPLEMENTATION_WORK_UNIT, "work-unit.frontend-implementation-verification"].includes(nextRoute)) {
117
+ try { enforceFrontendDelivery(decisionState, { root: options.root, phase: nextRoute === IMPLEMENTATION_WORK_UNIT ? "implementation" : "inputs" }); }
118
+ catch (error) { return blockedResult(["frontend-delivery-blocked"], [error.message]); }
119
+ }
115
120
  const routes = NEXT_ROUTES[currentWorkUnit];
116
121
  if (!routes) return blockedResult([BLOCKING_SIGNALS.invalidRoute], ["known_current_work_unit"]);
117
122
  if (nextRoute === null && routes.length === 0) {
@@ -138,6 +143,8 @@ function validateDecisionBoundary(workUnit, state, options) {
138
143
  * `exists` is injectable so external adapters can resolve their own tracker refs.
139
144
  */
140
145
  export function validateTicketFormalization(state, { exists = existsSync, read = (ref) => readFileSync(ref, "utf8"), ...decisionOptions } = {}) {
146
+ try { enforceFrontendDelivery(state, { root: decisionOptions.root, phase: "implementation" }); }
147
+ catch (error) { return blockedResult(["frontend-delivery-blocked"], [error.message]); }
141
148
  const decomposition = state?.ticket_decomposition_result;
142
149
  const ticket = state?.vertical_slice_ticket;
143
150
  const contract = state?.slice_contract;
@@ -11,9 +11,10 @@ const strategyBasis=value=>({
11
11
  ...without(value,['domain_version','approval','rule_catalog','scenarios','invariants']),
12
12
  invariants:(value.invariants||[]).map(item=>without(item,['statement'])),
13
13
  });
14
- export async function verifyConsumption(data,{root=process.cwd(),sliceRef}={}) {
14
+ export async function verifyConsumption(data,{root=process.cwd(),sliceRef,consumer='tactical'}={}) {
15
15
  project(root);
16
- if (sliceRef) ensure(data.status === "approved", "切片只能消费 approved 战术合同");
16
+ ensure(['tactical','frontend'].includes(consumer),'未知战略消费者');
17
+ if (sliceRef) ensure(data.status === (consumer==='frontend'?'accepted':'approved'), consumer==='frontend'?'前端接收记录尚未 accepted':'切片只能消费 approved 战术合同');
17
18
  const binding=data.strategic_handoff;
18
19
  ensure(binding && present(binding.import_receipt_ref) && present(binding.context_reconciliation_ref),'缺少战略交接导入收据与目标对账引用');
19
20
  const receipt=read(safe(root,binding.import_receipt_ref));
@@ -44,15 +45,21 @@ export async function verifyConsumption(data,{root=process.cwd(),sliceRef}={}) {
44
45
  const catalogs=['aggregate_catalog','entity_catalog','value_object_catalog','behavior_catalog','invariant_catalog','state_transition_catalog','domain_event_catalog','gateway_catalog'];
45
46
  const ids=new Set(catalogs.flatMap(k=>(data[k]||[]).flatMap(x=>Object.entries(x).filter(([k])=>k.endsWith('_id')).map(([,v])=>v))));
46
47
  const seams=new Map((data.test_seams||[]).map(x=>[x.seam_id,x]));
48
+ const cases=new Map((data.frontend_cases||[]).map(x=>[x.case_id,x]));
47
49
  for(const[id,source]of known){
48
50
  const row=rows.get(id);
49
51
  if(!row){mark(id,'missing-mapping');continue;}
50
52
  ensure(['known','unknown'].includes(row.dependency_status) && Array.isArray(row.dependent_slice_refs),'缺少切片依赖判定');
51
53
  if(row.source_digest!==source.source_digest){mark(id,'stale-source',row);continue;}
52
- if(row.disposition==='implemented'){
53
- ensure(list(row.tactical_refs)&&row.tactical_refs.every(x=>ids.has(x)),`战术落点悬空: ${id}`);
54
- ensure(list(row.test_seam_refs)&&row.test_seam_refs.every(x=>seams.has(x)&&row.tactical_refs.includes(seams.get(x).subject_ref)),`测试 seam 未关联战术落点: ${id}`);
55
- if(id.startsWith('scenario.'))ensure(['success','failure'].every(outcome=>(row.scenario_tests||[]).some(x=>x.outcome===outcome&&row.test_seam_refs.includes(x.seam_ref))),`关键场景缺少成功/失败测试: ${id}`);
54
+ if(row.disposition===(consumer==='frontend'?'mapped':'implemented')){
55
+ if(consumer==='frontend'){
56
+ ensure(list(row.frontend_case_refs)&&row.frontend_case_refs.every(ref=>cases.has(ref)&&cases.get(ref).source_ids.includes(id)),`前端用例未承接源规则/场景: ${id}`);
57
+ if(id.startsWith('scenario.'))ensure(['success','failure'].every(outcome=>row.frontend_case_refs.some(ref=>cases.get(ref).outcome===outcome)),`前端关键场景缺少成功/失败用例: ${id}`);
58
+ }else{
59
+ ensure(list(row.tactical_refs)&&row.tactical_refs.every(x=>ids.has(x)),`战术落点悬空: ${id}`);
60
+ ensure(list(row.test_seam_refs)&&row.test_seam_refs.every(x=>seams.has(x)&&row.tactical_refs.includes(seams.get(x).subject_ref)),`测试 seam 未关联战术落点: ${id}`);
61
+ if(id.startsWith('scenario.'))ensure(['success','failure'].every(outcome=>(row.scenario_tests||[]).some(x=>x.outcome===outcome&&row.test_seam_refs.includes(x.seam_ref))),`关键场景缺少成功/失败测试: ${id}`);
62
+ }
56
63
  ensure(list(row.evidence_refs),`缺少落实证据: ${id}`);row.evidence_refs.forEach(ref=>safe(root,ref));
57
64
  }else if(row.disposition==='deferred'){
58
65
  for(const key of ['reason','risk','owner','followup_ticket_ref','verification_plan','target_version'])ensure(present(row[key]),`延期缺少 ${key}: ${id}`);
@@ -70,7 +77,7 @@ export async function verifyConsumption(data,{root=process.cwd(),sliceRef}={}) {
70
77
  all=true;issues.push({source_id:'strategic-design-basis',reason:'upstream-design-stale'});
71
78
  }
72
79
  const blockedHere=all||(sliceRef?blocked.has(sliceRef):issues.length>0);
73
- return {result:blockedHere?'blocked':'verified',bundle_digest:current.manifest.bundle_digest,consumed_bundle_digest:receipt.bundle_digest,tactical_digest:digest(data),scope:sliceRef||'whole-tactical-design',coverage_complete:[...known.keys()].every(id=>rows.has(id)),block_all:all,blocked_slice_refs:[...blocked].sort(),issues};
80
+ return {result:blockedHere?'blocked':'verified',bundle_digest:current.manifest.bundle_digest,consumed_bundle_digest:receipt.bundle_digest,...(consumer==='frontend'?{frontend_acceptance_digest:digest(data)}:{tactical_digest:digest(data)}),scope:sliceRef||`whole-${consumer}-design`,coverage_complete:[...known.keys()].every(id=>rows.has(id)),block_all:all,blocked_slice_refs:[...blocked].sort(),issues};
74
81
  });
75
82
  });
76
83
  }
@@ -13,7 +13,7 @@ const own = (a,b) => JSON.stringify(canonical(a)) === JSON.stringify(canonical(b
13
13
  const nonempty = x => typeof x === 'string' && x.trim();
14
14
  const bytesDigest = (bytes, kind) => kind === 'canonical-json' ? digest(parse(bytes)) : kind === 'sha256-bytes' ? hash(bytes) : (() => { throw new TypeError(`未知摘要算法: ${kind}`); })();
15
15
 
16
- async function sourceApproval(record, roles, root) {
16
+ export async function sourceApproval(record, roles, root) {
17
17
  const options={rolesDoc:roles,requireApproved:true,root,read:ref=>readFileSync(safe(root,path.relative(root,ref).split(path.sep).join('/')))};
18
18
  if (roles.user_decision_policy.gates.includes(record.gate_id)) {
19
19
  ensure(existsSync(path.join(ROOT,'scripts/lib/user-decision.mjs')), '接收工具不支持源用户决定策略,请升级工具后验包');
@@ -6,6 +6,8 @@ import { loadRegistry, ROOT } from "./lifecycle-registry.mjs";
6
6
  import { loadMaintenanceCheckpoint, validateMaintenanceCheckpoint } from "./maintenance-intensity.mjs";
7
7
  import { assertImplementationDecision } from "./user-decision.mjs";
8
8
  import { validateNextRoute } from "./lifecycle-transition.mjs";
9
+ import { enforceHarnessTaskScope } from "./harness-execution-scope.mjs";
10
+ import { enforceFrontendDelivery } from "./frontend-delivery-boundary.mjs";
9
11
  import { LEGACY_TASK_PACKAGE_SCHEMA, TASK_PACKAGE_SCHEMA, validateTaskPackageSchema } from "./task-package-schema.mjs";
10
12
 
11
13
  export { LEGACY_TASK_PACKAGE_SCHEMA, TASK_PACKAGE_SCHEMA, validateTaskPackageSchema };
@@ -181,6 +183,11 @@ function validateContract(value, registry, lifecycle) {
181
183
 
182
184
  export function validateTaskPackage(value, { rolesDoc, lifecycleDoc } = {}) {
183
185
  validateTaskPackageSchema(value);
186
+ enforceHarnessTaskScope(value);
187
+ const intake = value.execution_state === "Explorer" && value.allowed_write_paths.length === 0 && ["work-unit.entry-triage", "work-unit.harness-entry"].includes(value.work_unit_id);
188
+ if (!intake) {
189
+ enforceFrontendDelivery(value, { phase: value.contract.kind === "slice-implementation" ? "implementation" : "inputs" });
190
+ }
184
191
  const registry = rolesDoc || loadDigitalHumanRoles();
185
192
  const lifecycle = lifecycleDoc || loadRegistry();
186
193
  validateCommon(value, registry, lifecycle);
@@ -5,14 +5,16 @@ import path from 'node:path';
5
5
  import { inspectWorkingTreeScope } from './lib/repository-scope-policy.mjs';
6
6
  const root=path.resolve(import.meta.dirname,'..');
7
7
  const check=process.argv.includes('--check');
8
- const targets=['submodules/yss-harness-design-agent','submodules/yss-harness-dev-agent'];
8
+ const targets=['submodules/yss-harness-design-agent','submodules/yss-harness-dev-agent','submodules/yss-harness-backend-agent','submodules/yss-harness-frontend-agent'];
9
9
  const shared=['scripts/strategic-handoff','scripts/verify-strategic-handoff-consumption','scripts/verify-strategic-handoff-package-scenarios','scripts/verify-strategic-context-import','scripts/verify-strategic-context-import-scenarios','scripts/lib/context-contract.mjs','docs/process/strategic-handoff-package.md','docs/process/schemas/strategic-design-handoff.schema.json',...readdirSync(path.join(root,'scripts/lib')).filter(x=>x.startsWith('strategic-handoff-')||x==='strategic-handoff.mjs').map(x=>`scripts/lib/${x}`),...readdirSync(path.join(root,'docs/process/schemas')).filter(x=>x.startsWith('strategic-handoff-')).map(x=>`docs/process/schemas/${x}`),...readdirSync(path.join(root,'scripts/fixtures/strategic-handoff')).map(x=>`scripts/fixtures/strategic-handoff/${x}`)];
10
+ shared.push('scripts/instantiate-harness','scripts/lib/harness-execution-scope.mjs','scripts/backend-delivery','scripts/verify-frontend-delivery','scripts/lib/backend-delivery.mjs','scripts/lib/frontend-delivery.mjs','scripts/lib/frontend-delivery-boundary.mjs','docs/process/frontend-backend-delivery.md','docs/process/schemas/backend-delivery.schema.json','docs/process/schemas/backend-delivery-verification.schema.json','docs/process/schemas/frontend-delivery-acceptance.schema.json');
10
11
  let differences=[];
11
12
  function sync(source,dest){const bytes=readFileSync(source);if(!existsSync(dest)||!bytes.equals(readFileSync(dest))){differences.push(path.relative(root,dest));if(!check){mkdirSync(path.dirname(dest),{recursive:true});writeFileSync(dest,bytes);}}if(!check&&path.basename(path.dirname(dest))==='scripts'&&!path.extname(dest))chmodSync(dest,0o755);}
12
13
  for(const kind of ['domain-strategy','stage-decision-package'])sync(path.join(root,`.agents/skills/yss-stage-decision/references/${kind}.schema.json`),path.join(root,`docs/process/schemas/strategic-handoff-${kind}.schema.json`));
13
14
  for(const target of targets){
14
15
  if(!existsSync(path.join(root,target,'.git')))continue;
15
16
  if(!check){const state=inspectWorkingTreeScope(root,{project_root:target,repository_scope:'git-submodule',checkout_state:'attached-branch'});if(!state.writable)throw new Error(`${target}: ${state.violation}`);}
17
+ if(!target.endsWith('design-agent'))for(const ref of ['scripts/verify-frontend-delivery-scenarios','scripts/fixtures/backend-delivery/revision-server.mjs'])sync(path.join(root,ref),path.join(root,target,ref));
16
18
  for(const ref of new Set(shared))sync(path.join(root,ref),path.join(root,target,ref));
17
19
  }
18
20
  if(check&&differences.length){process.stderr.write(`交接工具同步漂移: ${differences.join(', ')}\n`);process.exitCode=1;}else process.stdout.write(`战略交接共享工具 ${check?'同步检查通过':`同步 ${differences.length} 文件`}\n`);
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { verifyFrontendDelivery } from './lib/frontend-delivery.mjs';
4
+ try {
5
+ const {values,positionals}=parseArgs({allowPositionals:true,options:{root:{type:'string',default:process.cwd()},slice:{type:'string'},'expected-digest':{type:'string'}}});
6
+ if(positionals.length!==1||!values.slice)throw new TypeError('用法: verify-frontend-delivery --root <repo> --slice <slice-id> [--expected-digest <sha256>] <acceptance-ref>');
7
+ const result=await verifyFrontendDelivery({root:values.root,sliceRef:values.slice,expectedDigest:values['expected-digest'],acceptanceRef:positionals[0]});
8
+ process.stdout.write(`${JSON.stringify(result,null,2)}\n`);
9
+ } catch(error) { process.stderr.write(`${JSON.stringify({result:'blocked',error:error.message})}\n`);process.exitCode=1; }
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { fork } from 'node:child_process';
5
+ import { once } from 'node:events';
6
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, cpSync, existsSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import { pathToFileURL } from 'node:url';
9
+ import { tmpdir } from 'node:os';
10
+ import { fixture, context } from './fixtures/strategic-handoff/fixture.mjs';
11
+ import { exportBundle } from './lib/strategic-handoff.mjs';
12
+ const backendTools=process.env.YSS_DELIVERY_TEST_BACKEND_ROOT;
13
+ const frontendTools=process.env.YSS_DELIVERY_TEST_FRONTEND_ROOT;
14
+ const { backendDeliveryBasis, exportBackendDelivery, openBackendDelivery, inspectBackendDelivery } = await import(backendTools?pathToFileURL(path.join(backendTools,'scripts/lib/backend-delivery.mjs')):'./lib/backend-delivery.mjs');
15
+ const {importBackendDelivery}=await import(frontendTools?pathToFileURL(path.join(frontendTools,'scripts/lib/backend-delivery.mjs')):'./lib/backend-delivery.mjs');
16
+ const { verifyFrontendDelivery } = await import(frontendTools?pathToFileURL(path.join(frontendTools,'scripts/lib/frontend-delivery.mjs')):'./lib/frontend-delivery.mjs');
17
+ const { enforceFrontendDelivery } = await import(frontendTools?pathToFileURL(path.join(frontendTools,'scripts/lib/frontend-delivery-boundary.mjs')):'./lib/frontend-delivery-boundary.mjs');
18
+ const { compileDefaultImplementationContract } = await import(frontendTools?pathToFileURL(path.join(frontendTools,'scripts/lib/implementation-contract-compiler.mjs')):'./lib/implementation-contract-compiler.mjs');
19
+ const { validateNextRoute, validateImplementationEntry } = await import(frontendTools?pathToFileURL(path.join(frontendTools,'scripts/lib/lifecycle-transition.mjs')):'./lib/lifecycle-transition.mjs');
20
+ import { read, hash, json, ROOT } from './lib/strategic-handoff-io.mjs';
21
+ const runtimeRegistry=read(path.join(frontendTools||ROOT,'docs/process/lifecycle-registry.yaml'));
22
+ const frontendStage=runtimeRegistry.stages.find(x=>x.id==='stage.frontend-engineering-design')?.id||runtimeRegistry.stages.find(x=>x.id==='stage.tactical-design')?.id||'stage.system-data-engineering';
23
+ const frontendWorkUnit=frontendStage==='stage.frontend-engineering-design'?'work-unit.frontend-engineering-design':frontendStage==='stage.tactical-design'?'work-unit.tactical-design':'work-unit.technical-analysis';
24
+
25
+ test('战略与后端交付联合接收:便携、真实服务、拒绝不完整/过期输入及执行入口绕过',async t=>{
26
+ const base=mkdtempSync(path.join(tmpdir(),'frontend-delivery-test-'));
27
+ const put=(root,ref,value)=>{mkdirSync(path.dirname(path.join(root,ref)),{recursive:true});writeFileSync(path.join(root,ref),typeof value==='string'?value:json(value));};
28
+ const source=path.join(base,'strategy'), backend=path.join(base,'backend'), frontend=path.join(base,'frontend');
29
+ [source,backend,frontend].forEach(root=>mkdirSync(root));
30
+ if(backendTools)cpSync(backendTools,backend,{recursive:true});
31
+ if(frontendTools)cpSync(frontendTools,frontend,{recursive:true});
32
+ const f=await fixture(source);
33
+ for(const root of [backend,frontend]) {put(root,'yss-project.yaml','schema_version: 1\nrepository_mode: project-instance\n');put(root,'CONTEXT.md',context);}
34
+ const strategic=await exportBundle({sourceRoot:source,handoffRef:'handoff.yaml',output:path.join(backend,'strategy-package')});
35
+ const roles=read(path.join(source,'docs/agents/digital-human-roles.yaml'));
36
+ for(const gate of ['gate.openapi-frozen','gate.slice-contract-approved']) {
37
+ roles.gate_policy.biological_human=(roles.gate_policy.biological_human||[]).filter(x=>x!==gate);
38
+ roles.gate_policy.digital_human_review=(roles.gate_policy.digital_human_review||[]).filter(x=>x.gate!==gate);
39
+ roles.gate_policy.digital_human_review.push({gate,countersigners:['role.product-manager']});
40
+ }
41
+ put(backend,'docs/agents/digital-human-roles.yaml',roles);
42
+ put(backend,'api.yaml',{openapi:'3.1.0',info:{title:'Synthetic supplier API',version:'1'},paths:{'/suppliers':{post:{operationId:'submitSupplier',responses:{'200':{description:'OK'}}}}}});
43
+ put(backend,'slice.json',{schema_version:2,contract_id:'contract.backend',contract_version:'v1',slice_id:'slice.submit',status:'approved'});
44
+ put(backend,'data.md','Synthetic data setup for maintenance tests, not product verification.');
45
+ put(backend,'verification.log','Synthetic test fixture: successful/failed submissions exercised.');
46
+ const file=ref=>({ref,digest:hash(readFileSync(path.join(backend,ref)))});
47
+ const approved=(ref,id,gate)=>{
48
+ const binding={...file(ref),id,version:'v1',approval_ref:`approvals/${id}.json`};
49
+ put(backend,binding.approval_ref,{schema_version:1,gate_id:gate,decision:'approved',actor_kind:'digital-human',role_id:'role.product-manager',runtime_id:'runtime.generic',principal_ref:'synthetic-maintenance-fixture',artifact_bindings:[{id,version:'v1',digest:binding.digest}]});return binding;
50
+ };
51
+ const serverState=path.join(base,'server.json');put(base,'server.json',{body:{}});
52
+ const server=fork(path.join(ROOT,'scripts/fixtures/backend-delivery/revision-server.mjs'),[serverState],{stdio:['ignore','ignore','inherit','ipc']});
53
+ t.after(()=>{server.kill();rmSync(base,{recursive:true,force:true});});
54
+ const [{port}]=await once(server,'message');
55
+ const delivery={schema_version:1,delivery_id:'backend-delivery.supplier',version:'v1',status:'verified',strategic_bundle_ref:'strategy-package',strategic_bundle_digest:strategic.bundle_digest,scope:{slice_id:'slice.submit',source_ids:['rule.complete','scenario.submit'],operation_ids:['submitSupplier']},openapi:approved('api.yaml','api.supplier','gate.openapi-frozen'),slice_contract:approved('slice.json','contract.backend','gate.slice-contract-approved'),build:{source_commit:'a'.repeat(40),artifact_digest:`sha256:${'b'.repeat(64)}`},environment:{id:'fixture-local',base_url:`http://127.0.0.1:${port}`,deployment_id:'fixture-v1',revision_path:'/version',revision_pointers:{deployment_id:'/deployment_id',source_commit:'/source_commit',openapi_digest:'/openapi_digest',artifact_digest:'/artifact_digest',test_data_digest:'/test_data_digest'},test_data:file('data.md')},verification:{},supporting_files:[]};
56
+ const record=kind=>({schema_version:1,kind,subject_digest:backendDeliveryBasis(delivery),results:[{command:'synthetic-maintenance-test',executed_at:new Date().toISOString(),exit_code:0,evidence:[file('verification.log')]}],operation_ids:['submitSupplier'],coverage:[{source_id:'scenario.submit',outcome:'success'},{source_id:'scenario.submit',outcome:'failure'}]});
57
+ put(backend,'contract-test.json',record('backend-contract'));put(backend,'deployment-test.json',record('backend-deployment'));
58
+ delivery.verification={contract:file('contract-test.json'),deployment:file('deployment-test.json')};put(backend,'delivery.json',delivery);
59
+ const body={deployment_id:'fixture-v1',source_commit:delivery.build.source_commit,openapi_digest:delivery.openapi.digest,artifact_digest:delivery.build.artifact_digest,test_data_digest:delivery.environment.test_data.digest};
60
+ put(base,'server.json',{body});
61
+ await t.test('导出拒绝缺失场景、篡改接口及错误批准',async()=>{
62
+ const bytes=readFileSync(path.join(backend,'api.yaml'));put(backend,'api.yaml','changed');
63
+ await assert.rejects(()=>inspectBackendDelivery(backend,'delivery.json'),/摘要不一致/);writeFileSync(path.join(backend,'api.yaml'),bytes);
64
+ const approval=read(path.join(backend,delivery.openapi.approval_ref));put(backend,delivery.openapi.approval_ref,{...approval,artifact_bindings:[]});
65
+ await assert.rejects(()=>inspectBackendDelivery(backend,'delivery.json'),/批准未绑定/);put(backend,delivery.openapi.approval_ref,approval);
66
+ const tests=record('backend-contract');tests.coverage.pop();put(backend,'contract-test.json',tests);
67
+ const broken=structuredClone(delivery);broken.verification.contract=file('contract-test.json');put(backend,'delivery.json',broken);
68
+ await assert.rejects(()=>inspectBackendDelivery(backend,'delivery.json'),/成功\/失败/);
69
+ put(backend,'contract-test.json',record('backend-contract'));delivery.verification.contract=file('contract-test.json');put(backend,'delivery.json',delivery);
70
+ });
71
+ const output=path.join(base,'package');await exportBackendDelivery({sourceRoot:backend,deliveryRef:'delivery.json',output,zip:true});
72
+ const imported=await importBackendDelivery({bundle:`${output}.zip`,targetRoot:frontend});
73
+ assert.equal((await importBackendDelivery({bundle:output,targetRoot:frontend})).result,'already-imported');
74
+ const acceptance=read(path.join(frontend,imported.acceptance_ref));
75
+ await t.test('草案/缺少两类产物不能进入前端任务',async()=>{
76
+ await assert.rejects(()=>verifyFrontendDelivery({root:frontend,acceptanceRef:imported.acceptance_ref,sliceRef:'slice.submit'}),/schema/);
77
+ const profileRef=path.join(frontend,'docs/process/harness-profile.yaml');
78
+ const profile=existsSync(profileRef)?read(profileRef):{schema_version:1,profile_id:'harness.frontend-delivery'};
79
+ put(frontend,'docs/process/harness-profile.yaml',{...profile,frontend_delivery:{required:true}});
80
+ assert.throws(()=>enforceFrontendDelivery({}, {root:frontend}),/frontend-delivery-required/);
81
+ assert.throws(()=>compileDefaultImplementationContract({root:frontend}),/frontend-delivery-required/);
82
+ assert.throws(()=>enforceFrontendDelivery({contract:{kind:'template-maintenance'}},{root:frontend}),/不得用模板维护/);
83
+ assert.equal(validateNextRoute(frontendWorkUnit==='work-unit.technical-analysis'?'work-unit.spec-synthesis':'work-unit.harness-entry',frontendWorkUnit,undefined,{root:frontend}).result,'blocked');
84
+ assert.equal(validateImplementationEntry({}, {root:frontend}).blocking_signals[0],'frontend-delivery-blocked');
85
+ });
86
+ put(frontend,'reconciliation.json',{schema_version:1,repository_mode:'project-instance',stage:frontendStage,work_unit:frontendWorkUnit,status:'reconciled',context_snapshot:f.snapshot,changes:{added:['Global/Supplier'],updated:[],deprecated:[]},unresolved_terms:[],evidence_refs:['CONTEXT.md']});
87
+ put(frontend,'cases.md','Synthetic planned UI cases: complete submission succeeds; incomplete submission fails.');
88
+ acceptance.status='accepted';acceptance.strategic_handoff.context_reconciliation_ref='reconciliation.json';
89
+ acceptance.frontend_cases=['success','failure'].map(outcome=>({case_id:`submit-${outcome}`,source_ids:['rule.complete','scenario.submit'],outcome,operation_ids:['submitSupplier'],visual_case_ids:['primary-desktop','primary-narrow'],evidence_ref:'cases.md',evidence_digest:hash(readFileSync(path.join(frontend,'cases.md')))}));
90
+ for(const row of acceptance.strategic_handoff.rows)Object.assign(row,{disposition:'mapped',frontend_case_refs:acceptance.frontend_cases.map(x=>x.case_id),dependency_status:'known',dependent_slice_refs:['slice.submit'],evidence_refs:['cases.md']});
91
+ put(frontend,imported.acceptance_ref,acceptance);
92
+ const check=()=>verifyFrontendDelivery({root:frontend,acceptanceRef:imported.acceptance_ref,sliceRef:'slice.submit'});
93
+ await t.test('源仓消失后可验 ZIP 并独立恢复;输入就绪不授予代码实施',async()=>{
94
+ rmSync(source,{recursive:true});rmSync(backend,{recursive:true});
95
+ assert.equal(await openBackendDelivery(`${output}.zip`,b=>b.delivery.scope.slice_id),'slice.submit');
96
+ const verified=await check();assert.equal(verified.result,'inputs-verified');assert.equal(verified.ready_for_agent,false);
97
+ assert.equal(verified.actual_verification.observed.deployment_id,'fixture-v1');
98
+ const draft=compileDefaultImplementationContract({root:frontend,slice_id:'slice.submit',frontend_delivery:{acceptance_ref:imported.acceptance_ref},requiredCapabilities:['governance.implementation-contract']});
99
+ assert.equal(draft.status,'draft');assert.equal(draft.slice_id,'slice.submit');assert.equal(draft.frontend_delivery.digest,verified.acceptance_digest);
100
+ assert.equal(enforceFrontendDelivery({slice_id:'slice.submit',frontend_delivery:{acceptance_ref:imported.acceptance_ref}}, {root:frontend}).result,'inputs-verified');
101
+ assert.throws(()=>enforceFrontendDelivery({slice_id:'slice.submit',frontend_delivery:{acceptance_ref:imported.acceptance_ref}}, {root:frontend,phase:'implementation'}),/approved Slice Contract/);
102
+ });
103
+ await t.test('实现边界读取持久化合同并冻结接收记录及用例字节',async()=>{
104
+ const binding={acceptance_ref:imported.acceptance_ref,digest:hash(readFileSync(path.join(frontend,imported.acceptance_ref)))};
105
+ put(frontend,'frontend-contract.json',{status:'approved',slice_id:'slice.submit',frontend:{delivery:binding}});
106
+ assert.equal(enforceFrontendDelivery({slice_contract_ref:'frontend-contract.json'},{root:frontend,phase:'implementation'}).result,'inputs-verified');
107
+ put(frontend,'cases.md','changed case');await assert.rejects(check,/摘要漂移/);
108
+ put(frontend,'cases.md','Synthetic planned UI cases: complete submission succeeds; incomplete submission fails.');
109
+ put(frontend,'frontend-contract.json',{status:'draft',slice_id:'slice.submit',frontend:{delivery:binding}});
110
+ assert.throws(()=>enforceFrontendDelivery({slice_contract_ref:'frontend-contract.json'},{root:frontend,phase:'implementation'}),/approved Slice Contract/);
111
+ });
112
+ await t.test('部署版本/数据失配与服务失败使旧接收记录失效',async()=>{
113
+ for(const field of ['deployment_id','source_commit','openapi_digest','artifact_digest','test_data_digest']) {
114
+ put(base,'server.json',{body:{...body,[field]:'wrong'}});await assert.rejects(check,/真实服务版本不匹配/);
115
+ }
116
+ put(base,'server.json',{status:503,body});await assert.rejects(check,/真实接口不可用/);put(base,'server.json',{body});
117
+ });
118
+ await t.test('范围、摘要和逐条承接缺口均拒绝',async()=>{
119
+ await assert.rejects(()=>verifyFrontendDelivery({root:frontend,acceptanceRef:imported.acceptance_ref,sliceRef:'slice.other'}),/切片不匹配/);
120
+ await assert.rejects(()=>verifyFrontendDelivery({root:frontend,acceptanceRef:imported.acceptance_ref,sliceRef:'slice.submit',expectedDigest:`sha256:${'0'.repeat(64)}`}),/摘要已变化/);
121
+ for(const mutate of [a=>a.frontend_cases.pop(),a=>a.strategic_handoff.rows[0].disposition='pending',a=>a.frontend_cases[0].operation_ids=['unknown'],a=>a.strategic_handoff.bundle_digest=`sha256:${'0'.repeat(64)}`]) {
122
+ const changed=structuredClone(acceptance);mutate(changed);put(frontend,imported.acceptance_ref,changed);await assert.rejects(check);
123
+ }
124
+ put(frontend,imported.acceptance_ref,acceptance);
125
+ });
126
+ await t.test('已导入更新版本阻断旧切片;包篡改拒绝',async()=>{
127
+ const receipt=read(path.join(frontend,imported.receipt_ref));const newer=path.posix.dirname(path.posix.dirname(imported.receipt_ref))+'/v2';
128
+ put(frontend,`${newer}/import-receipt.json`,{...receipt,version:'v2'});await assert.rejects(check,/旧前端接收记录 stale/);rmSync(path.join(frontend,newer),{recursive:true});
129
+ const changed=path.join(base,'tampered');cpSync(output,changed,{recursive:true});put(changed,'extra.txt','extra');await assert.rejects(()=>openBackendDelivery(changed,()=>null),/未登记文件/);
130
+ });
131
+ });
@@ -6,6 +6,7 @@ import { parseArgs } from "node:util";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { parseDocument } from "./vendor/yaml.mjs";
8
8
  import { validateVisualBaseline } from "../.agents/skills/yss-prototype-stage/scripts/visual-baseline-contract.mjs";
9
+ import { enforceFrontendDelivery } from "./lib/frontend-delivery-boundary.mjs";
9
10
 
10
11
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
12
  const schemaPath = path.join(repoRoot, "docs/process/schemas/frontend-implementation-evidence.schema.json");
@@ -29,6 +30,7 @@ try {
29
30
  const document = parseDocument(readFileSync(path.resolve(positionals[0]), "utf8"), { maxAliasCount: 0, uniqueKeys: true });
30
31
  if (document.errors.length) throw new TypeError(document.errors[0].message);
31
32
  const value = document.toJS({ maxAliasCount: 0 });
33
+ if (!values["allow-template"]) enforceFrontendDelivery(value, { root, phase: value.kind === "verification" ? "implementation" : "inputs" });
32
34
  if (value.schema_version === 1 && !values["allow-template"]) throw new TypeError("migration-required: frontend implementation evidence v1 是只读历史资产;在途 UI 工作必须升级 v2 并绑定 Visual Baseline case_id");
33
35
  const result = spawnSync("python3", ["-c", validator, schemaPath], { cwd: repoRoot, encoding: "utf8", input: JSON.stringify(value) });
34
36
  if (result.status !== 0) throw new TypeError(`${result.stdout}${result.stderr}`.trim());
@@ -21,7 +21,7 @@
21
21
  "revision": "c4d958f18cc947cb91cbe1bd48783fa4601cc18f"
22
22
  },
23
23
  "iloveZzz/yss-harness-design-agent": {
24
- "revision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79"
24
+ "revision": "5c1c98225bb181c7c1add8972c1e44b866205f76"
25
25
  }
26
26
  },
27
27
  "skills": {
@@ -559,7 +559,7 @@
559
559
  "sourceType": "git-submodule",
560
560
  "skillPath": ".agents/skills/prototype-review/SKILL.md",
561
561
  "effectiveHash": "51021f119697dbff644d70c3d6ba3314147c47f412c6f56e4b5540ea3bf554a1",
562
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
562
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
563
563
  "upstreamHash": "2e77b3093a758bb692fb3bda453c5c020d0523f875975e17e2731365f67432e1",
564
564
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
565
565
  "targets": [
@@ -824,7 +824,7 @@
824
824
  "sourceType": "git-submodule",
825
825
  "skillPath": ".agents/skills/yss-antd-design/SKILL.md",
826
826
  "effectiveHash": "917099caa9b76ca3cfd1d0f81f61b28111c1c54ed93e92891f81012dcd0662e2",
827
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
827
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
828
828
  "upstreamHash": "08ce99d45586d5ea19e25fdd9e9a6ede16e66f18992b70b87e0cd23f92343b16",
829
829
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
830
830
  "targets": [
@@ -842,7 +842,7 @@
842
842
  "sourceType": "git-submodule",
843
843
  "skillPath": ".agents/skills/yss-antdv-next-design/SKILL.md",
844
844
  "effectiveHash": "8c55e3a815ace34fbf591e931a58970b3d58cb657e00432ca6c72938d307563b",
845
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
845
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
846
846
  "upstreamHash": "2942ed52dd58a38e51f09eb11254809334aeaed08f21cd081f09f35e6647a68c",
847
847
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
848
848
  "targets": [
@@ -953,7 +953,7 @@
953
953
  "sourceType": "git-submodule",
954
954
  "skillPath": ".agents/skills/yss-design-system/SKILL.md",
955
955
  "effectiveHash": "e69597b8b6a5ce4de5bd40f67630443b8176202f6b028b2ce146a2a1d062816f",
956
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
956
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
957
957
  "upstreamHash": "90aaf06b2514353152464a4d032076fa9d5ef56272d8ad526f5544f3c22a2b00",
958
958
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
959
959
  "targets": [
@@ -1078,7 +1078,7 @@
1078
1078
  "source": "project",
1079
1079
  "sourceType": "local",
1080
1080
  "skillPath": ".agents/skills/yss-frontend-scaffold-generator/SKILL.md",
1081
- "effectiveHash": "16a90623e7351ce1249ed6239df48656a0a9002733a85b678d8d8017a046f026",
1081
+ "effectiveHash": "87bc0d2f0c279d3fff14ded7a07ba7b9f72a61080e7b9ca826bb452894877e4e",
1082
1082
  "targets": [
1083
1083
  ".agents/skills",
1084
1084
  ".claude/skills",
@@ -1108,7 +1108,7 @@
1108
1108
  "source": "project",
1109
1109
  "sourceType": "local",
1110
1110
  "skillPath": ".agents/skills/yss-implementation-contract-compiler/SKILL.md",
1111
- "effectiveHash": "dedc93c358a58ba99ccabf7ce4ecd8c0ef404f5458eae775f3fe03e391cfdb76",
1111
+ "effectiveHash": "abc754700c6ac1c20245923400c0e5c65e5dcf419c7d6674ec9a3fe6a997868b",
1112
1112
  "targets": [
1113
1113
  ".agents/skills",
1114
1114
  ".claude/skills",
@@ -1213,7 +1213,7 @@
1213
1213
  "source": "project",
1214
1214
  "sourceType": "local",
1215
1215
  "skillPath": ".agents/skills/yss-product-lifecycle/SKILL.md",
1216
- "effectiveHash": "0e5b6382626e262b56116a9da291f99bb1251acb53a6366dd8265c0255b3397c",
1216
+ "effectiveHash": "3134144d171d3ac7a52ba37683d6a2972cf7df1f83492a5d58083567f12764af",
1217
1217
  "targets": [
1218
1218
  ".agents/skills",
1219
1219
  ".claude/skills",
@@ -1229,7 +1229,7 @@
1229
1229
  "sourceType": "git-submodule",
1230
1230
  "skillPath": ".agents/skills/yss-prototype-stage/SKILL.md",
1231
1231
  "effectiveHash": "0ecf3125a73d15e4c9b6b665f92535180df82ccb2255fa863ec59f3ff5d460aa",
1232
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
1232
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
1233
1233
  "upstreamHash": "e8f2f3a507da6bf7be0582e4a4dd9659381da8e291755070698a1f51a4e33808",
1234
1234
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
1235
1235
  "targets": [
@@ -1325,7 +1325,7 @@
1325
1325
  "sourceType": "git-submodule",
1326
1326
  "skillPath": ".agents/skills/yss-stage-decision/SKILL.md",
1327
1327
  "effectiveHash": "63e222ec2bc572c8d66fc52143ba3862650d243f57e1e9c71214b533eba37c23",
1328
- "sourceRevision": "31189d2b4b78821ea8bef202d8516ac85b7a3c79",
1328
+ "sourceRevision": "5c1c98225bb181c7c1add8972c1e44b866205f76",
1329
1329
  "upstreamHash": "1eadf230f0e17bf6d209c1df1249acef3520ab29c26a80c476a2a557a224c10a",
1330
1330
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
1331
1331
  "targets": [
@@ -1357,7 +1357,7 @@
1357
1357
  "source": "project",
1358
1358
  "sourceType": "local",
1359
1359
  "skillPath": ".agents/skills/yss-ui/SKILL.md",
1360
- "effectiveHash": "de209a33f15a70aeb6d01bd74bc54b2b181a341b0c14a757cbe78915c3fe7d2f",
1360
+ "effectiveHash": "db94317f36d4fccf981dfe303b8243a403f3c9a763e8a03b51673a6cc22d8370",
1361
1361
  "targets": [
1362
1362
  ".agents/skills",
1363
1363
  ".claude/skills",
@@ -71,5 +71,47 @@
71
71
  "initExcludeRootFiles": [
72
72
  "yss-public-skills.json"
73
73
  ],
74
- "initExcludePaths": []
74
+ "initExcludePaths": [],
75
+ "ownershipPolicy": {
76
+ "version": 1,
77
+ "default": "managed",
78
+ "rules": [
79
+ { "pattern": ".git/**", "ownership": "protected" },
80
+ { "pattern": ".template-source/**", "ownership": "protected" },
81
+ { "pattern": ".cursor/environment.json", "ownership": "protected" },
82
+ { "pattern": ".gitmodules", "ownership": "user-owned" },
83
+ { "pattern": "apps/**", "ownership": "user-owned" },
84
+ { "pattern": "packages/**", "ownership": "user-owned" },
85
+ { "pattern": "wiki/**", "ownership": "user-owned" },
86
+ { "pattern": "docs/reviews/**", "ownership": "user-owned" },
87
+ { "pattern": "docs/.scratch/**", "ownership": "user-owned" },
88
+ { "pattern": "yss-project.yaml", "ownership": "generated" },
89
+ { "pattern": "skills-lock.json", "ownership": "generated" },
90
+ { "pattern": "yss-public-skills.json", "ownership": "generated" },
91
+ { "pattern": ".agents/skills/.yss-skills-manifest.json", "ownership": "generated" },
92
+ { "pattern": "AGENTS.md", "ownership": "managed-customizable" },
93
+ { "pattern": "CLAUDE.md", "ownership": "managed-customizable" },
94
+ { "pattern": "CONTEXT.md", "ownership": "managed-customizable" },
95
+ { "pattern": "DESIGN.md", "ownership": "managed-customizable" },
96
+ { "pattern": "README.md", "ownership": "managed-customizable" },
97
+ { "pattern": ".mcp.json", "ownership": "managed-customizable" }
98
+ ]
99
+ },
100
+ "customizationPolicy": {
101
+ "version": 1,
102
+ "defaultStrategy": "replace-with-force",
103
+ "rules": [
104
+ { "pattern": "CONTEXT.md", "strategy": "manual" },
105
+ { "pattern": "DESIGN.md", "strategy": "manual" }
106
+ ]
107
+ },
108
+ "generatorPolicy": {
109
+ "version": 1,
110
+ "rules": [
111
+ { "pattern": "yss-project.yaml", "generatorId": "repository-identity", "generatorVersion": 1 },
112
+ { "pattern": "skills-lock.json", "generatorId": "skills-lock", "generatorVersion": 1 },
113
+ { "pattern": "yss-public-skills.json", "generatorId": "public-skills", "generatorVersion": 1 },
114
+ { "pattern": ".agents/skills/.yss-skills-manifest.json", "generatorId": "skills-manifest", "generatorVersion": 1 }
115
+ ]
116
+ }
75
117
  }
@@ -3,15 +3,15 @@
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": "21f0db3345135dca3a37ce3a2f288cb11df05d6f",
7
- "templateCommit": "21f0db3345135dca3a37ce3a2f288cb11df05d6f",
8
- "manifestHash": "3c72c098a0f70a8f7c80e8888c67ab76ad9023b5fc5e1019c084d1f15950db17",
6
+ "requestedRef": "4f01bef25dfac465e9ff54c71681cf4d0f74669b",
7
+ "templateCommit": "4f01bef25dfac465e9ff54c71681cf4d0f74669b",
8
+ "manifestHash": "f7520dfdfa388b19ea47cb893c2daab8936019783aa84589f13921e459702280",
9
9
  "encodedPaths": {
10
10
  ".codex/skills/data-analytics/.gitignore": ".codex/skills/data-analytics/__yss_dotfile__.gitignore",
11
11
  ".codex/skills/product-design/.npmignore": ".codex/skills/product-design/__yss_dotfile__.npmignore",
12
12
  ".codex/skills/product-design/templates/prototype/.npmrc": ".codex/skills/product-design/templates/prototype/__yss_dotfile__.npmrc",
13
13
  ".gitignore": "__yss_dotfile__.gitignore"
14
14
  },
15
- "snapshotHash": "1f345f3649ad6e34d616be6a3262f57579b5abb0751aeb82a5299abe0d340234",
16
- "generatedAt": "2026-09-05T09:37:43.210Z"
15
+ "snapshotHash": "3c5bb9dea7cc8f5bf8559f49ee7b31fea830bff51839dfc49e233e7afe9f948d",
16
+ "generatedAt": "2026-09-07T03:27:16.902Z"
17
17
  }