cc-devflow 4.5.9 → 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.
- package/.claude/skills/cc-act/CHANGELOG.md +6 -0
- package/.claude/skills/cc-act/SKILL.md +12 -10
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +1 -1
- package/.claude/skills/cc-act/references/closure-contract.md +1 -1
- package/.claude/skills/cc-act/references/git-commit-guidelines.md +1 -1
- package/.claude/skills/cc-check/CHANGELOG.md +17 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +1 -0
- package/.claude/skills/cc-check/SKILL.md +9 -5
- package/.claude/skills/cc-check/references/review-contract.md +7 -0
- package/.claude/skills/cc-check/scripts/render-report-card.js +6 -1
- package/.claude/skills/cc-dev/CHANGELOG.md +5 -0
- package/.claude/skills/cc-dev/SKILL.md +26 -1
- package/.claude/skills/cc-do/CHANGELOG.md +12 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +7 -7
- package/.claude/skills/cc-do/SKILL.md +35 -37
- package/.claude/skills/cc-do/references/execution-recovery.md +18 -13
- package/.claude/skills/cc-do/scripts/build-task-context.sh +4 -17
- package/.claude/skills/cc-do/scripts/record-review-decision.sh +4 -5
- package/.claude/skills/cc-do/scripts/recover-workflow.sh +9 -11
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +12 -10
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +7 -29
- package/.claude/skills/cc-investigate/CHANGELOG.md +17 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +6 -5
- package/.claude/skills/cc-investigate/SKILL.md +56 -44
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -5
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +4 -3
- package/.claude/skills/cc-investigate/assets/{ANALYSIS_TEMPLATE.md → legacy/ANALYSIS_TEMPLATE.md} +1 -0
- package/.claude/skills/cc-investigate/references/investigation-contract.md +2 -2
- package/.claude/skills/cc-investigate/scripts/bootstrap-analysis.sh +1 -1
- package/.claude/skills/cc-plan/CHANGELOG.md +19 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +55 -53
- package/.claude/skills/cc-plan/SKILL.md +101 -85
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +47 -14
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +4 -2
- package/.claude/skills/cc-plan/assets/{DESIGN_TEMPLATE.md → legacy/DESIGN_TEMPLATE.md} +1 -0
- package/.claude/skills/cc-plan/assets/{TINY_DESIGN_TEMPLATE.md → legacy/TINY_DESIGN_TEMPLATE.md} +1 -1
- package/.claude/skills/cc-plan/references/planning-contract.md +11 -10
- package/.claude/skills/cc-review/CHANGELOG.md +6 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +9 -11
- package/.claude/skills/cc-review/SKILL.md +37 -61
- package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +1 -1
- package/.claude/skills/cc-review/references/implementation-review-branch.md +5 -5
- package/.claude/skills/cc-review/references/plan-review-branch.md +1 -1
- package/.claude/skills/cc-review/references/review-methods.md +4 -4
- package/.claude/skills/cc-review/scripts/collect-review-context.sh +14 -7
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTING.md +40 -4
- package/CONTRIBUTING.zh-CN.md +40 -4
- package/README.md +20 -8
- package/README.zh-CN.md +20 -8
- package/bin/cc-devflow-cli.js +293 -36
- package/docs/examples/START-HERE.md +5 -4
- package/docs/examples/example-bindings.json +8 -8
- package/docs/examples/full-design-blocked/README.md +2 -2
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +2 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +3 -2
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +11 -8
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +4 -4
- package/docs/examples/local-handoff/README.md +2 -2
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +2 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +3 -2
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +9 -6
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +1 -1
- package/docs/examples/pdca-loop/README.md +2 -2
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +2 -2
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +2 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +2 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +9 -6
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +1 -1
- package/docs/examples/scripts/check-example-bindings.sh +2 -0
- package/docs/get-shit-done-strategy-audit.md +22 -22
- package/docs/guides/artifact-contract.md +1 -1
- package/docs/guides/getting-started.md +10 -8
- package/docs/guides/getting-started.zh-CN.md +10 -8
- package/docs/guides/minimize-artifacts.md +123 -0
- package/lib/compiler/__tests__/skills-registry.test.js +2 -2
- package/lib/skill-runtime/CLAUDE.md +1 -1
- package/lib/skill-runtime/__tests__/autopilot.test.js +42 -6
- package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +165 -0
- package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +2 -2
- package/lib/skill-runtime/__tests__/dispatch.test.js +8 -38
- package/lib/skill-runtime/__tests__/intent.test.js +4 -20
- package/lib/skill-runtime/__tests__/lifecycle.test.js +1 -1
- package/lib/skill-runtime/__tests__/paths.test.js +7 -1
- package/lib/skill-runtime/__tests__/planner.tdd.test.js +61 -0
- package/lib/skill-runtime/__tests__/prepare-pr.test.js +3 -16
- package/lib/skill-runtime/__tests__/query.test.js +388 -7
- package/lib/skill-runtime/__tests__/review-check-integration.test.js +148 -0
- package/lib/skill-runtime/__tests__/review-records.test.js +619 -0
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +64 -23
- package/lib/skill-runtime/__tests__/schemas.test.js +43 -0
- package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +137 -0
- package/lib/skill-runtime/__tests__/task-contract.test.js +783 -0
- package/lib/skill-runtime/__tests__/verify-artifacts.test.js +203 -0
- package/lib/skill-runtime/__tests__/worker-run.test.js +4 -11
- package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +31 -0
- package/lib/skill-runtime/__tests__/workflow-context.test.js +98 -0
- package/lib/skill-runtime/artifacts.js +0 -5
- package/lib/skill-runtime/context-index.js +545 -0
- package/lib/skill-runtime/intent.js +9 -33
- package/lib/skill-runtime/lifecycle.js +1 -1
- package/lib/skill-runtime/operations/CLAUDE.md +2 -2
- package/lib/skill-runtime/operations/dispatch.js +4 -42
- package/lib/skill-runtime/operations/init.js +2 -6
- package/lib/skill-runtime/operations/janitor.js +2 -18
- package/lib/skill-runtime/operations/resume.js +21 -38
- package/lib/skill-runtime/operations/review-records.js +265 -0
- package/lib/skill-runtime/operations/snapshot.js +1 -1
- package/lib/skill-runtime/operations/task-contract.js +524 -0
- package/lib/skill-runtime/operations/worker-run.js +2 -30
- package/lib/skill-runtime/paths.js +4 -4
- package/lib/skill-runtime/planner.js +24 -11
- package/lib/skill-runtime/query-registry.js +2 -2
- package/lib/skill-runtime/query.js +15 -2
- package/lib/skill-runtime/review-records.js +123 -0
- package/lib/skill-runtime/review.js +246 -11
- package/lib/skill-runtime/schemas.js +174 -12
- package/lib/skill-runtime/store.js +0 -10
- package/lib/skill-runtime/task-contract.js +187 -0
- package/lib/skill-runtime/workflow-context.js +748 -0
- package/package.json +7 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [INPUT]: 依赖 zod 进行运行时 schema 校验,依赖调用方提供 manifest/report/
|
|
3
|
-
* [OUTPUT]: 对外提供 Manifest/Task/ReportCard/
|
|
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([]),
|
|
@@ -110,7 +110,7 @@ const ManifestSchema = z.object({
|
|
|
110
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 })
|
|
@@ -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:
|
|
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) {
|
|
@@ -559,6 +704,14 @@ function parseRuntimeState(input) {
|
|
|
559
704
|
return parseWithSchema(RuntimeStateSchema, input, 'RuntimeState');
|
|
560
705
|
}
|
|
561
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
|
+
|
|
562
715
|
module.exports = {
|
|
563
716
|
ChangeIdSchema,
|
|
564
717
|
TaskSchema,
|
|
@@ -577,8 +730,17 @@ module.exports = {
|
|
|
577
730
|
ReviewSectionSchema,
|
|
578
731
|
ReportReviewSchema,
|
|
579
732
|
ReportCardSchema,
|
|
733
|
+
ReviewLedgerEventSchema,
|
|
734
|
+
ReviewLedgerStartedEventSchema,
|
|
735
|
+
ReviewLedgerNodeCheckedEventSchema,
|
|
736
|
+
ReviewLedgerFindingAddedEventSchema,
|
|
737
|
+
ReviewLedgerClosedEventSchema,
|
|
738
|
+
ReviewLedgerFindingEntrySchema,
|
|
739
|
+
ReviewFindingsDocSchema,
|
|
580
740
|
parseManifest,
|
|
581
741
|
parseCheckpoint,
|
|
582
742
|
parseReportCard,
|
|
583
|
-
parseRuntimeState
|
|
743
|
+
parseRuntimeState,
|
|
744
|
+
parseReviewLedgerEvent,
|
|
745
|
+
parseReviewFindingsDoc
|
|
584
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
|
+
};
|