cc-devflow 4.5.1 → 4.5.2

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 (36) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +14 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +26 -1
  3. package/.claude/skills/cc-act/SKILL.md +36 -7
  4. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +20 -0
  5. package/.claude/skills/cc-act/references/closure-contract.md +8 -0
  6. package/.claude/skills/cc-act/scripts/cc-act-common.sh +6 -1
  7. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +99 -0
  8. package/.claude/skills/cc-act/scripts/verify-act-gate.sh +17 -1
  9. package/.claude/skills/cc-check/CHANGELOG.md +14 -0
  10. package/.claude/skills/cc-check/PLAYBOOK.md +101 -1
  11. package/.claude/skills/cc-check/SKILL.md +128 -7
  12. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +121 -1
  13. package/.claude/skills/cc-check/references/review-contract.md +88 -0
  14. package/.claude/skills/cc-check/scripts/render-report-card.js +172 -5
  15. package/.claude/skills/cc-check/scripts/verify-gate.sh +21 -0
  16. package/.claude/skills/cc-investigate/CHANGELOG.md +13 -0
  17. package/.claude/skills/cc-investigate/PLAYBOOK.md +105 -4
  18. package/.claude/skills/cc-investigate/SKILL.md +185 -8
  19. package/.claude/skills/cc-investigate/assets/ANALYSIS_TEMPLATE.md +77 -3
  20. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +10 -3
  21. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +102 -1
  22. package/.claude/skills/cc-investigate/references/investigation-contract.md +146 -0
  23. package/.claude/skills/cc-simplify/CHANGELOG.md +15 -0
  24. package/.claude/skills/cc-simplify/SKILL.md +255 -35
  25. package/CHANGELOG.md +16 -0
  26. package/docs/examples/example-bindings.json +3 -3
  27. package/docs/examples/full-design-blocked/README.md +1 -1
  28. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +140 -3
  29. package/docs/examples/local-handoff/README.md +1 -1
  30. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +92 -0
  31. package/docs/examples/pdca-loop/README.md +1 -1
  32. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +20 -0
  33. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +92 -0
  34. package/lib/skill-runtime/review.js +64 -1
  35. package/lib/skill-runtime/schemas.js +150 -3
  36. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  - Example version: `1.0.0`
6
6
  - Last reviewed: `2026-04-17`
7
- - Bound skills: `cc-roadmap@4.3.4`, `cc-plan@3.5.6`, `cc-do@1.5.2`, `cc-check@1.8.2`, `cc-act@1.6.2`
7
+ - Bound skills: `cc-roadmap@4.3.4`, `cc-plan@3.5.6`, `cc-do@1.5.2`, `cc-check@1.8.4`, `cc-act@1.6.4`
8
8
 
9
9
  This folder shows one minimal but complete `cc-roadmap -> cc-plan -> cc-do -> cc-check -> cc-act` loop.
10
10
 
@@ -20,6 +20,25 @@
20
20
  - Base branch: `main`
21
21
  - PR / MR: not created yet
22
22
 
23
+ ## Review Range
24
+
25
+ - Reviewed base SHA: `example-base`
26
+ - Reviewed head SHA: `example-head`
27
+ - Review packet: `planning/tasks.md#T001-T003`; `planning/design.md`
28
+ - Finding triage: no findings
29
+ - QA / claim evidence: `qa=pass`, `tests-pass=pass`, `requirements-met=pass`
30
+
31
+ ## Readiness Dashboard
32
+
33
+ - Review freshness: `fresh`, reviewed `example-head`, current `example-head`
34
+ - Review quality: `qualityScore=9`
35
+ - Specialist review facets: `testing:pass`
36
+ - QA coverage: `status=pass`, gaps `0`, e2eRequired `false`
37
+ - Browser QA: `skipped`, example fixture uses targeted component evidence instead of a live browser
38
+ - Failure ownership: no open failures recorded
39
+ - Documentation release: README and CLAUDE unchanged for this scoped example
40
+ - PR body accuracy: regenerate the PR body from this brief, current report-card, and current diff
41
+
23
42
  ## Summary
24
43
 
25
44
  - removes a small but visible sharing friction in the beta flow
@@ -35,6 +54,7 @@
35
54
  ## Verification Evidence
36
55
 
37
56
  - `report-card.json` verdict: `pass`
57
+ - Merged-result verification: not applicable before merge
38
58
  - Fresh evidence:
39
59
  - `npm test -- src/features/share/ShareDialog.test.tsx`
40
60
  - `npm run lint -- src/features/share/ShareDialog.tsx`
@@ -3,6 +3,65 @@
3
3
  "verdict": "pass",
4
4
  "overall": "pass",
5
5
  "summary": "verdict=pass quick=2/2 strict=0/0 review=pass",
6
+ "specAlignment": "pass",
7
+ "specDeltaVerified": true,
8
+ "specSyncReady": true,
9
+ "runtime": {
10
+ "status": "pass",
11
+ "failureOwnership": []
12
+ },
13
+ "claimEvidence": [
14
+ {
15
+ "claim": "tests-pass",
16
+ "requiredProof": "fresh targeted test command with exit 0",
17
+ "commandOrArtifact": "npm test -- src/features/share/ShareDialog.test.tsx",
18
+ "exitStatus": 0,
19
+ "keyObservation": "share dialog targeted tests passed",
20
+ "status": "pass"
21
+ },
22
+ {
23
+ "claim": "requirements-met",
24
+ "requiredProof": "line-by-line planning/tasks.md and task-manifest.json checklist",
25
+ "commandOrArtifact": "planning/tasks.md + planning/task-manifest.json",
26
+ "exitStatus": null,
27
+ "keyObservation": "T001-T003 complete with spec/code review proof",
28
+ "status": "pass"
29
+ }
30
+ ],
31
+ "qa": {
32
+ "status": "pass",
33
+ "regressionProof": [],
34
+ "testQuality": [
35
+ {
36
+ "area": "share-dialog",
37
+ "checksRealBehavior": true,
38
+ "mockBoundary": "none",
39
+ "testOnlyProductionApi": false,
40
+ "status": "pass"
41
+ }
42
+ ],
43
+ "coverageAudit": {
44
+ "status": "pass",
45
+ "coveragePct": 80,
46
+ "pathMap": ["copy invite link success path", "clipboard fallback error path"],
47
+ "gaps": [],
48
+ "testsAdded": ["src/features/share/ShareDialog.test.tsx"],
49
+ "e2eRequired": false,
50
+ "evalRequired": false,
51
+ "qualityStars": "★★"
52
+ },
53
+ "browserEvidence": {
54
+ "status": "skipped",
55
+ "mode": "not-applicable",
56
+ "affectedRoutes": [],
57
+ "screenshots": [],
58
+ "consoleErrors": [],
59
+ "healthScore": null,
60
+ "issues": [],
61
+ "skipReason": "example fixture uses targeted component evidence instead of a live browser"
62
+ },
63
+ "tddException": null
64
+ },
6
65
  "quickGates": [
7
66
  {
8
67
  "name": "targeted-tests",
@@ -22,10 +81,35 @@
22
81
  "status": "pass",
23
82
  "summary": "Task review proof and requirement-level diff review both passed",
24
83
  "details": "T001-T003 have matching spec/code review proof and the diff stays inside the approved tiny-design boundary.",
84
+ "freshness": {
85
+ "status": "fresh",
86
+ "reviewedCommit": "example-head",
87
+ "currentCommit": "example-head",
88
+ "commitsSinceReview": 0,
89
+ "staleReason": ""
90
+ },
91
+ "qualityScore": 9,
92
+ "specialistReviews": [
93
+ {
94
+ "name": "testing",
95
+ "status": "pass",
96
+ "required": true,
97
+ "summary": "targeted tests cover the share dialog behavior for this example",
98
+ "skipReason": "",
99
+ "findings": []
100
+ }
101
+ ],
25
102
  "taskReviews": {
26
103
  "status": "pass",
27
104
  "required": true,
28
105
  "summary": "All completed tasks carry spec/code proof",
106
+ "reviewPacket": {
107
+ "baseSha": "example-base",
108
+ "headSha": "example-head",
109
+ "requirements": "planning/tasks.md#T001-T003",
110
+ "implemented": "copy invite link workflow",
111
+ "reviewerContext": "task manifest plus changed share dialog files"
112
+ },
29
113
  "reviewers": [],
30
114
  "findings": []
31
115
  },
@@ -33,12 +117,20 @@
33
117
  "status": "pass",
34
118
  "required": false,
35
119
  "summary": "No blocking requirement-level findings",
120
+ "reviewPacket": {
121
+ "baseSha": "example-base",
122
+ "headSha": "example-head",
123
+ "requirements": "planning/design.md",
124
+ "implemented": "copy invite link workflow",
125
+ "reviewerContext": "requirement diff, plan completion, scope drift, docs staleness"
126
+ },
36
127
  "reviewers": [],
37
128
  "findings": []
38
129
  },
39
130
  "findings": []
40
131
  },
41
132
  "blockingFindings": [],
133
+ "gaps": [],
42
134
  "reroute": "none",
43
135
  "timestamp": "2026-04-15T11:20:00.000Z"
44
136
  }
@@ -79,6 +79,7 @@ function makeFinding({
79
79
  status = 'open',
80
80
  fingerprint
81
81
  }) {
82
+ const confidenceScore = severityRank(severity) >= severityRank('important') ? 8 : 6;
82
83
  return {
83
84
  id,
84
85
  source,
@@ -91,7 +92,12 @@ function makeFinding({
91
92
  ...(typeof line === 'number' ? { line } : {}),
92
93
  action,
93
94
  status,
94
- ...(fingerprint ? { fingerprint } : {})
95
+ confidenceScore,
96
+ fingerprint: fingerprint || `${source}:${category}:${id}`,
97
+ displayTier: status === 'informational'
98
+ ? 'info'
99
+ : (severityRank(severity) >= severityRank('important') ? 'blocking' : 'warning'),
100
+ suppressionReason: null
95
101
  };
96
102
  }
97
103
 
@@ -231,6 +237,14 @@ async function detectBaseRef(repoRoot) {
231
237
  return 'HEAD~1';
232
238
  }
233
239
 
240
+ async function detectCurrentCommit(repoRoot) {
241
+ const result = await runCommand('git rev-parse --short HEAD', { cwd: repoRoot });
242
+ if (result.code === 0 && result.stdout.trim()) {
243
+ return result.stdout.trim();
244
+ }
245
+ return 'working-tree';
246
+ }
247
+
234
248
  async function runCodexStructuredReview({ repoRoot, baseRef }) {
235
249
  const command = [
236
250
  'codex review',
@@ -530,6 +544,7 @@ async function runReviewSuite({ repoRoot, changeId, manifest, strict, skipReview
530
544
  const diffReview = await runDiffReviewSection({ repoRoot, strict, skipReview });
531
545
  const findings = flattenFindings(taskReviews, diffReview);
532
546
  const status = deriveReviewStatus(taskReviews, diffReview);
547
+ const currentCommit = await detectCurrentCommit(repoRoot);
533
548
  const details = [
534
549
  taskReviews.summary,
535
550
  diffReview.summary
@@ -539,6 +554,54 @@ async function runReviewSuite({ repoRoot, changeId, manifest, strict, skipReview
539
554
  status,
540
555
  summary: `Task review: ${taskReviews.status}. Diff review: ${diffReview.status}.`,
541
556
  details,
557
+ freshness: {
558
+ status: 'fresh',
559
+ reviewedCommit: currentCommit,
560
+ currentCommit,
561
+ commitsSinceReview: 0,
562
+ staleReason: ''
563
+ },
564
+ qualityScore: status === 'pass' ? 9 : (status === 'blocked' ? 5 : 3),
565
+ specialistReviews: [
566
+ {
567
+ name: 'testing',
568
+ status: taskReviews.status,
569
+ required: true,
570
+ summary: taskReviews.summary,
571
+ skipReason: '',
572
+ findings: []
573
+ }
574
+ ],
575
+ runtime: {
576
+ status: status === 'pass' ? 'pass' : (status === 'fail' ? 'fail' : 'blocked'),
577
+ failureOwnership: []
578
+ },
579
+ qa: {
580
+ status: 'pass',
581
+ regressionProof: [],
582
+ testQuality: [],
583
+ coverageAudit: {
584
+ status: 'pass',
585
+ coveragePct: null,
586
+ pathMap: [],
587
+ gaps: [],
588
+ testsAdded: [],
589
+ e2eRequired: false,
590
+ evalRequired: false,
591
+ qualityStars: ''
592
+ },
593
+ browserEvidence: {
594
+ status: 'skipped',
595
+ mode: 'not-applicable',
596
+ affectedRoutes: [],
597
+ screenshots: [],
598
+ consoleErrors: [],
599
+ healthScore: null,
600
+ issues: [],
601
+ skipReason: 'runtime verify did not identify a UI browser QA target'
602
+ },
603
+ tddException: null
604
+ },
542
605
  taskReviews,
543
606
  diffReview,
544
607
  findings
@@ -269,8 +269,26 @@ const GateResultSchema = z.object({
269
269
  });
270
270
 
271
271
  const ReviewSeveritySchema = z.enum(['critical', 'important', 'minor', 'info']);
272
- const ReviewActionSchema = z.enum(['fix_now', 'follow_up', 'cc-investigate', 'none']);
273
- const ReviewFindingStatusSchema = z.enum(['open', 'resolved', 'accepted', 'informational']);
272
+ const ReviewActionSchema = z.enum([
273
+ 'fix_now',
274
+ 'follow_up',
275
+ 'cc-investigate',
276
+ 'reroute-cc-do',
277
+ 'reroute-cc-plan',
278
+ 'reroute-cc-investigate',
279
+ 'document-follow-up',
280
+ 'none'
281
+ ]);
282
+ const ReviewFindingStatusSchema = z.enum([
283
+ 'open',
284
+ 'resolved',
285
+ 'accepted',
286
+ 'informational',
287
+ 'accepted-fixed',
288
+ 'rejected-with-evidence',
289
+ 'deferred-minor',
290
+ 'clarification-needed'
291
+ ]);
274
292
 
275
293
  const ReviewEvidenceSchema = z.object({
276
294
  kind: z.enum(['command', 'file', 'note']),
@@ -291,7 +309,11 @@ const ReviewFindingSchema = z.object({
291
309
  line: z.number().int().min(1).optional(),
292
310
  action: ReviewActionSchema.default('none'),
293
311
  status: ReviewFindingStatusSchema.default('open'),
294
- fingerprint: z.string().optional()
312
+ fingerprint: z.string().optional(),
313
+ confidence: z.enum(['high', 'medium', 'low']).optional(),
314
+ confidenceScore: z.number().min(1).max(10).optional(),
315
+ displayTier: z.enum(['blocking', 'warning', 'info', 'suppressed']).optional(),
316
+ suppressionReason: z.string().nullable().optional()
295
317
  });
296
318
 
297
319
  const ReviewerResultSchema = z.object({
@@ -317,6 +339,28 @@ const ReportReviewSchema = z.object({
317
339
  status: ReviewDecisionStatusSchema.default('skipped'),
318
340
  summary: z.string().default(''),
319
341
  details: z.string().default(''),
342
+ freshness: z.object({
343
+ status: z.enum(['fresh', 'stale', 'unknown', 'not-applicable']).default('unknown'),
344
+ reviewedCommit: z.string().default(''),
345
+ currentCommit: z.string().default(''),
346
+ commitsSinceReview: z.number().int().min(0).nullable().default(null),
347
+ staleReason: z.string().default('')
348
+ }).default({
349
+ status: 'unknown',
350
+ reviewedCommit: '',
351
+ currentCommit: '',
352
+ commitsSinceReview: null,
353
+ staleReason: ''
354
+ }),
355
+ qualityScore: z.number().min(0).max(10).nullable().default(null),
356
+ specialistReviews: z.array(z.object({
357
+ name: z.string().min(1),
358
+ status: ReviewDecisionStatusSchema,
359
+ required: z.boolean().default(false),
360
+ summary: z.string().default(''),
361
+ skipReason: z.string().default(''),
362
+ findings: z.array(ReviewFindingSchema).default([])
363
+ })).default([]),
320
364
  taskReviews: ReviewSectionSchema.default({
321
365
  status: 'skipped',
322
366
  required: false,
@@ -334,15 +378,118 @@ const ReportReviewSchema = z.object({
334
378
  findings: z.array(ReviewFindingSchema).default([])
335
379
  });
336
380
 
381
+ const ClaimEvidenceSchema = z.object({
382
+ claim: z.string().min(1),
383
+ requiredProof: z.string().min(1),
384
+ commandOrArtifact: z.string().min(1),
385
+ exitStatus: z.number().int().nullable().optional(),
386
+ keyObservation: z.string().default(''),
387
+ status: ReviewDecisionStatusSchema
388
+ });
389
+
390
+ const RuntimeFailureOwnershipSchema = z.object({
391
+ failure: z.string().min(1),
392
+ classification: z.enum(['in-branch', 'pre-existing', 'environment', 'ambiguous']),
393
+ touchedByDiff: z.boolean().optional(),
394
+ evidence: z.string().default(''),
395
+ action: z.string().default(''),
396
+ status: z.string().default('open')
397
+ });
398
+
399
+ const ReportRuntimeSchema = z.object({
400
+ status: ReviewDecisionStatusSchema.default('skipped'),
401
+ failureOwnership: z.array(RuntimeFailureOwnershipSchema).default([])
402
+ }).default({
403
+ status: 'skipped',
404
+ failureOwnership: []
405
+ });
406
+
407
+ const QaSchema = z.object({
408
+ status: ReviewDecisionStatusSchema.default('skipped'),
409
+ regressionProof: z.array(z.record(z.any())).default([]),
410
+ testQuality: z.array(z.record(z.any())).default([]),
411
+ coverageAudit: z.object({
412
+ status: ReviewDecisionStatusSchema.default('skipped'),
413
+ coveragePct: z.number().nullable().default(null),
414
+ pathMap: z.array(z.string()).default([]),
415
+ gaps: z.array(z.string()).default([]),
416
+ testsAdded: z.array(z.string()).default([]),
417
+ e2eRequired: z.boolean().default(false),
418
+ evalRequired: z.boolean().default(false),
419
+ qualityStars: z.string().default('')
420
+ }).default({
421
+ status: 'skipped',
422
+ coveragePct: null,
423
+ pathMap: [],
424
+ gaps: [],
425
+ testsAdded: [],
426
+ e2eRequired: false,
427
+ evalRequired: false,
428
+ qualityStars: ''
429
+ }),
430
+ browserEvidence: z.object({
431
+ status: ReviewDecisionStatusSchema.default('skipped'),
432
+ mode: z.string().default('not-applicable'),
433
+ affectedRoutes: z.array(z.string()).default([]),
434
+ screenshots: z.array(z.string()).default([]),
435
+ consoleErrors: z.array(z.string()).default([]),
436
+ healthScore: z.number().nullable().default(null),
437
+ issues: z.array(z.record(z.any())).default([]),
438
+ skipReason: z.string().default('')
439
+ }).default({
440
+ status: 'skipped',
441
+ mode: 'not-applicable',
442
+ affectedRoutes: [],
443
+ screenshots: [],
444
+ consoleErrors: [],
445
+ healthScore: null,
446
+ issues: [],
447
+ skipReason: ''
448
+ }),
449
+ tddException: z.string().nullable().default(null)
450
+ }).default({
451
+ status: 'skipped',
452
+ regressionProof: [],
453
+ testQuality: [],
454
+ coverageAudit: {
455
+ status: 'skipped',
456
+ coveragePct: null,
457
+ pathMap: [],
458
+ gaps: [],
459
+ testsAdded: [],
460
+ e2eRequired: false,
461
+ evalRequired: false,
462
+ qualityStars: ''
463
+ },
464
+ browserEvidence: {
465
+ status: 'skipped',
466
+ mode: 'not-applicable',
467
+ affectedRoutes: [],
468
+ screenshots: [],
469
+ consoleErrors: [],
470
+ healthScore: null,
471
+ issues: [],
472
+ skipReason: ''
473
+ },
474
+ tddException: null
475
+ });
476
+
337
477
  const ReportCardSchema = z.object({
338
478
  changeId: ChangeIdSchema,
339
479
  verdict: z.enum(['pass', 'fail', 'blocked']).optional(),
340
480
  overall: z.enum(['pass', 'fail']),
341
481
  summary: z.string().default(''),
482
+ specAlignment: z.enum(['pass', 'fail', 'blocked']).default('blocked'),
483
+ specDeltaVerified: z.boolean().default(false),
484
+ specSyncReady: z.boolean().default(false),
485
+ runtime: ReportRuntimeSchema,
486
+ claimEvidence: z.array(ClaimEvidenceSchema).default([]),
487
+ qa: QaSchema,
342
488
  quickGates: z.array(GateResultSchema),
343
489
  strictGates: z.array(GateResultSchema),
344
490
  review: ReportReviewSchema,
345
491
  blockingFindings: z.array(z.string()),
492
+ gaps: z.array(z.string()).default([]),
346
493
  reroute: z.enum(['none', 'cc-do', 'cc-investigate', 'cc-plan']).default('none'),
347
494
  timestamp: z.string().datetime()
348
495
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-devflow",
3
- "version": "4.5.1",
3
+ "version": "4.5.2",
4
4
  "description": "Multi-platform CLI and skill pack for agent coding",
5
5
  "main": "bin/cc-devflow.js",
6
6
  "bin": {