kld-sdd 2.7.3 → 2.7.8

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 (65) hide show
  1. package/README.md +12 -5
  2. package/USABILITY.md +84 -0
  3. package/kld-sdd-guide.html +16 -4
  4. package/lib/hook-gate-core.js +327 -0
  5. package/lib/init.js +221 -37
  6. package/lib/scale-thresholds.json +19 -0
  7. package/lib/skills-bundle.js +2 -1
  8. package/package.json +5 -3
  9. package/skywalk-sdd/context-client.cjs +50 -30
  10. package/skywalk-sdd/index.cjs +36 -16
  11. package/skywalk-sdd/kb-sync-identity.cjs +99 -451
  12. package/skywalk-sdd/kb-upload.cjs +67 -44
  13. package/skywalk-sdd/lib/usage-contract.cjs +3 -2
  14. package/skywalk-sdd/lib/usage-reporter.cjs +27 -2
  15. package/skywalk-sdd/metrics-v3.cjs +2 -2
  16. package/skywalk-sdd/ontology/active-changes.cjs +2 -1
  17. package/skywalk-sdd/ontology/archive-package.cjs +1 -1
  18. package/skywalk-sdd/ontology/artifact-parser.cjs +7 -4
  19. package/skywalk-sdd/ontology/id.cjs +5 -4
  20. package/skywalk-sdd/ontology/identity-index.cjs +4 -3
  21. package/skywalk-sdd/ontology/list-changes.cjs +1 -1
  22. package/skywalk-sdd/ontology/runtime.cjs +7 -3
  23. package/skywalk-sdd/ontology/schema.cjs +2 -0
  24. package/skywalk-sdd/ontology/traceability-validator.cjs +74 -4
  25. package/skywalk-sdd/ontology/workspace-layout.cjs +25 -5
  26. package/skywalk-sdd/reporting/change-report-model.cjs +4 -3
  27. package/templates/git-hooks/commit-msg +22 -21
  28. package/templates/git-hooks/consistency-check-core.cjs +1087 -0
  29. package/templates/git-hooks/hooks.config +20 -1
  30. package/templates/git-hooks/pre-commit +22 -21
  31. package/templates/git-hooks/pre-commit-consistency-check.cjs +29 -332
  32. package/templates/git-hooks/pre-commit-sdd-check.cjs +98 -0
  33. package/templates/git-hooks/pre-push +22 -21
  34. package/templates/git-hooks/pre-push-consistency-check.cjs +58 -406
  35. package/templates/hooks/codebuddy/hooks/sdd-tdd-rhythm-gate.cjs +1 -1
  36. package/templates/openspec/tasks.md +3 -3
  37. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +43 -6
  38. package/templates/skills/kld-sdd/opsx-apply/checklist.md +1 -1
  39. package/templates/skills/kld-sdd/opsx-apply/reference.md +1 -1
  40. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +9 -13
  41. package/templates/skills/kld-sdd/opsx-check/SKILL.md +54 -328
  42. package/templates/skills/kld-sdd/opsx-check/checklist.md +7 -4
  43. package/templates/skills/kld-sdd/opsx-check/reference.md +58 -0
  44. package/templates/skills/kld-sdd/opsx-check/result-template.json +52 -0
  45. package/templates/skills/kld-sdd/opsx-check/reviewer.md +100 -0
  46. package/templates/skills/kld-sdd/opsx-consistency-check/SKILL.md +82 -451
  47. package/templates/skills/kld-sdd/opsx-consistency-check/{reference.md → references/reference.md} +0 -1
  48. package/templates/skills/kld-sdd/opsx-consistency-check/scripts/scripts.cjs +517 -0
  49. package/templates/skills/kld-sdd/opsx-design/SKILL.md +10 -30
  50. package/templates/skills/kld-sdd/opsx-design/checklist.md +3 -4
  51. package/templates/skills/kld-sdd/opsx-design/reference.md +1 -1
  52. package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +12 -74
  53. package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +7 -5
  54. package/templates/skills/kld-sdd/opsx-ontology-query/phase-1-prechange.md +2 -2
  55. package/templates/skills/kld-sdd/opsx-ontology-query/reference.md +1 -1
  56. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +28 -73
  57. package/templates/skills/kld-sdd/opsx-propose/checklist.md +8 -8
  58. package/templates/skills/kld-sdd/opsx-propose/interaction-policy.md +28 -0
  59. package/templates/skills/kld-sdd/opsx-propose/reference.md +12 -46
  60. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +14 -61
  61. package/templates/skills/kld-sdd/opsx-spec/checklist.md +3 -3
  62. package/templates/skills/kld-sdd/opsx-task/SKILL.md +38 -32
  63. package/templates/skills/kld-sdd/opsx-task/checklist.md +5 -6
  64. package/templates/skills/kld-sdd/opsx-test/SKILL.md +2 -0
  65. package/templates/skills/kld-sdd/tdd-rules/rules/tdd-strategy-selection.md +1 -1
@@ -244,7 +244,7 @@ function findActiveStage(projectRoot, criteria) {
244
244
  const matchingStarts = [];
245
245
  for (const changeDir of changeDirs) {
246
246
  const dir = path.join(eventsDir, changeDir);
247
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort().reverse();
247
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).reverse();
248
248
  for (const file of files) {
249
249
  const lines = fs.readFileSync(path.join(dir, file), 'utf-8').split('\n').filter(Boolean);
250
250
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -357,7 +357,7 @@ function discoverTaskFiles(projectRoot, changeName) {
357
357
  return walkFiles(changeDir, filePath => {
358
358
  const fileName = path.basename(filePath).toLowerCase();
359
359
  return fileName === 'tasks.md' || fileName === 'task.md';
360
- }).sort();
360
+ }).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
361
361
  }
362
362
 
363
363
  function checklistEntriesOutsideCodeFences(lines) {
@@ -587,7 +587,7 @@ function scanTaskCompletionForArchiveDir(projectRoot, changeName, archiveDir) {
587
587
  const taskFiles = walkFiles(archiveDir, filePath => {
588
588
  const fileName = path.basename(filePath).toLowerCase();
589
589
  return fileName === 'tasks.md' || fileName === 'task.md';
590
- }).sort();
590
+ }).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
591
591
  return scanTaskCompletionFromFiles(normalizedRoot, changeName, taskFiles);
592
592
  }
593
593
 
@@ -796,7 +796,7 @@ function collectSpecFiles(changeRoot) {
796
796
  if (!changeRoot || !fs.existsSync(changeRoot)) return [];
797
797
  const specsDir = path.join(changeRoot, 'specs');
798
798
  if (fs.existsSync(specsDir) && fs.statSync(specsDir).isDirectory()) {
799
- return walkFiles(specsDir, filePath => path.basename(filePath).toLowerCase() === 'spec.md').sort();
799
+ return walkFiles(specsDir, filePath => path.basename(filePath).toLowerCase() === 'spec.md').sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
800
800
  }
801
801
  const rootSpec = path.join(changeRoot, 'spec.md');
802
802
  if (fs.existsSync(rootSpec)) return [rootSpec];
@@ -1503,7 +1503,7 @@ function readEventsRaw(dataDir, changeName) {
1503
1503
  if (!fs.existsSync(dir)) return [];
1504
1504
 
1505
1505
  const events = [];
1506
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort();
1506
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
1507
1507
  for (const file of files) {
1508
1508
  const lines = fs.readFileSync(path.join(dir, file), 'utf-8').split('\n').filter(Boolean);
1509
1509
  for (const line of lines) {
@@ -4859,7 +4859,7 @@ function buildReport(projectRoot, events, options = {}) {
4859
4859
  const scopedReport = scopeEventsForChangeReport(events, options.change);
4860
4860
  events = scopedReport.events;
4861
4861
  const archiveEvent = latestByTimestamp(events.filter(e => {
4862
- return e.command === 'archive' && e.type === 'stage_end';
4862
+ return e.command === 'archive' && e.type === 'stage_end' && e.details?.archive_result;
4863
4863
  }));
4864
4864
  const gitCommitTimeline = resolveGitCommitTimeline(
4865
4865
  events,
@@ -5475,7 +5475,8 @@ function cmdEnd(args, options = {}) {
5475
5475
  command: args.command,
5476
5476
  agent_type: args.agent || args['agent-type'],
5477
5477
  };
5478
- const startEvent = eventId
5478
+ // let:下方占位 change 迁移逻辑需要重新赋值 startEvent(const 会在运行时抛 TypeError)
5479
+ let startEvent = eventId
5479
5480
  ? searchEventInDataDir(dataDir, eventId)
5480
5481
  : findActiveStage(projectRoot, activeCriteria);
5481
5482
  if (!eventId && startEvent) {
@@ -5960,7 +5961,7 @@ function normalizeRepositoryRelativeFiles(files, {
5960
5961
  }
5961
5962
  normalized.push(raw);
5962
5963
  }
5963
- return [...new Set(normalized)].sort();
5964
+ return [...new Set(normalized)].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
5964
5965
  }
5965
5966
 
5966
5967
  function normalizeStrictCheckResult(details, result) {
@@ -6412,13 +6413,13 @@ function normalizeStrictTestResult(args, details) {
6412
6413
 
6413
6414
  const taskId = String(args['task-id'] || args.task_id || input.task_id || '').trim();
6414
6415
  const verifiedTaskIds = Array.isArray(input.verified_task_ids)
6415
- ? [...new Set(input.verified_task_ids.map(value => String(value || '').trim()).filter(Boolean))].sort()
6416
+ ? [...new Set(input.verified_task_ids.map(value => String(value || '').trim()).filter(Boolean))].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
6416
6417
  : [];
6417
6418
  if (!taskId && verifiedTaskIds.length === 0) {
6418
6419
  fail('E_STRICT_FIELD_REQUIRED: strict test_result 必须提供 task_id 或 verified_task_ids');
6419
6420
  }
6420
6421
  if (taskId && !verifiedTaskIds.includes(taskId)) verifiedTaskIds.push(taskId);
6421
- verifiedTaskIds.sort();
6422
+ verifiedTaskIds.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
6422
6423
 
6423
6424
  const normalized = {
6424
6425
  run_id: requireStrictString(args['run-id'] || args.run_id || input.run_id, 'EVENT_RUN_ID_REQUIRED', 'run_id'),
@@ -7859,7 +7860,7 @@ function searchEventInDataDir(dataDir, eventId) {
7859
7860
 
7860
7861
  for (const changeDir of changeDirs) {
7861
7862
  const dir = path.join(eventsDir, changeDir);
7862
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort().reverse();
7863
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).reverse();
7863
7864
  for (const file of files) {
7864
7865
  const lines = fs.readFileSync(path.join(dir, file), 'utf-8').split('\n').filter(Boolean);
7865
7866
  for (const line of lines) {
@@ -7885,7 +7886,7 @@ function readAllEventEndsById(dataDir, eventId, changeName) {
7885
7886
 
7886
7887
  const scanDir = (dir) => {
7887
7888
  if (!fs.existsSync(dir)) return;
7888
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort().reverse();
7889
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl')).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).reverse();
7889
7890
  for (const file of files) {
7890
7891
  const lines = fs.readFileSync(path.join(dir, file), 'utf-8').split('\n').filter(Boolean);
7891
7892
  for (const line of lines) {
@@ -8174,18 +8175,37 @@ function cmdSemanticScan(args) {
8174
8175
 
8175
8176
  function cmdSemanticReconcile(args, options = {}) {
8176
8177
  const context = semanticCommandContext(args);
8178
+ const started = process.hrtime.bigint();
8177
8179
  const result = require('./ontology/runtime.cjs').reconcileChange(
8178
8180
  context.projectRoot,
8179
8181
  context.changeName,
8180
8182
  { profile: context.profile, markPending: Boolean(options.markPending) },
8181
8183
  );
8182
- console.log(JSON.stringify(semanticResultPayload(result), null, 2));
8184
+ const payload = semanticResultPayload(result);
8185
+ if (options.includeTasks) {
8186
+ const semanticFinished = process.hrtime.bigint();
8187
+ // A review packet avoids another CLI launch and a parent-agent document scan.
8188
+ // It is fresh input metadata, never a cached approval or a substitute for review.
8189
+ payload.task_completion = scanTaskCompletion(context.projectRoot, context.changeName);
8190
+ payload.review_inputs = {
8191
+ change_dir: path.join(context.projectRoot, 'openspec', 'changes', context.changeName),
8192
+ files: result.state.files,
8193
+ identity_history_hash: result.state.identity_history_hash,
8194
+ };
8195
+ const finished = process.hrtime.bigint();
8196
+ payload.timings_ms = {
8197
+ semantic: Number(semanticFinished - started) / 1e6,
8198
+ tasks: Number(finished - semanticFinished) / 1e6,
8199
+ total: Number(finished - started) / 1e6,
8200
+ };
8201
+ }
8202
+ console.log(JSON.stringify(payload, null, 2));
8183
8203
  if (!result.valid) process.exitCode = 1;
8184
8204
  return result;
8185
8205
  }
8186
8206
 
8187
8207
  function cmdSemanticCheck(args) {
8188
- return cmdSemanticReconcile(args, { markPending: true });
8208
+ return cmdSemanticReconcile(args, { markPending: true, includeTasks: Boolean(args['include-tasks']) });
8189
8209
  }
8190
8210
 
8191
8211
  function cmdSemanticStatus(args) {
@@ -8476,7 +8496,7 @@ SDD Telemetry CLI - 流程度量采集工具
8476
8496
  node skywalk-sdd/log.cjs semantic-identity --delta-state=modified --entity-id=<uuid> --predecessor-version=<uuid>
8477
8497
  node skywalk-sdd/log.cjs semantic-identity --delta-state=unchanged --entity-id=<uuid> --version-id=<uuid>
8478
8498
  node skywalk-sdd/log.cjs semantic-reconcile --project=<path> --change=<name> [--profile=auto|simple|full|strict]
8479
- node skywalk-sdd/log.cjs semantic-check --project=<path> --change=<name> [--profile=...]
8499
+ node skywalk-sdd/log.cjs semantic-check --project=<path> --change=<name> [--profile=...] [--include-tasks]
8480
8500
  node skywalk-sdd/log.cjs semantic-status --project=<path> --change=<name>
8481
8501
  node skywalk-sdd/log.cjs semantic-observe --project=<path> --change=<name> [--interval=1500]
8482
8502
 
@@ -8494,7 +8514,7 @@ SDD Telemetry CLI - 流程度量采集工具
8494
8514
  semantic-identity 生成或复用实体/版本 ID(8 位十六进制);无需网络和中央 ID 服务
8495
8515
  semantic-scan 只读解析并校验当前 Change
8496
8516
  semantic-reconcile 全量对账文件与本地工作态本体实例
8497
- semantic-check 全量对账并在通过时标记 pending
8517
+ semantic-check 全量对账并在通过时标记 pending;--include-tasks 同时返回任务状态、评审输入清单和程序耗时
8498
8518
  semantic-status 读取当前工作态本体实例
8499
8519
  semantic-observe 观察文件变化并同步;不提供 Hook 式控制,Check/Archive 仍会全量对账
8500
8520