lee-spec-kit 0.9.7 → 0.9.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.
- package/dist/index.js +90 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/en/common/README.md +2 -2
- package/templates/en/common/agents/agents.md +1 -1
- package/templates/en/common/agents/skills/create-feature.md +2 -2
- package/templates/en/common/agents/skills/create-pr.md +2 -2
- package/templates/en/common/agents/skills/execute-task.md +2 -2
- package/templates/en/common/features/feature-base/tasks.md +1 -1
- package/templates/ko/common/README.md +2 -2
- package/templates/ko/common/agents/agents.md +1 -1
- package/templates/ko/common/agents/skills/create-feature.md +2 -2
- package/templates/ko/common/agents/skills/create-pr.md +2 -2
- package/templates/ko/common/agents/skills/execute-task.md +2 -2
- package/templates/ko/common/features/feature-base/tasks.md +1 -1
package/dist/index.js
CHANGED
|
@@ -215,7 +215,7 @@ var koCli = {
|
|
|
215
215
|
"init.choice.taskAgent.on": "\uC0AC\uC6A9 - \uAC01 Task \uAD6C\uD604\uC744 \uC704\uC784",
|
|
216
216
|
"init.choice.taskAgent.off": "\uC0AC\uC6A9 \uC548 \uD568 - \uBA54\uC778 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC9C1\uC811 \uAD6C\uD604",
|
|
217
217
|
"init.prompt.reviews": "\uB3C5\uB9BD \uAC80\uC218 \uB2E8\uACC4\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
218
|
-
"init.prompt.maxReviewRounds": "\uC794\uC5EC \uC704\uD5D8\uC744 \uAE30\uB85D\uD558\uACE0 \uACC4\uC18D\uD558\uAE30 \uC804 \uB9AC\uBDF0 \
|
|
218
|
+
"init.prompt.maxReviewRounds": "\uC794\uC5EC \uC704\uD5D8\uC744 \uAE30\uB85D\uD558\uACE0 \uACC4\uC18D\uD558\uAE30 \uC804 fresh \uB9AC\uBDF0 \uCD5C\uB300 \uC2E4\uD589 \uD69F\uC218:",
|
|
219
219
|
"init.choice.review.plan": "Plan",
|
|
220
220
|
"init.choice.review.task": "Task",
|
|
221
221
|
"init.choice.review.feature": "Feature",
|
|
@@ -241,7 +241,7 @@ var koCli = {
|
|
|
241
241
|
"init.log.workflowLabel": "\uC6CC\uD06C\uD50C\uB85C\uC6B0",
|
|
242
242
|
"init.log.taskAgentLabel": "Task \uAD6C\uD604",
|
|
243
243
|
"init.log.reviewsLabel": "\uAC80\uC218",
|
|
244
|
-
"init.log.maxReviewRoundsLabel": "\uB9AC\uBDF0 \
|
|
244
|
+
"init.log.maxReviewRoundsLabel": "fresh \uB9AC\uBDF0 \uCD5C\uB300 \uC2E4\uD589 \uD69F\uC218",
|
|
245
245
|
"init.log.completionStrategyLabel": "\uB85C\uCEEC \uD1B5\uD569",
|
|
246
246
|
"init.summary.taskAgent.on": "\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8",
|
|
247
247
|
"init.summary.taskAgent.off": "\uBA54\uC778 \uC5D0\uC774\uC804\uD2B8",
|
|
@@ -531,7 +531,7 @@ var enCli = {
|
|
|
531
531
|
"init.choice.taskAgent.on": "On - delegate each task implementation",
|
|
532
532
|
"init.choice.taskAgent.off": "Off - implement tasks in the main agent",
|
|
533
533
|
"init.prompt.reviews": "Select independent review gates:",
|
|
534
|
-
"init.prompt.maxReviewRounds": "Maximum
|
|
534
|
+
"init.prompt.maxReviewRounds": "Maximum fresh review rounds before continuing with residual risks:",
|
|
535
535
|
"init.choice.review.plan": "Plan",
|
|
536
536
|
"init.choice.review.task": "Task",
|
|
537
537
|
"init.choice.review.feature": "Feature",
|
|
@@ -557,7 +557,7 @@ var enCli = {
|
|
|
557
557
|
"init.log.workflowLabel": "Workflow",
|
|
558
558
|
"init.log.taskAgentLabel": "Task implementation",
|
|
559
559
|
"init.log.reviewsLabel": "Reviews",
|
|
560
|
-
"init.log.maxReviewRoundsLabel": "
|
|
560
|
+
"init.log.maxReviewRoundsLabel": "Maximum fresh review rounds",
|
|
561
561
|
"init.log.completionStrategyLabel": "Local integration",
|
|
562
562
|
"init.summary.taskAgent.on": "subagent",
|
|
563
563
|
"init.summary.taskAgent.off": "main agent",
|
|
@@ -1375,13 +1375,13 @@ Before taking the next workflow step:
|
|
|
1375
1375
|
13. When \`workflow-stage --json\` returns \`nextAction.category === "plan_review"\` with \`executor === "subagent"\`, delegate a fresh read-only review of \`spec.md\` and \`plan.md\` using the returned model settings and exact \`specHash\` / \`planHash\`; the main agent records the returned reviewRound, evidence, decision, reviewer metadata, and both hashes, and any later spec/plan content change requires a fresh review
|
|
1376
1376
|
14. When \`workflow-stage --json\` returns \`nextAction.category === "task_execute"\` with \`executor === "subagent"\`, mark exactly the returned \`taskId\` as active and delegate its implementation plus task-scoped verification to a fresh subagent in the returned \`workingDirectory\`, using the returned \`model\`, \`reasoningEffort\`, \`onUnavailable\`, and exact \`workerContract\`; no named execution skill is required
|
|
1377
1377
|
15. The task implementation worker executes directly: it must follow the approved Verification Contract, must not add unplanned durable tests, and must not run \`workflow-stage\`, spawn another subagent, edit lee-spec-kit docs, change task state, commit, request approvals, or perform remote/destructive actions. It may modify project code and run task-scoped checks only. The main agent inspects the result, synchronizes docs and task state, and owns every commit and workflow transition; official hooks block commits while \`task_execute\` is still active
|
|
1378
|
-
16. When \`workflow-stage --json\` returns \`nextAction.category === "task_review"\` with \`executor === "subagent"\`, delegate a fresh read-only review for the returned \`taskId\`, \`baseSha\`, \`targetSha\`, and \`targetTree\`; the main agent records the returned reviewRound and evidence and moves the task from \`REVIEW\` to \`DONE\`
|
|
1378
|
+
16. When \`workflow-stage --json\` returns \`nextAction.category === "task_review"\` with \`executor === "subagent"\`, delegate a fresh read-only review for the returned \`taskId\`, \`baseSha\`, \`targetSha\`, and \`targetTree\`; the main agent records the returned reviewRound and evidence and moves the task from \`REVIEW\` to \`DONE\` after an approve decision or the automatic exhausted-\`changes_requested\` path described below
|
|
1379
1379
|
17. When \`workflow-stage --json\` returns \`nextAction.category === "pre_pr_review"\` and \`nextAction.executor === "subagent"\`, delegate a fresh read-only Feature review using the returned \`model\`, \`reasoningEffort\`, \`onUnavailable\`, reviewRound, and review target metadata; do not select or require a named review skill
|
|
1380
1380
|
18. Review subagents return findings without modifying code; the main agent remediates findings and records the actual reviewer metadata, reviewed scope, evidence, decision, and exact hash/SHA/tree target metadata
|
|
1381
1381
|
19. After delegating to a subagent, wait until it returns a terminal outcome: completed, explicit failure, cancellation, or an approval/user-input request that requires action
|
|
1382
1382
|
20. While the subagent remains running, use repeated bounded waits, preferably longer waits. A bounded wait that returns no update, a lack of status messages, or a lack of file changes means only that the subagent is still pending; none is evidence of failure or stalled work. Read-only review subagents are expected not to modify files
|
|
1383
1383
|
21. Do not interrupt, replace, or abandon a running subagent solely because it has been quiet or has not changed files. Stop it only after an explicit user request, a terminal failure/cancellation, or an unrecoverable runtime status
|
|
1384
|
-
22. workflow.agentReview.maxRounds
|
|
1384
|
+
22. workflow.agentReview.maxRounds is the maximum number of fresh reviews for each Plan/task/Feature gate. A changes_requested decision on the final allowed review is remediated once, but the changed target is not reviewed again; keep remaining findings and the post-review target change as residual risks and automatically complete the gate without asking the user for a review-approval token. For example, maxRounds=1 means review round 1, remediate once, then continue with no round 2; blocked decisions never auto-complete
|
|
1385
1385
|
23. For a local workflow, do not report completion directly after implementation approval; follow the exact returned \`local verify\` / \`local merge\` / \`local cleanup\` commands until \`workflow-stage\` proves verification, integration, and cleanup and returns \`done\`; review-fix and \`feature_remediation\` stages explicitly permit scoped fixes
|
|
1386
1386
|
24. In a \`local-ff\` or \`local-squash\` workflow, keep implementation approval and local merge approval distinct when \`local_merge\` is required: the first accepts the implementation, and the second authorizes the configured integration strategy, post-merge checks, and local cleanup
|
|
1387
1387
|
|
|
@@ -2139,7 +2139,7 @@ function initCommand(program2) {
|
|
|
2139
2139
|
"Review gates: comma-separated plan,task,feature | none"
|
|
2140
2140
|
).option(
|
|
2141
2141
|
"--max-review-rounds <count>",
|
|
2142
|
-
"
|
|
2142
|
+
"Maximum fresh review rounds before continuing with residual risks (default: 1)"
|
|
2143
2143
|
).option(
|
|
2144
2144
|
"--completion-strategy <strategy>",
|
|
2145
2145
|
"Local completion: local-ff | local-squash | none"
|
|
@@ -4558,7 +4558,7 @@ function configCommand(program2) {
|
|
|
4558
4558
|
"Review gates: comma-separated plan,task,feature | none"
|
|
4559
4559
|
).option(
|
|
4560
4560
|
"--max-review-rounds <count>",
|
|
4561
|
-
"
|
|
4561
|
+
"Maximum fresh review rounds before continuing with residual risks"
|
|
4562
4562
|
).option(
|
|
4563
4563
|
"--completion-strategy <strategy>",
|
|
4564
4564
|
"Local completion: local-ff | local-squash | none"
|
|
@@ -8847,8 +8847,8 @@ function resolveMaxReviewRounds(config) {
|
|
|
8847
8847
|
const configured = config.workflow?.agentReview?.maxRounds;
|
|
8848
8848
|
return typeof configured === "number" && Number.isInteger(configured) && configured > 0 ? configured : 1;
|
|
8849
8849
|
}
|
|
8850
|
-
function
|
|
8851
|
-
return decisionOutcome === "changes_requested" && (reviewRound || 1)
|
|
8850
|
+
function reviewRoundLimitReached(config, reviewRound, decisionOutcome) {
|
|
8851
|
+
return decisionOutcome === "changes_requested" && (reviewRound || 1) >= resolveMaxReviewRounds(config);
|
|
8852
8852
|
}
|
|
8853
8853
|
function parseWorkflowTaskLine(line, index = -1) {
|
|
8854
8854
|
const canonical = parseTaskLine(line, index);
|
|
@@ -9573,28 +9573,34 @@ function reviewEvidenceSatisfied(config, feature, scope, evidence) {
|
|
|
9573
9573
|
});
|
|
9574
9574
|
}
|
|
9575
9575
|
function planReviewSatisfied(config, feature, review, target) {
|
|
9576
|
-
|
|
9576
|
+
const reviewLimitReached = reviewRoundLimitReached(
|
|
9577
9577
|
config,
|
|
9578
9578
|
review.reviewRound,
|
|
9579
9579
|
review.decisionOutcome
|
|
9580
|
-
)
|
|
9580
|
+
);
|
|
9581
|
+
const targetMatches = review.reviewedSpecHash === target.specHash && review.reviewedPlanHash === target.planHash;
|
|
9582
|
+
return review.status === "done" && reviewEvidenceSatisfied(config, feature, "plan", review.evidence) && (review.decisionOutcome === "approve" && targetMatches || reviewLimitReached && !targetMatches);
|
|
9581
9583
|
}
|
|
9582
9584
|
function featureReviewSatisfied(config, feature, tasks, target) {
|
|
9583
|
-
|
|
9585
|
+
const reviewLimitReached = reviewRoundLimitReached(
|
|
9584
9586
|
config,
|
|
9585
9587
|
tasks.prePrReviewRound,
|
|
9586
9588
|
tasks.prePrDecisionOutcome
|
|
9587
|
-
)
|
|
9589
|
+
);
|
|
9590
|
+
const targetMatches = !!target && tasks.prePrReviewedHead === target.targetSha && tasks.prePrReviewedTree === target.targetTree;
|
|
9591
|
+
return !!target && tasks.prePrReviewStatus === "done" && reviewEvidenceSatisfied(config, feature, "feature", tasks.prePrEvidence) && !!tasks.prePrDecision && (tasks.prePrDecisionOutcome === "approve" && targetMatches || reviewLimitReached && !targetMatches);
|
|
9588
9592
|
}
|
|
9589
9593
|
function taskReviewEvidenceSatisfied(config, feature, task, target) {
|
|
9590
|
-
|
|
9594
|
+
const reviewLimitReached = reviewRoundLimitReached(
|
|
9591
9595
|
config,
|
|
9592
9596
|
task.reviewRound,
|
|
9593
9597
|
task.reviewDecisionOutcome
|
|
9594
|
-
)
|
|
9598
|
+
);
|
|
9599
|
+
const targetMatches = !!target && task.reviewedHead === target.targetSha && task.reviewedTree === target.targetTree;
|
|
9600
|
+
return !!target && reviewEvidenceSatisfied(config, feature, "task", task.reviewEvidence) && (task.reviewDecisionOutcome === "approve" && targetMatches || reviewLimitReached && !targetMatches);
|
|
9595
9601
|
}
|
|
9596
9602
|
function recordedTaskReviewSatisfied(config, feature, projectGitCwd, task, currentTargetSha) {
|
|
9597
|
-
if (!currentTargetSha || !task.reviewedHead || !task.reviewedTree || task.reviewDecisionOutcome !== "approve" && !
|
|
9603
|
+
if (!currentTargetSha || !task.reviewedHead || !task.reviewedTree || task.reviewDecisionOutcome !== "approve" && !reviewRoundLimitReached(
|
|
9598
9604
|
config,
|
|
9599
9605
|
task.reviewRound,
|
|
9600
9606
|
task.reviewDecisionOutcome
|
|
@@ -9605,7 +9611,7 @@ function recordedTaskReviewSatisfied(config, feature, projectGitCwd, task, curre
|
|
|
9605
9611
|
["rev-parse", `${task.reviewedHead}^{tree}`],
|
|
9606
9612
|
projectGitCwd
|
|
9607
9613
|
) || "";
|
|
9608
|
-
return actualTree === task.reviewedTree && isAncestor(projectGitCwd, task.reviewedHead, currentTargetSha);
|
|
9614
|
+
return actualTree === task.reviewedTree && isAncestor(projectGitCwd, task.reviewedHead, currentTargetSha) && (task.reviewDecisionOutcome === "approve" || task.reviewedHead !== currentTargetSha);
|
|
9609
9615
|
}
|
|
9610
9616
|
function resolvePreviousTaskReviewBase(config, feature, tasks, task) {
|
|
9611
9617
|
const taskIndex = tasks.tasks.indexOf(task);
|
|
@@ -10009,7 +10015,8 @@ function resolvePlanReviewPayload(config, feature, review, target) {
|
|
|
10009
10015
|
const targetMatches = review.reviewedSpecHash === target.specHash && review.reviewedPlanHash === target.planHash;
|
|
10010
10016
|
const maxReviewRounds = resolveMaxReviewRounds(config);
|
|
10011
10017
|
const reviewRound = review.reviewRound || 1;
|
|
10012
|
-
const
|
|
10018
|
+
const requestedReviewRound = !targetMatches && review.decisionOutcome ? reviewRound + 1 : reviewRound;
|
|
10019
|
+
const nextReviewRound = Math.min(requestedReviewRound, maxReviewRounds);
|
|
10013
10020
|
const actionContext = {
|
|
10014
10021
|
reviewScope: "plan",
|
|
10015
10022
|
reviewRound: nextReviewRound,
|
|
@@ -10019,7 +10026,8 @@ function resolvePlanReviewPayload(config, feature, review, target) {
|
|
|
10019
10026
|
docsDirectory: config.docsDir
|
|
10020
10027
|
};
|
|
10021
10028
|
if (review.decisionOutcome === "changes_requested") {
|
|
10022
|
-
if (targetMatches) {
|
|
10029
|
+
if (targetMatches && reviewRound <= maxReviewRounds) {
|
|
10030
|
+
const finalReviewRound = reviewRound >= maxReviewRounds;
|
|
10023
10031
|
return {
|
|
10024
10032
|
status: "ok",
|
|
10025
10033
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10028,7 +10036,7 @@ function resolvePlanReviewPayload(config, feature, review, target) {
|
|
|
10028
10036
|
stage: "plan_review_fix",
|
|
10029
10037
|
nextAction: buildAction(
|
|
10030
10038
|
"plan_review_fix",
|
|
10031
|
-
`Address the independent Plan review findings in spec.md or plan.md, record review round ${reviewRound + 1}, keep implementation blocked, and request a fresh review for the resulting document hashes.`,
|
|
10039
|
+
finalReviewRound ? `Address the independent Plan review findings in spec.md or plan.md, keep review round ${reviewRound}, and do not request another review. The resulting document hash changes will be preserved with the remaining findings as residual risks before automatic Plan promotion.` : `Address the independent Plan review findings in spec.md or plan.md, record review round ${reviewRound + 1}, keep implementation blocked, and request a fresh review for the resulting document hashes.`,
|
|
10032
10040
|
false,
|
|
10033
10041
|
null,
|
|
10034
10042
|
void 0,
|
|
@@ -10040,6 +10048,26 @@ function resolvePlanReviewPayload(config, feature, review, target) {
|
|
|
10040
10048
|
};
|
|
10041
10049
|
}
|
|
10042
10050
|
}
|
|
10051
|
+
if (review.decisionOutcome === "changes_requested" && reviewRound >= maxReviewRounds) {
|
|
10052
|
+
return {
|
|
10053
|
+
status: "ok",
|
|
10054
|
+
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
10055
|
+
docsDir: config.docsDir,
|
|
10056
|
+
featureRef: buildFeatureRef(feature),
|
|
10057
|
+
stage: "plan_review",
|
|
10058
|
+
nextAction: buildAction(
|
|
10059
|
+
"plan_review",
|
|
10060
|
+
`The Plan review round limit was reached at round ${reviewRound}. Do not delegate another review. Repair or record the final-round evidence if needed, preserve the remaining findings and any post-review hash changes as residual risks, and continue through automatic Plan promotion.`,
|
|
10061
|
+
false,
|
|
10062
|
+
null,
|
|
10063
|
+
void 0,
|
|
10064
|
+
{ ...actionContext, reviewRound: maxReviewRounds }
|
|
10065
|
+
),
|
|
10066
|
+
approvalRequired: false,
|
|
10067
|
+
implementationAllowed: false,
|
|
10068
|
+
blockedReasonCode: "PLAN_REVIEW_NOT_APPROVED"
|
|
10069
|
+
};
|
|
10070
|
+
}
|
|
10043
10071
|
if (targetMatches && review.decisionOutcome === "blocked") {
|
|
10044
10072
|
return {
|
|
10045
10073
|
status: "ok",
|
|
@@ -10119,11 +10147,11 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10119
10147
|
specContent || "",
|
|
10120
10148
|
planContent || ""
|
|
10121
10149
|
);
|
|
10122
|
-
const planReviewAutoCompleted = planReview.status === "done" && reviewEvidenceSatisfied(config, feature, "plan", planReview.evidence) &&
|
|
10150
|
+
const planReviewAutoCompleted = planReview.status === "done" && reviewEvidenceSatisfied(config, feature, "plan", planReview.evidence) && reviewRoundLimitReached(
|
|
10123
10151
|
config,
|
|
10124
10152
|
planReview.reviewRound,
|
|
10125
10153
|
planReview.decisionOutcome
|
|
10126
|
-
) && planReview.reviewedSpecHash
|
|
10154
|
+
) && (planReview.reviewedSpecHash !== planReviewTarget.specHash || planReview.reviewedPlanHash !== planReviewTarget.planHash);
|
|
10127
10155
|
const enforcePlanReview = requirements.planReviewEnabled && (tasks.docStatus !== "approved" || planReview.hasMetadata);
|
|
10128
10156
|
const issueDraft = parseWorkflowDraftMetadataExtended(issueContent || "");
|
|
10129
10157
|
const prDraft = parseWorkflowDraftMetadataExtended(prContent || "");
|
|
@@ -10181,7 +10209,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10181
10209
|
stage: "plan",
|
|
10182
10210
|
nextAction: buildAction(
|
|
10183
10211
|
isReviewStage ? "plan_approve" : "plan_write",
|
|
10184
|
-
isReviewStage ? planReviewAutoCompleted ? "The Plan review
|
|
10212
|
+
isReviewStage ? planReviewAutoCompleted ? "The Plan review round limit was reached. Preserve the remaining findings and post-review hash changes as residual risks, promote plan.md from Review to Approved, and continue automatically without asking the user for review approval." : approvalRequired ? "Get user approval and update plan.md status to Approved." : "Promote plan.md from Review to Approved and continue automatically." : "Write or refine plan.md until it is ready for approval.",
|
|
10185
10213
|
approvalRequired
|
|
10186
10214
|
),
|
|
10187
10215
|
approvalRequired,
|
|
@@ -10369,6 +10397,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10369
10397
|
const evidenceMatchesTarget = reviewTask.reviewedHead === reviewTarget.targetSha && reviewTask.reviewedTree === reviewTarget.targetTree;
|
|
10370
10398
|
if (reviewTask.reviewDecisionOutcome === "changes_requested") {
|
|
10371
10399
|
if (evidenceMatchesTarget && reviewContext.reviewRound <= reviewContext.maxReviewRounds) {
|
|
10400
|
+
const finalReviewRound = reviewContext.reviewRound >= reviewContext.maxReviewRounds;
|
|
10372
10401
|
return {
|
|
10373
10402
|
status: "ok",
|
|
10374
10403
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10377,7 +10406,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10377
10406
|
stage: "task_review_fix",
|
|
10378
10407
|
nextAction: buildAction(
|
|
10379
10408
|
"task_review_fix",
|
|
10380
|
-
`Address the independent review findings for ${reviewTask.taskId || reviewTask.title}, return the task to REVIEW, commit the new code tip, and request fresh review round ${reviewContext.reviewRound + 1}.`,
|
|
10409
|
+
finalReviewRound ? `Address the independent review findings for ${reviewTask.taskId || reviewTask.title}, return the task to REVIEW, and commit the new code tip without requesting another review. Preserve any remaining findings and the post-review target change as residual risks before automatic task completion.` : `Address the independent review findings for ${reviewTask.taskId || reviewTask.title}, return the task to REVIEW, commit the new code tip, and request fresh review round ${reviewContext.reviewRound + 1}.`,
|
|
10381
10410
|
false,
|
|
10382
10411
|
null,
|
|
10383
10412
|
void 0,
|
|
@@ -10410,7 +10439,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10410
10439
|
};
|
|
10411
10440
|
}
|
|
10412
10441
|
if (taskReviewEvidenceSatisfied(config, feature, reviewTask, reviewTarget)) {
|
|
10413
|
-
const completionSummary = reviewTask.reviewDecisionOutcome === "approve" ? reviewTask.status === "DONE" ? `The independent review approved ${reviewTask.taskId || reviewTask.title} at the current code tree. Commit the review evidence before continuing.` : `The independent review approved ${reviewTask.taskId || reviewTask.title} at the current code tree. Mark the task DONE and commit the review evidence before starting another task.` : reviewTask.status === "DONE" ? `The review
|
|
10442
|
+
const completionSummary = reviewTask.reviewDecisionOutcome === "approve" ? reviewTask.status === "DONE" ? `The independent review approved ${reviewTask.taskId || reviewTask.title} at the current code tree. Commit the review evidence before continuing.` : `The independent review approved ${reviewTask.taskId || reviewTask.title} at the current code tree. Mark the task DONE and commit the review evidence before starting another task.` : reviewTask.status === "DONE" ? `The review round limit was reached for ${reviewTask.taskId || reviewTask.title}. Keep the remaining findings${evidenceMatchesTarget ? "" : " and any post-review unreviewed target changes"} as residual risks and commit the review evidence before continuing automatically.` : `The review round limit was reached for ${reviewTask.taskId || reviewTask.title}. Keep the remaining findings${evidenceMatchesTarget ? "" : " and any post-review unreviewed target changes"} as residual risks, mark the task DONE, and commit the review evidence before continuing automatically.`;
|
|
10414
10443
|
return {
|
|
10415
10444
|
status: "ok",
|
|
10416
10445
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10430,6 +10459,26 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10430
10459
|
blockedReasonCode: "TASK_REVIEW_NOT_APPROVED"
|
|
10431
10460
|
};
|
|
10432
10461
|
}
|
|
10462
|
+
if (reviewTask.reviewDecisionOutcome === "changes_requested" && reviewContext.reviewRound >= reviewContext.maxReviewRounds) {
|
|
10463
|
+
return {
|
|
10464
|
+
status: "ok",
|
|
10465
|
+
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
10466
|
+
docsDir: config.docsDir,
|
|
10467
|
+
featureRef: buildFeatureRef(feature),
|
|
10468
|
+
stage: "task_review",
|
|
10469
|
+
nextAction: buildAction(
|
|
10470
|
+
"task_review",
|
|
10471
|
+
`The review round limit was reached for ${reviewTask.taskId || reviewTask.title} at round ${reviewContext.reviewRound}. Do not delegate another review. Repair or record the final-round evidence if needed and preserve the remaining findings and any post-review target changes as residual risks before completing the task automatically.`,
|
|
10472
|
+
false,
|
|
10473
|
+
null,
|
|
10474
|
+
void 0,
|
|
10475
|
+
reviewContext
|
|
10476
|
+
),
|
|
10477
|
+
approvalRequired: false,
|
|
10478
|
+
implementationAllowed: false,
|
|
10479
|
+
blockedReasonCode: "TASK_REVIEW_NOT_APPROVED"
|
|
10480
|
+
};
|
|
10481
|
+
}
|
|
10433
10482
|
return {
|
|
10434
10483
|
status: "ok",
|
|
10435
10484
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10438,13 +10487,16 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10438
10487
|
stage: "task_review",
|
|
10439
10488
|
nextAction: buildAction(
|
|
10440
10489
|
"task_review",
|
|
10441
|
-
`Delegate fresh read-only review round ${evidenceMatchesTarget ? reviewContext.reviewRound : reviewTask.reviewDecisionOutcome ? reviewContext.reviewRound + 1 : reviewContext.reviewRound} of ${reviewTask.taskId || reviewTask.title} for ${reviewTarget.baseSha}..${reviewTarget.targetSha}. Record Review Round, evidence, decision, reviewer metadata, Reviewed Head, and Reviewed Tree without modifying code.`,
|
|
10490
|
+
`Delegate fresh read-only review round ${evidenceMatchesTarget ? reviewContext.reviewRound : reviewTask.reviewDecisionOutcome ? Math.min(reviewContext.reviewRound + 1, reviewContext.maxReviewRounds) : reviewContext.reviewRound} of ${reviewTask.taskId || reviewTask.title} for ${reviewTarget.baseSha}..${reviewTarget.targetSha}. Record Review Round, evidence, decision, reviewer metadata, Reviewed Head, and Reviewed Tree without modifying code.`,
|
|
10442
10491
|
false,
|
|
10443
10492
|
null,
|
|
10444
10493
|
resolveAgentReviewer(config, "task"),
|
|
10445
10494
|
{
|
|
10446
10495
|
...reviewContext,
|
|
10447
|
-
reviewRound: !evidenceMatchesTarget && reviewTask.reviewDecisionOutcome ?
|
|
10496
|
+
reviewRound: !evidenceMatchesTarget && reviewTask.reviewDecisionOutcome ? Math.min(
|
|
10497
|
+
reviewContext.reviewRound + 1,
|
|
10498
|
+
reviewContext.maxReviewRounds
|
|
10499
|
+
) : reviewContext.reviewRound
|
|
10448
10500
|
}
|
|
10449
10501
|
),
|
|
10450
10502
|
approvalRequired: false,
|
|
@@ -10554,6 +10606,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10554
10606
|
const evidenceMatchesTarget = !!reviewTarget && tasks.prePrReviewedHead === reviewTarget.targetSha && tasks.prePrReviewedTree === reviewTarget.targetTree;
|
|
10555
10607
|
if (reviewContext && tasks.prePrDecisionOutcome === "changes_requested") {
|
|
10556
10608
|
if (evidenceMatchesTarget && reviewContext.reviewRound <= reviewContext.maxReviewRounds) {
|
|
10609
|
+
const finalReviewRound = reviewContext.reviewRound >= reviewContext.maxReviewRounds;
|
|
10557
10610
|
return {
|
|
10558
10611
|
status: "ok",
|
|
10559
10612
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10562,7 +10615,7 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10562
10615
|
stage: "feature_review_fix",
|
|
10563
10616
|
nextAction: buildAction(
|
|
10564
10617
|
"feature_review_fix",
|
|
10565
|
-
`Address the Feature review findings, commit the remediation, record review round ${reviewContext.reviewRound + 1}, and request a fresh review of the new code tree before implementation approval.`,
|
|
10618
|
+
finalReviewRound ? `Address the Feature review findings and commit the remediation without requesting another review. Preserve any remaining findings and the post-review target change as residual risks before continuing automatically.` : `Address the Feature review findings, commit the remediation, record review round ${reviewContext.reviewRound + 1}, and request a fresh review of the new code tree before implementation approval.`,
|
|
10566
10619
|
false,
|
|
10567
10620
|
null,
|
|
10568
10621
|
void 0,
|
|
@@ -10595,7 +10648,12 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10595
10648
|
};
|
|
10596
10649
|
}
|
|
10597
10650
|
if (!featureReviewSatisfied(config, feature, tasks, reviewTarget)) {
|
|
10598
|
-
const
|
|
10651
|
+
const rawRequestedRound = reviewContext && !evidenceMatchesTarget && tasks.prePrDecisionOutcome ? reviewContext.reviewRound + 1 : reviewContext?.reviewRound;
|
|
10652
|
+
const requestedRound = reviewContext && typeof rawRequestedRound === "number" ? Math.min(
|
|
10653
|
+
rawRequestedRound,
|
|
10654
|
+
reviewContext.maxReviewRounds
|
|
10655
|
+
) : rawRequestedRound;
|
|
10656
|
+
const reviewLimitExhausted = !!reviewContext && tasks.prePrDecisionOutcome === "changes_requested" && reviewContext.reviewRound >= reviewContext.maxReviewRounds;
|
|
10599
10657
|
return {
|
|
10600
10658
|
status: "ok",
|
|
10601
10659
|
reasonCode: "WORKFLOW_STAGE_RESOLVED",
|
|
@@ -10604,10 +10662,10 @@ async function collectWorkflowStage(cwd, selector, component) {
|
|
|
10604
10662
|
stage: "pre_pr_review",
|
|
10605
10663
|
nextAction: buildAction(
|
|
10606
10664
|
"pre_pr_review",
|
|
10607
|
-
reviewTarget ? `Delegate independent read-only Feature review round ${requestedRound} to a fresh subagent for ${reviewTarget.baseSha}..${reviewTarget.targetSha}. Record Review Round, findings, decision, reviewer metadata, Pre-PR Reviewed Head, and Pre-PR Reviewed Tree as evidence.` : "Create a project commit before requesting the independent Feature review.",
|
|
10665
|
+
reviewLimitExhausted ? `The Feature review round limit was reached at round ${reviewContext.reviewRound}. Do not delegate another review. Repair or record the final-round evidence if needed and preserve the remaining findings and any post-review target changes as residual risks before continuing automatically.` : reviewTarget ? `Delegate independent read-only Feature review round ${requestedRound} to a fresh subagent for ${reviewTarget.baseSha}..${reviewTarget.targetSha}. Record Review Round, findings, decision, reviewer metadata, Pre-PR Reviewed Head, and Pre-PR Reviewed Tree as evidence.` : "Create a project commit before requesting the independent Feature review.",
|
|
10608
10666
|
false,
|
|
10609
10667
|
null,
|
|
10610
|
-
reviewTarget ? resolveAgentReviewer(config, "feature") : void 0,
|
|
10668
|
+
reviewTarget && !reviewLimitExhausted ? resolveAgentReviewer(config, "feature") : void 0,
|
|
10611
10669
|
reviewContext ? { ...reviewContext, reviewRound: requestedRound } : void 0
|
|
10612
10670
|
),
|
|
10613
10671
|
approvalRequired: false,
|