cc-devflow 4.5.8 → 4.5.10

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 (149) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +33 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +9 -4
  3. package/.claude/skills/cc-act/SKILL.md +73 -12
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +30 -0
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +29 -0
  6. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +103 -0
  7. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +61 -5
  8. package/.claude/skills/cc-act/references/closure-contract.md +4 -1
  9. package/.claude/skills/cc-act/references/git-commit-guidelines.md +342 -37
  10. package/.claude/skills/cc-act/scripts/cc-act-common.sh +29 -1
  11. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +164 -0
  12. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +1 -1
  13. package/.claude/skills/cc-check/CHANGELOG.md +17 -0
  14. package/.claude/skills/cc-check/PLAYBOOK.md +1 -0
  15. package/.claude/skills/cc-check/SKILL.md +9 -5
  16. package/.claude/skills/cc-check/references/review-contract.md +7 -0
  17. package/.claude/skills/cc-check/scripts/render-report-card.js +6 -1
  18. package/.claude/skills/cc-dev/CHANGELOG.md +5 -0
  19. package/.claude/skills/cc-dev/SKILL.md +26 -1
  20. package/.claude/skills/cc-do/CHANGELOG.md +23 -0
  21. package/.claude/skills/cc-do/PLAYBOOK.md +7 -7
  22. package/.claude/skills/cc-do/SKILL.md +49 -45
  23. package/.claude/skills/cc-do/references/execution-recovery.md +18 -13
  24. package/.claude/skills/cc-do/scripts/build-task-context.sh +13 -22
  25. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +0 -6
  26. package/.claude/skills/cc-do/scripts/record-review-decision.sh +4 -5
  27. package/.claude/skills/cc-do/scripts/recover-workflow.sh +9 -11
  28. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +12 -10
  29. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +7 -29
  30. package/.claude/skills/cc-investigate/CHANGELOG.md +34 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +21 -5
  32. package/.claude/skills/cc-investigate/SKILL.md +97 -40
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +66 -4
  34. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +30 -59
  35. package/.claude/skills/cc-investigate/assets/{ANALYSIS_TEMPLATE.md → legacy/ANALYSIS_TEMPLATE.md} +48 -0
  36. package/.claude/skills/cc-investigate/references/investigation-contract.md +16 -2
  37. package/.claude/skills/cc-investigate/scripts/bootstrap-analysis.sh +1 -1
  38. package/.claude/skills/cc-next/CHANGELOG.md +6 -0
  39. package/.claude/skills/cc-next/PLAYBOOK.md +26 -4
  40. package/.claude/skills/cc-next/SKILL.md +39 -4
  41. package/.claude/skills/cc-plan/CHANGELOG.md +38 -0
  42. package/.claude/skills/cc-plan/PLAYBOOK.md +60 -53
  43. package/.claude/skills/cc-plan/SKILL.md +164 -87
  44. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +101 -9
  45. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +58 -229
  46. package/.claude/skills/cc-plan/assets/{DESIGN_TEMPLATE.md → legacy/DESIGN_TEMPLATE.md} +68 -0
  47. package/.claude/skills/cc-plan/assets/{TINY_DESIGN_TEMPLATE.md → legacy/TINY_DESIGN_TEMPLATE.md} +47 -1
  48. package/.claude/skills/cc-plan/references/planning-contract.md +48 -33
  49. package/.claude/skills/cc-review/CHANGELOG.md +6 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +9 -11
  51. package/.claude/skills/cc-review/SKILL.md +37 -61
  52. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +1 -1
  53. package/.claude/skills/cc-review/references/implementation-review-branch.md +5 -5
  54. package/.claude/skills/cc-review/references/plan-review-branch.md +1 -1
  55. package/.claude/skills/cc-review/references/review-methods.md +4 -4
  56. package/.claude/skills/cc-review/scripts/collect-review-context.sh +14 -7
  57. package/.claude/skills/cc-roadmap/CHANGELOG.md +6 -0
  58. package/.claude/skills/cc-roadmap/PLAYBOOK.md +30 -0
  59. package/.claude/skills/cc-roadmap/SKILL.md +45 -8
  60. package/.claude/skills/cc-roadmap/assets/BACKLOG_TEMPLATE.md +8 -0
  61. package/.claude/skills/cc-roadmap/assets/ROADMAP_TEMPLATE.md +22 -0
  62. package/.claude/skills/cc-roadmap/assets/TRACKING_TEMPLATE.json +32 -1
  63. package/.claude/skills/cc-roadmap/references/roadmap-dialogue.md +14 -14
  64. package/CHANGELOG.md +28 -0
  65. package/CONTRIBUTING.md +40 -4
  66. package/CONTRIBUTING.zh-CN.md +40 -4
  67. package/README.md +57 -43
  68. package/README.zh-CN.md +57 -43
  69. package/bin/cc-devflow-cli.js +293 -36
  70. package/docs/examples/START-HERE.md +5 -4
  71. package/docs/examples/example-bindings.json +10 -10
  72. package/docs/examples/full-design-blocked/BACKLOG.md +1 -1
  73. package/docs/examples/full-design-blocked/README.md +2 -2
  74. package/docs/examples/full-design-blocked/ROADMAP.md +1 -1
  75. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +2 -1
  76. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +29 -312
  77. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +11 -8
  78. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +4 -4
  79. package/docs/examples/full-design-blocked/roadmap.json +1 -1
  80. package/docs/examples/local-handoff/BACKLOG.md +1 -1
  81. package/docs/examples/local-handoff/README.md +2 -2
  82. package/docs/examples/local-handoff/ROADMAP.md +1 -1
  83. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +2 -1
  84. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +27 -210
  85. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +9 -6
  86. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +1 -1
  87. package/docs/examples/local-handoff/roadmap.json +1 -1
  88. package/docs/examples/pdca-loop/BACKLOG.md +1 -1
  89. package/docs/examples/pdca-loop/README.md +2 -2
  90. package/docs/examples/pdca-loop/ROADMAP.md +1 -1
  91. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +65 -1
  92. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +2 -1
  93. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +26 -228
  94. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +9 -6
  95. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +1 -1
  96. package/docs/examples/pdca-loop/roadmap.json +1 -1
  97. package/docs/examples/scripts/check-example-bindings.sh +11 -5
  98. package/docs/get-shit-done-strategy-audit.md +22 -22
  99. package/docs/guides/artifact-contract.md +44 -0
  100. package/docs/guides/getting-started.md +10 -8
  101. package/docs/guides/getting-started.zh-CN.md +10 -8
  102. package/docs/guides/minimize-artifacts.md +123 -0
  103. package/docs/guides/project-postmortem.md +78 -0
  104. package/lib/compiler/__tests__/skills-registry.test.js +2 -2
  105. package/lib/skill-runtime/CLAUDE.md +1 -1
  106. package/lib/skill-runtime/__tests__/autopilot.test.js +42 -6
  107. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +165 -0
  108. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +2 -2
  109. package/lib/skill-runtime/__tests__/dispatch.test.js +8 -38
  110. package/lib/skill-runtime/__tests__/intent.test.js +4 -20
  111. package/lib/skill-runtime/__tests__/lifecycle.test.js +1 -1
  112. package/lib/skill-runtime/__tests__/paths.test.js +7 -1
  113. package/lib/skill-runtime/__tests__/planner.tdd.test.js +63 -2
  114. package/lib/skill-runtime/__tests__/prepare-pr.test.js +3 -16
  115. package/lib/skill-runtime/__tests__/query.test.js +388 -7
  116. package/lib/skill-runtime/__tests__/review-check-integration.test.js +148 -0
  117. package/lib/skill-runtime/__tests__/review-records.test.js +619 -0
  118. package/lib/skill-runtime/__tests__/runtime.integration.test.js +64 -23
  119. package/lib/skill-runtime/__tests__/schemas.test.js +76 -2
  120. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +137 -0
  121. package/lib/skill-runtime/__tests__/task-contract.test.js +783 -0
  122. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +203 -0
  123. package/lib/skill-runtime/__tests__/worker-run.test.js +4 -11
  124. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +31 -0
  125. package/lib/skill-runtime/__tests__/workflow-context.test.js +98 -0
  126. package/lib/skill-runtime/artifacts.js +0 -5
  127. package/lib/skill-runtime/context-index.js +545 -0
  128. package/lib/skill-runtime/intent.js +9 -33
  129. package/lib/skill-runtime/lifecycle.js +1 -1
  130. package/lib/skill-runtime/operations/CLAUDE.md +2 -2
  131. package/lib/skill-runtime/operations/dispatch.js +4 -42
  132. package/lib/skill-runtime/operations/init.js +2 -6
  133. package/lib/skill-runtime/operations/janitor.js +2 -18
  134. package/lib/skill-runtime/operations/resume.js +21 -38
  135. package/lib/skill-runtime/operations/review-records.js +265 -0
  136. package/lib/skill-runtime/operations/snapshot.js +1 -1
  137. package/lib/skill-runtime/operations/task-contract.js +524 -0
  138. package/lib/skill-runtime/operations/worker-run.js +2 -30
  139. package/lib/skill-runtime/paths.js +4 -4
  140. package/lib/skill-runtime/planner.js +25 -13
  141. package/lib/skill-runtime/query-registry.js +2 -2
  142. package/lib/skill-runtime/query.js +16 -3
  143. package/lib/skill-runtime/review-records.js +123 -0
  144. package/lib/skill-runtime/review.js +246 -11
  145. package/lib/skill-runtime/schemas.js +179 -15
  146. package/lib/skill-runtime/store.js +0 -10
  147. package/lib/skill-runtime/task-contract.js +187 -0
  148. package/lib/skill-runtime/workflow-context.js +748 -0
  149. package/package.json +7 -4
@@ -1,6 +1,6 @@
1
1
  /**
2
- * [INPUT]: 依赖 zod 进行运行时 schema 校验,依赖调用方提供 manifest/report/checkpoint 原始对象。
3
- * [OUTPUT]: 对外提供 Manifest/Task/ReportCard/Checkpoint/Runtime approval schema 与 parse 校验函数。
2
+ * [INPUT]: 依赖 zod 进行运行时 schema 校验,依赖调用方提供 manifest/report/runtime 原始对象。
3
+ * [OUTPUT]: 对外提供 Manifest/Task/ReportCard/Runtime approval schema 与 parse 校验函数。
4
4
  * [POS]: skill runtime 的类型边界层,被 planner/dispatcher/verifier/release 复用。
5
5
  * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
6
  */
@@ -82,7 +82,7 @@ const TaskReviewStateSchema = z.object({
82
82
  const TaskSchema = z.object({
83
83
  id: z.string().regex(TASK_ID_PATTERN, 'Invalid task id'),
84
84
  title: z.string().min(1, 'Task title is required'),
85
- type: z.enum(['TEST', 'IMPL', 'OTHER']).default('OTHER'),
85
+ type: z.enum(['TEST', 'IMPL', 'OTHER', 'REFACTOR', 'VERIFY']).default('OTHER'),
86
86
  phase: z.number().int().min(1).default(1),
87
87
  parallel: z.boolean().default(false),
88
88
  dependsOn: z.array(z.string().regex(TASK_ID_PATTERN)).default([]),
@@ -107,10 +107,10 @@ const ManifestSchema = z.object({
107
107
  createdAt: z.string().datetime(),
108
108
  updatedAt: z.string().datetime(),
109
109
  currentTaskId: z.string().regex(TASK_ID_PATTERN).nullable().default(null),
110
- activePhase: z.number().int().min(1).nullable().default(null),
110
+ activePhase: z.number().int().min(1).nullable().optional(),
111
111
  tasks: z.array(TaskSchema),
112
112
  metadata: z.object({
113
- source: z.enum(['tasks.md', 'default']).default('default'),
113
+ source: z.enum(['tasks.md', 'planning/tasks.md', 'default']).default('default'),
114
114
  generatedBy: z.string().min(1).default('skill:cc-plan'),
115
115
  planVersion: z.number().int().min(1).default(1)
116
116
  }).default({ source: 'default', generatedBy: 'skill:cc-plan', planVersion: 1 })
@@ -161,7 +161,7 @@ const ManifestSchema = z.object({
161
161
  ? Math.min(...unfinished.map((task) => task.phase || 1))
162
162
  : null;
163
163
 
164
- if (manifest.activePhase !== null && manifest.activePhase !== derivedActivePhase) {
164
+ if (manifest.activePhase !== undefined && manifest.activePhase !== null && manifest.activePhase !== derivedActivePhase) {
165
165
  ctx.addIssue({
166
166
  code: z.ZodIssueCode.custom,
167
167
  path: ['activePhase'],
@@ -370,17 +370,31 @@ const ReviewSectionSchema = z.object({
370
370
  findings: z.array(ReviewFindingSchema).default([])
371
371
  });
372
372
 
373
+ const ReviewFreshnessSchema = z.object({
374
+ status: z.enum(['fresh', 'stale', 'unknown', 'not-applicable']).default('unknown'),
375
+ reviewedCommit: z.string().default(''),
376
+ currentCommit: z.string().default(''),
377
+ commitsSinceReview: z.number().int().min(0).nullable().default(null),
378
+ staleReason: z.string().default('')
379
+ });
380
+
381
+ const RecordReviewSchema = ReviewSectionSchema.extend({
382
+ source: z.string().min(1).default('review-records'),
383
+ freshness: ReviewFreshnessSchema.default({
384
+ status: 'unknown',
385
+ reviewedCommit: '',
386
+ currentCommit: '',
387
+ commitsSinceReview: null,
388
+ staleReason: ''
389
+ }),
390
+ errors: z.array(z.string()).default([])
391
+ });
392
+
373
393
  const ReportReviewSchema = z.object({
374
394
  status: ReviewDecisionStatusSchema.default('skipped'),
375
395
  summary: z.string().default(''),
376
396
  details: z.string().default(''),
377
- freshness: z.object({
378
- status: z.enum(['fresh', 'stale', 'unknown', 'not-applicable']).default('unknown'),
379
- reviewedCommit: z.string().default(''),
380
- currentCommit: z.string().default(''),
381
- commitsSinceReview: z.number().int().min(0).nullable().default(null),
382
- staleReason: z.string().default('')
383
- }).default({
397
+ freshness: ReviewFreshnessSchema.default({
384
398
  status: 'unknown',
385
399
  reviewedCommit: '',
386
400
  currentCommit: '',
@@ -403,6 +417,22 @@ const ReportReviewSchema = z.object({
403
417
  reviewers: [],
404
418
  findings: []
405
419
  }),
420
+ recordReview: RecordReviewSchema.default({
421
+ source: 'review-records',
422
+ status: 'skipped',
423
+ required: false,
424
+ summary: '',
425
+ reviewers: [],
426
+ findings: [],
427
+ freshness: {
428
+ status: 'unknown',
429
+ reviewedCommit: '',
430
+ currentCommit: '',
431
+ commitsSinceReview: null,
432
+ staleReason: ''
433
+ },
434
+ errors: []
435
+ }),
406
436
  diffReview: ReviewSectionSchema.default({
407
437
  status: 'skipped',
408
438
  required: false,
@@ -529,6 +559,121 @@ const ReportCardSchema = z.object({
529
559
  timestamp: z.string().datetime()
530
560
  });
531
561
 
562
+ /* ============================================================
563
+ * Review-ledger event schemas (REQ-003 minimize-workflow-artifacts)
564
+ * Source: devflow/changes/REQ-003-minimize-workflow-artifacts/
565
+ * planning/design.md §Interface & Data Contract
566
+ * ============================================================ */
567
+
568
+ const REVIEW_LEDGER_SCHEMA = z.literal('review-ledger.v2');
569
+ const REVIEW_FINDINGS_SCHEMA = z.literal('review-findings.v2');
570
+ const ReviewLedgerRouteSchema = z.enum(['cc-do', 'cc-check', 'cc-plan', 'cc-act', 'no-op']);
571
+ const ReviewLedgerModeSchema = z.enum(['plan', 'implementation', 'mixed']);
572
+ const ReviewLedgerSeveritySchema = z.enum(['critical', 'important', 'advisory']);
573
+ const ReviewLedgerDisplayTierSchema = z.enum(['blocking', 'warning', 'info', 'suppressed']);
574
+ const ReviewLedgerFreshnessStatusSchema = z.enum(['fresh', 'stale', 'unknown']);
575
+ const ReviewLedgerSummaryStatusSchema = z.enum(['clean', 'findings', 'blocked']);
576
+
577
+ const reviewLedgerEventCommon = {
578
+ schema: REVIEW_LEDGER_SCHEMA,
579
+ change: z.string().min(1),
580
+ reviewId: z.string().min(1),
581
+ createdAt: z.string().datetime(),
582
+ createdBy: z.string().min(1)
583
+ };
584
+
585
+ const ReviewLedgerStartedEventSchema = z.object({
586
+ ...reviewLedgerEventCommon,
587
+ event: z.literal('review-started'),
588
+ mode: ReviewLedgerModeSchema,
589
+ scope: z.string().min(1),
590
+ baseSha: z.string().min(1),
591
+ headSha: z.string().min(1),
592
+ selectedNodes: z.array(z.string().min(1)).default([]),
593
+ skippedNodes: z.array(z.object({
594
+ node: z.string().min(1),
595
+ reason: z.string().default('')
596
+ })).default([]),
597
+ riskLanes: z.array(z.string().min(1)).default([])
598
+ });
599
+
600
+ const ReviewLedgerNodeCheckedEventSchema = z.object({
601
+ ...reviewLedgerEventCommon,
602
+ event: z.literal('review-node-checked'),
603
+ nodeId: z.string().min(1),
604
+ mode: ReviewLedgerModeSchema,
605
+ target: z.string().min(1),
606
+ status: z.enum(['checked', 'skipped', 'blocked']),
607
+ coverage: z.array(z.string().min(1)).default([]),
608
+ evidenceRefs: z.array(z.string().min(1)).default([]),
609
+ findings: z.array(z.string().min(1)).default([]),
610
+ next: ReviewLedgerRouteSchema
611
+ });
612
+
613
+ const ReviewLedgerFindingAddedEventSchema = z.object({
614
+ ...reviewLedgerEventCommon,
615
+ event: z.literal('review-finding-added'),
616
+ findingId: z.string().min(1),
617
+ severity: ReviewLedgerSeveritySchema,
618
+ confidence: z.number().min(0).max(10),
619
+ displayTier: ReviewLedgerDisplayTierSchema,
620
+ fingerprint: z.string().min(1),
621
+ scope: z.string().min(1),
622
+ path: z.string().min(1),
623
+ evidence: z.string().min(1),
624
+ recommendation: z.string().min(1),
625
+ route: ReviewLedgerRouteSchema
626
+ });
627
+
628
+ const ReviewLedgerClosedEventSchema = z.object({
629
+ ...reviewLedgerEventCommon,
630
+ event: z.literal('review-closed'),
631
+ status: ReviewLedgerSummaryStatusSchema,
632
+ blockingCount: z.number().int().min(0),
633
+ warningCount: z.number().int().min(0),
634
+ next: ReviewLedgerRouteSchema
635
+ });
636
+
637
+ const ReviewLedgerEventSchema = z.discriminatedUnion('event', [
638
+ ReviewLedgerStartedEventSchema,
639
+ ReviewLedgerNodeCheckedEventSchema,
640
+ ReviewLedgerFindingAddedEventSchema,
641
+ ReviewLedgerClosedEventSchema
642
+ ]);
643
+
644
+ const ReviewLedgerFindingEntrySchema = z.object({
645
+ id: z.string().min(1),
646
+ severity: ReviewLedgerSeveritySchema,
647
+ confidence: z.number().min(0).max(10),
648
+ displayTier: ReviewLedgerDisplayTierSchema,
649
+ fingerprint: z.string().min(1),
650
+ scope: z.string().min(1),
651
+ path: z.string().min(1),
652
+ evidence: z.string().min(1),
653
+ recommendation: z.string().min(1),
654
+ route: ReviewLedgerRouteSchema
655
+ });
656
+
657
+ const ReviewFindingsDocSchema = z.object({
658
+ schema: REVIEW_FINDINGS_SCHEMA,
659
+ change: z.string().min(1),
660
+ reviewId: z.string().min(1),
661
+ headSha: z.string().min(1),
662
+ freshness: z.object({
663
+ status: ReviewLedgerFreshnessStatusSchema,
664
+ reviewedCommit: z.string().min(1),
665
+ currentCommit: z.string().min(1),
666
+ commitsSinceReview: z.number().int().min(0)
667
+ }),
668
+ summary: z.object({
669
+ status: ReviewLedgerSummaryStatusSchema,
670
+ blockingCount: z.number().int().min(0),
671
+ warningCount: z.number().int().min(0),
672
+ next: ReviewLedgerRouteSchema
673
+ }),
674
+ findings: z.array(ReviewLedgerFindingEntrySchema).default([])
675
+ });
676
+
532
677
  function parseWithSchema(schema, input, label) {
533
678
  const parsed = schema.safeParse(input);
534
679
  if (parsed.success) {
@@ -542,7 +687,9 @@ function parseWithSchema(schema, input, label) {
542
687
  }
543
688
 
544
689
  function parseManifest(input) {
545
- return parseWithSchema(ManifestSchema, input, 'Manifest');
690
+ const manifest = parseWithSchema(ManifestSchema, input, 'Manifest');
691
+ delete manifest.activePhase;
692
+ return manifest;
546
693
  }
547
694
 
548
695
  function parseCheckpoint(input) {
@@ -557,6 +704,14 @@ function parseRuntimeState(input) {
557
704
  return parseWithSchema(RuntimeStateSchema, input, 'RuntimeState');
558
705
  }
559
706
 
707
+ function parseReviewLedgerEvent(input) {
708
+ return parseWithSchema(ReviewLedgerEventSchema, input, 'ReviewLedgerEvent');
709
+ }
710
+
711
+ function parseReviewFindingsDoc(input) {
712
+ return parseWithSchema(ReviewFindingsDocSchema, input, 'ReviewFindingsDoc');
713
+ }
714
+
560
715
  module.exports = {
561
716
  ChangeIdSchema,
562
717
  TaskSchema,
@@ -575,8 +730,17 @@ module.exports = {
575
730
  ReviewSectionSchema,
576
731
  ReportReviewSchema,
577
732
  ReportCardSchema,
733
+ ReviewLedgerEventSchema,
734
+ ReviewLedgerStartedEventSchema,
735
+ ReviewLedgerNodeCheckedEventSchema,
736
+ ReviewLedgerFindingAddedEventSchema,
737
+ ReviewLedgerClosedEventSchema,
738
+ ReviewLedgerFindingEntrySchema,
739
+ ReviewFindingsDocSchema,
578
740
  parseManifest,
579
741
  parseCheckpoint,
580
742
  parseReportCard,
581
- parseRuntimeState
743
+ parseRuntimeState,
744
+ parseReviewLedgerEvent,
745
+ parseReviewFindingsDoc
582
746
  };
@@ -80,14 +80,6 @@ function getEventsPath(repoRoot, changeId, taskId, options = {}) {
80
80
  return getTaskPaths(repoRoot, changeId, taskId, options).eventsPath;
81
81
  }
82
82
 
83
- function getCheckpointPath(repoRoot, changeId, taskId, options = {}) {
84
- return getTaskPaths(repoRoot, changeId, taskId, options).checkpointPath;
85
- }
86
-
87
- function getTaskReviewPath(repoRoot, changeId, taskId, kind, options = {}) {
88
- return path.join(getTaskPaths(repoRoot, changeId, taskId, options).taskDir, `review-${kind}.md`);
89
- }
90
-
91
83
  async function exists(filePath) {
92
84
  try {
93
85
  await fsp.access(filePath);
@@ -239,8 +231,6 @@ module.exports = {
239
231
  getRuntimeChangeDir,
240
232
  getRuntimeTaskDir,
241
233
  getEventsPath,
242
- getCheckpointPath,
243
- getTaskReviewPath,
244
234
  exists,
245
235
  ensureDir,
246
236
  readText,
@@ -0,0 +1,187 @@
1
+ /**
2
+ * [INPUT]: 接收 tasks.md 原始 Markdown 字符串;无文件 IO、无全局状态。
3
+ * [OUTPUT]: 对外暴露 extractTasksContractSummary / extractTasksRootCauseContract → {found, content, fields}。
4
+ * [POS]: REQ-003 task-contract 纯函数层;后续 compile / validate / migrate 复用本文件的解析 engine。
5
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
+ */
7
+
8
+ // ============================================================================
9
+ // Section field schema
10
+ // ============================================================================
11
+
12
+ const CONTRACT_FIELD_MAP = {
13
+ 'Change': 'change',
14
+ 'Mode': 'mode',
15
+ 'Profile': 'profile',
16
+ 'Approval': 'approval',
17
+ 'Goal': 'goal',
18
+ 'Do Not Do': 'doNotDo',
19
+ 'Approved Direction': 'approvedDirection',
20
+ 'Acceptance': 'acceptance',
21
+ 'Verification': 'verification',
22
+ 'Risk / Escalate If': 'risk'
23
+ };
24
+
25
+ const CONTRACT_SINGLE_LINE = new Set(['change', 'mode', 'profile', 'approval']);
26
+ const CONTRACT_LIST = new Set(['goal', 'doNotDo', 'approvedDirection', 'acceptance', 'risk']);
27
+ const CONTRACT_VERIFICATION = new Set(['verification']);
28
+
29
+ const ROOT_CAUSE_FIELD_MAP = {
30
+ 'Change': 'change',
31
+ 'Mode': 'mode',
32
+ 'Profile': 'profile',
33
+ 'Diagnosis': 'diagnosis',
34
+ 'Symptom': 'symptom',
35
+ 'Reproduction': 'reproduction',
36
+ 'Expected': 'expected',
37
+ 'Actual': 'actual',
38
+ 'Root Cause': 'rootCause',
39
+ 'Evidence Chain': 'evidenceChain',
40
+ 'Repair Boundary': 'repairBoundary',
41
+ 'Prevention': 'prevention',
42
+ 'Risk / Escalate If': 'risk'
43
+ };
44
+
45
+ const ROOT_CAUSE_SINGLE_LINE = new Set(['change', 'mode', 'profile', 'diagnosis']);
46
+ const ROOT_CAUSE_LIST = new Set([
47
+ 'symptom',
48
+ 'reproduction',
49
+ 'expected',
50
+ 'actual',
51
+ 'rootCause',
52
+ 'evidenceChain',
53
+ 'repairBoundary',
54
+ 'prevention',
55
+ 'risk'
56
+ ]);
57
+ const ROOT_CAUSE_VERIFICATION = new Set();
58
+
59
+ // ============================================================================
60
+ // Heading block extraction (shared engine)
61
+ // ============================================================================
62
+
63
+ function extractHeadingBlock(text, heading) {
64
+ if (typeof text !== 'string') {
65
+ throw new TypeError('task-contract parser expects a string input');
66
+ }
67
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+ const pattern = new RegExp(`(^|\\n)(${escaped}\\r?\\n?)`);
69
+ const match = text.match(pattern);
70
+ if (!match) {
71
+ return { found: false, content: '', body: '' };
72
+ }
73
+ const headingStart = match.index + match[1].length;
74
+ const headingLineEnd = headingStart + match[2].length;
75
+ const rest = text.slice(headingLineEnd);
76
+ const nextIdx = rest.search(/\n## /);
77
+ const blockEnd = nextIdx === -1 ? text.length : headingLineEnd + nextIdx;
78
+ return {
79
+ found: true,
80
+ content: text.slice(headingStart, blockEnd),
81
+ body: text.slice(headingLineEnd, blockEnd)
82
+ };
83
+ }
84
+
85
+ // ============================================================================
86
+ // Generic section parser
87
+ // ============================================================================
88
+
89
+ function parseSectionFields(body, schema) {
90
+ const { fieldMap, singleLineKeys, listKeys, verificationKeys } = schema;
91
+ const lines = body.split('\n');
92
+ const fields = {};
93
+ let currentLabel = null;
94
+ let currentInline = null;
95
+ let currentLines = [];
96
+
97
+ const flush = () => {
98
+ if (!currentLabel) return;
99
+ const key = fieldMap[currentLabel];
100
+ if (key) {
101
+ const value = finalizeFieldValue(key, currentInline, currentLines, singleLineKeys, listKeys, verificationKeys);
102
+ if (value !== undefined) fields[key] = value;
103
+ }
104
+ currentLabel = null;
105
+ currentInline = null;
106
+ currentLines = [];
107
+ };
108
+
109
+ for (const line of lines) {
110
+ const kv = line.match(/^([A-Z][A-Za-z /]+?):\s*(.*)$/);
111
+ if (kv && Object.prototype.hasOwnProperty.call(fieldMap, kv[1])) {
112
+ flush();
113
+ currentLabel = kv[1];
114
+ currentInline = kv[2].length > 0 ? kv[2].trim() : null;
115
+ } else if (currentLabel) {
116
+ currentLines.push(line);
117
+ }
118
+ }
119
+ flush();
120
+ return fields;
121
+ }
122
+
123
+ function finalizeFieldValue(key, inline, lines, singleLineKeys, listKeys, verificationKeys) {
124
+ if (singleLineKeys.has(key)) {
125
+ return inline !== null ? inline : undefined;
126
+ }
127
+ if (listKeys.has(key)) {
128
+ const items = lines
129
+ .map(line => line.match(/^\s*-\s+(.*)$/))
130
+ .filter(Boolean)
131
+ .map(match => match[1].trim());
132
+ if (items.length > 0) return items;
133
+ return inline !== null ? inline : undefined;
134
+ }
135
+ if (verificationKeys.has(key)) {
136
+ const joined = lines.join('\n');
137
+ const fenceMatch = joined.match(/```[^\n]*\n([\s\S]*?)```/);
138
+ if (fenceMatch) return fenceMatch[1].replace(/^\n+|\n+$/g, '');
139
+ const trimmed = joined.replace(/^\n+|\n+$/g, '');
140
+ if (trimmed.length > 0) return trimmed;
141
+ return inline !== null ? inline : undefined;
142
+ }
143
+ return undefined;
144
+ }
145
+
146
+ // ============================================================================
147
+ // Public parsers
148
+ // ============================================================================
149
+
150
+ function extractTasksContractSummary(text) {
151
+ const block = extractHeadingBlock(text, '## Contract Summary');
152
+ if (!block.found) {
153
+ return { found: false, content: '', fields: {} };
154
+ }
155
+ return {
156
+ found: true,
157
+ content: block.content,
158
+ fields: parseSectionFields(block.body, {
159
+ fieldMap: CONTRACT_FIELD_MAP,
160
+ singleLineKeys: CONTRACT_SINGLE_LINE,
161
+ listKeys: CONTRACT_LIST,
162
+ verificationKeys: CONTRACT_VERIFICATION
163
+ })
164
+ };
165
+ }
166
+
167
+ function extractTasksRootCauseContract(text) {
168
+ const block = extractHeadingBlock(text, '## Root Cause Contract');
169
+ if (!block.found) {
170
+ return { found: false, content: '', fields: {} };
171
+ }
172
+ return {
173
+ found: true,
174
+ content: block.content,
175
+ fields: parseSectionFields(block.body, {
176
+ fieldMap: ROOT_CAUSE_FIELD_MAP,
177
+ singleLineKeys: ROOT_CAUSE_SINGLE_LINE,
178
+ listKeys: ROOT_CAUSE_LIST,
179
+ verificationKeys: ROOT_CAUSE_VERIFICATION
180
+ })
181
+ };
182
+ }
183
+
184
+ module.exports = {
185
+ extractTasksContractSummary,
186
+ extractTasksRootCauseContract
187
+ };