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.
- package/README.md +10 -0
- package/cli-core.lock.json +130 -0
- package/package.json +7 -3
- package/resources/upgrade-skill/SKILL.md +26 -0
- package/resources/upgrade-skill/protocol.md +40 -0
- package/resources/upgrade-skill/references/cli-families.md +14 -0
- package/src/cli/help.js +1 -0
- package/src/cli/index.js +12 -0
- package/src/filesystem/transaction-runner.js +27 -0
- package/src/template/asset-runtime.js +9 -1
- package/src/template/instance-runtime.js +4 -2
- package/src/template/verification-runtime.js +9 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
- package/template/.agents/skills/yss-harness-upgrade/SKILL.md +26 -0
- package/template/.agents/skills/yss-harness-upgrade/references/cli-families.md +14 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +15 -19
- package/template/.agents/skills/yss-stage-decision/SKILL.md +3 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
- package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
- package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
- package/template/.codex/skills/yss-harness-upgrade/SKILL.md +26 -0
- package/template/.codex/skills/yss-harness-upgrade/references/cli-families.md +14 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +15 -19
- package/template/.codex/skills/yss-stage-decision/SKILL.md +3 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
- package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
- package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
- package/template/.cursor/skills/yss-harness-upgrade/SKILL.md +26 -0
- package/template/.cursor/skills/yss-harness-upgrade/references/cli-families.md +14 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +15 -19
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +3 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
- package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
- package/template/.pi/skills/yss-harness-upgrade/SKILL.md +26 -0
- package/template/.pi/skills/yss-harness-upgrade/references/cli-families.md +14 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +15 -19
- package/template/.pi/skills/yss-stage-decision/SKILL.md +3 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
- package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
- package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
- package/template/.template-spec/agents/yss-skill-registry.yaml +9 -1
- package/template/.template-spec/process/contract-reading.md +1 -1
- package/template/.template-spec/process/document-writing.md +12 -0
- package/template/.template-spec/process/harness-upgrade.md +40 -0
- package/template/.template-spec/process/lifecycle-artifact-map.md +2 -2
- package/template/.template-spec/process/lifecycle-registry.yaml +3 -1
- package/template/.template-spec/process/schemas/approval-record.schema.json +23 -0
- package/template/.template-spec/process/stage-tracking.md +2 -2
- package/template/.template-spec/process/structured-assets.md +35 -0
- package/template/.template-spec/process/templates/lifecycle-checkpoint-template.json +48 -0
- package/template/.template-spec/templates/approval-record-template.json +23 -0
- package/template/scripts/check-plan-stage-coverage +12 -0
- package/template/scripts/contract +6 -0
- package/template/scripts/lib/approval-record.mjs +4 -3
- package/template/scripts/lib/asset-command.mjs +35 -0
- package/template/scripts/lib/asset-migration.mjs +101 -0
- package/template/scripts/lib/asset-transactions.mjs +235 -0
- package/template/scripts/lib/checkpoint-boundary.mjs +3 -0
- package/template/scripts/lib/contract-views.mjs +7 -1
- package/template/scripts/lib/json-schema.mjs +43 -4
- package/template/scripts/lib/lifecycle-context-query.mjs +7 -0
- package/template/scripts/lib/lifecycle-presentation.mjs +77 -0
- package/template/scripts/lib/lifecycle-status.mjs +16 -1
- package/template/scripts/lib/plan-stage-coverage.mjs +40 -0
- package/template/scripts/lib/reading-view-bundle.mjs +1 -0
- package/template/scripts/lib/reading-view-markdown.mjs +8 -6
- package/template/scripts/lib/stage-tracking-migration.mjs +11 -3
- package/template/scripts/lib/structured-assets.mjs +81 -0
- package/template/scripts/lib/task-package.mjs +4 -1
- package/template/scripts/lifecycle-status +6 -3
- package/template/scripts/verify-lifecycle-checkpoint +8 -5
- package/template/skills-lock.json +21 -9
- package/template.manifest.json +2 -1
- package/template.snapshot.json +5 -5
- package/vendor/cli-core/build.mjs +302 -0
- package/vendor/cli-core/bundle.mjs +198 -0
- package/vendor/cli-core/cli.mjs +182 -0
- package/vendor/cli-core/command-runner.mjs +107 -0
- package/vendor/cli-core/engine.mjs +239 -0
- package/vendor/cli-core/family.mjs +28 -0
- package/vendor/cli-core/gitignore.mjs +15 -0
- package/vendor/cli-core/identity.mjs +274 -0
- package/vendor/cli-core/io.mjs +153 -0
- package/vendor/cli-core/layout-migration.mjs +64 -0
- package/vendor/cli-core/legacy.mjs +19 -0
- package/vendor/cli-core/migrate.mjs +237 -0
- package/vendor/cli-core/migration-adapter.mjs +19 -0
- package/vendor/cli-core/package.json +13 -0
- package/vendor/cli-core/scaffold.mjs +117 -0
- package/vendor/cli-core/spec-migration-adapter.mjs +83 -0
- package/vendor/cli-core/tests/build.test.mjs +40 -0
- package/vendor/cli-core/tests/cli.test.mjs +1065 -0
- package/vendor/cli-core/tests/migration-version.test.mjs +8 -0
- package/vendor/cli-core/tests/package-contract.mjs +89 -0
- package/vendor/cli-core/transaction.mjs +529 -0
- package/vendor/cli-core/update.mjs +79 -0
- package/vendor/cli-core/vendor/NOTICES.md +6 -0
- package/vendor/cli-core/vendor/yaml.LICENSE +13 -0
- package/vendor/cli-core/vendor/yaml.mjs +6311 -0
- 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', '
|
|
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
|
-
|
|
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]
|
|
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 =
|
|
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 =
|
|
58
|
+
if (exists) checkpoint = parseAsset(readTracking(root, checkpoint_ref), checkpoint_ref);
|
|
54
59
|
else {
|
|
55
|
-
const
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
923
|
-
"sourceRevision": "
|
|
924
|
-
"upstreamHash": "
|
|
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",
|
package/template.manifest.json
CHANGED
|
@@ -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"
|
package/template.snapshot.json
CHANGED
|
@@ -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": "
|
|
7
|
-
"templateCommit": "
|
|
6
|
+
"requestedRef": "dd58fc3929b3958579ac8a426f06f087682f2f6c",
|
|
7
|
+
"templateCommit": "dd58fc3929b3958579ac8a426f06f087682f2f6c",
|
|
8
8
|
"sourceState": "committed",
|
|
9
|
-
"manifestHash": "
|
|
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": "
|
|
20
|
-
"generatedAt": "2026-10-
|
|
19
|
+
"snapshotHash": "e97773b2461a3bb7a35551a1f97371af689b94f281bbdb7a635e687d5239b405",
|
|
20
|
+
"generatedAt": "2026-10-01T15:17:35.092Z"
|
|
21
21
|
}
|