@mediadatafusion/pi-workflow-suite 0.0.8 → 0.0.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/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +14 -4
- package/README.md +31 -124
- package/SECURITY.md +6 -2
- package/SUPPORT.md +3 -5
- package/VERSION +1 -1
- package/config/prompts/mission-final-validation.md +3 -2
- package/config/prompts/validate-approved-plan.md +4 -3
- package/extensions/subagent/index.ts +69 -3
- package/extensions/subagent/repolock-guard.ts +111 -0
- package/extensions/subagent/runner.ts +51 -3
- package/extensions/workflow-modes.ts +95 -49
- package/extensions/workflow-parsers.ts +2 -1
- package/extensions/workflow-state.ts +2 -1
- package/extensions/workflow-tool-guard.ts +68 -29
- package/extensions/workflow-validation-classifier.ts +5 -2
- package/package.json +8 -4
- package/scripts/install-to-live.sh +2 -1
- package/scripts/prepare-package-readme.mjs +33 -7
|
@@ -737,7 +737,7 @@ function effectiveStandardModelRole(settings: ReturnType<typeof loadWorkflowSett
|
|
|
737
737
|
|
|
738
738
|
let workflowScheduledAgentTurns = 0;
|
|
739
739
|
|
|
740
|
-
const WORKFLOW_AGENT_TURN_RETRY_DELAYS_MS = [250, 750, 1500] as const;
|
|
740
|
+
const WORKFLOW_AGENT_TURN_RETRY_DELAYS_MS = [250, 750, 1500, 3000, 5000, 8000, 13000] as const;
|
|
741
741
|
|
|
742
742
|
function workflowTurnSendErrorMessage(error: unknown): string {
|
|
743
743
|
return error instanceof Error ? error.message : String(error ?? "");
|
|
@@ -771,14 +771,7 @@ function queueAgentTurn(pi: ExtensionAPI, content: string, customType: string):
|
|
|
771
771
|
}
|
|
772
772
|
|
|
773
773
|
function queueNativeFinalSummary(pi: ExtensionAPI, summary: string, customType: "workflow-plan-final-summary" | "workflow-mission-final-summary"): void {
|
|
774
|
-
|
|
775
|
-
setTimeout(() => {
|
|
776
|
-
try {
|
|
777
|
-
pi.sendMessage({ customType, content: `Print this comprehensive final deliverable summary exactly as written. Do not call tools. Do not shorten it. Do not replace it with a status receipt. Do not add Workflow Suite card wrappers or hidden handoff commentary. Preserve the provided simple section headings exactly so the terminal can render styled headings without showing raw hashtag markers. Use professional presentation only: no emoji, no Markdown tables, no box-art tables, and no dash-bullet lists.\n\n${professionalFinalSummaryMarkdown(summary)}`, display: false }, { triggerTurn: true, deliverAs: "followUp" });
|
|
778
|
-
} finally {
|
|
779
|
-
workflowScheduledAgentTurns = Math.max(0, workflowScheduledAgentTurns - 1);
|
|
780
|
-
}
|
|
781
|
-
}, 0);
|
|
774
|
+
queueAgentTurn(pi, `Print this comprehensive final deliverable summary exactly as written. Do not call tools. Do not shorten it. Do not replace it with a status receipt. Do not add Workflow Suite card wrappers or hidden handoff commentary. Preserve the provided simple section headings exactly so the terminal can render styled headings without showing raw hashtag markers. Use professional presentation only: no emoji, no Markdown tables, no box-art tables, and no dash-bullet lists.\n\n${professionalFinalSummaryMarkdown(summary)}`, customType);
|
|
782
775
|
}
|
|
783
776
|
|
|
784
777
|
function visibleDeferredHandoffFailure(label: string): boolean {
|
|
@@ -1770,7 +1763,14 @@ Approved plan is the execution contract. Do only this plan. No unrelated refacto
|
|
|
1770
1763
|
|
|
1771
1764
|
Before editing, restate the approved plan, list expected files to change, and run the required/appropriate execution sub-agents. Do not edit until forced sub-agent requirements are satisfied. Then implement the plan. After implementation, summarize changed files, sub-agent findings used, and recommend validation.
|
|
1772
1765
|
|
|
1773
|
-
|
|
1766
|
+
Plan progress tracking is mandatory for every numbered approved Plan step:
|
|
1767
|
+
- Before starting a step, call workflow_progress with that step number and status "active".
|
|
1768
|
+
- After completing a step, call workflow_progress with that step number and status "completed".
|
|
1769
|
+
- If a step fails or blocks, call workflow_progress with that step number and status "failed" or "blocked" and stop with the blocker.
|
|
1770
|
+
- For full-plan execution, progress through the approved steps in order so Current and Next advance visibly.
|
|
1771
|
+
- For per-step gated execution, track only the current allowed step and stop after that step.
|
|
1772
|
+
|
|
1773
|
+
Before your final execution summary, call workflow_execution_result with status, completedSteps, changedFiles, commands, blockers, and summary. completedSteps must list every approved Plan step number completed in this execution turn. The typed tool result is the primary handoff control plane. Your final execution summary must be validator-grade evidence, not just prose. Include Acceptance criteria coverage, exact files changed, Commands run with exit status, Checks skipped with reason, remaining manual verification, and any evidence from execution sub-agents.
|
|
1774
1774
|
|
|
1775
1775
|
Execution scope:
|
|
1776
1776
|
- ${stepExecutionGuidance}
|
|
@@ -1841,10 +1841,10 @@ ${requiredSubagentPreflightSection(preflightBlock)}
|
|
|
1841
1841
|
- When validationPolicy is auto, deep, or maximum, validation sub-agents are expected for non-trivial work; prefer quality-validation for independent diff/risk/build-test review.
|
|
1842
1842
|
- ${preflightSatisfied && policy === "forced" ? "Forced validation policy was satisfied by Workflow Suite preflight; do not rerun required workers solely for policy compliance." : "If validationPolicy is forced, required sub-agents must run before verdict, or stop with: Sub-agent policy is forced, but sub-agent execution is unavailable because <reason>."}
|
|
1843
1843
|
- PASS only when the approved plan is fully satisfied with no blocking unresolved risk.
|
|
1844
|
-
-
|
|
1845
|
-
-
|
|
1844
|
+
- FAIL when concrete missing requirements, unexpected changes, regressions, broken checks, unsafe/out-of-scope work, or concrete code/content/citation/source/file/metadata/artifact fixes remain.
|
|
1845
|
+
- PARTIAL PASS is only for manual/visual/browser verification caveats or evidence gaps without a concrete repairable issue.
|
|
1846
1846
|
- Manual visual-verification caveats alone are not repairable failures; classify them as PARTIAL PASS and recommend manual QA/revalidation, not repair.
|
|
1847
|
-
- If
|
|
1847
|
+
- If concrete repairable issues remain in code, content, citations, sources, generated files, indexes, metadata, artifacts, or validation artifacts, mark Concrete Repairable Issue: yes, list them clearly under Missing Requirements or Recommended Next Action, and prefer FAIL over PARTIAL PASS.
|
|
1848
1848
|
- Evidence gaps are not repairable defects unless you identify a concrete missing requirement or artifact. Mark Evidence Gap: yes and Concrete Repairable Issue: no when the correct next action is evidence collection or revalidation rather than repair.
|
|
1849
1849
|
|
|
1850
1850
|
Project rules priority:
|
|
@@ -2755,7 +2755,7 @@ Forced Policy Rule: forced is a hard requirement and uses the Maximum / Forced t
|
|
|
2755
2755
|
}
|
|
2756
2756
|
|
|
2757
2757
|
function renderPermissionSummary(settings: ReturnType<typeof loadWorkflowSettings>): string {
|
|
2758
|
-
const repoLock = settings.safety.repoLockEnabled === true ? "enabled for this project -
|
|
2758
|
+
const repoLock = settings.safety.repoLockEnabled === true ? "enabled for this project - normal file tools, conservative bash path checks, and sub-agents are scoped to the current repository; project .pi is readable for context but protected from normal edits; sub-agent child processes inherit the repository boundary but are not OS-sandboxed" : "disabled for this project - tools may access paths allowed by Pi and the active process permissions";
|
|
2759
2759
|
return `Repo Lock: ${repoLock}
|
|
2760
2760
|
Main Plan Permissions: read-only; bash ${settings.safety.disableBashInPlanMode === false ? "allowed for safe commands" : "blocked"}; edit/write blocked
|
|
2761
2761
|
Main Execution Permissions: read/edit/write/safe bash; destructive bash ${settings.safety.blockDestructiveCommands !== false ? "blocked" : "allowed by setting"}
|
|
@@ -3075,7 +3075,7 @@ Note: Parallel File Edits controls simultaneous file writes only. It must not di
|
|
|
3075
3075
|
|
|
3076
3076
|
## Global Safety
|
|
3077
3077
|
Repo Lock (Project): ${settings.safety.repoLockEnabled === true ? "enabled" : "disabled"}
|
|
3078
|
-
Repo Lock Scope: project override only; toggling writes to the active repo .pi/workflow-settings.json; file tools are limited to the active repo
|
|
3078
|
+
Repo Lock Scope: project override only; toggling writes to the active repo .pi/workflow-settings.json; normal file tools and sub-agents are limited to the active repo, while protected project .pi paths are read-only through normal tools
|
|
3079
3079
|
Disable Bash In Plan Mode: ${settings.safety.disableBashInPlanMode !== false ? "enabled" : "disabled"}
|
|
3080
3080
|
Disable Bash In Validator Mode: ${settings.safety.disableBashInValidatorMode !== false ? "enabled" : "disabled"}
|
|
3081
3081
|
Block Destructive Commands: ${settings.safety.blockDestructiveCommands !== false ? "enabled" : "disabled"}
|
|
@@ -3246,7 +3246,7 @@ function planValidationState(state: WorkflowState): PlanValidationStatus {
|
|
|
3246
3246
|
if (state.planProgress?.validationStatus) return state.planProgress.validationStatus;
|
|
3247
3247
|
if (state.mode === "validating" || state.mode === "revalidating") return "running";
|
|
3248
3248
|
if (state.validationVerdict === "PASS") return "pass";
|
|
3249
|
-
if (state.validationVerdict === "PARTIAL PASS") return "
|
|
3249
|
+
if (state.validationVerdict === "PARTIAL PASS") return "partial pass";
|
|
3250
3250
|
if (state.validationVerdict === "FAIL") return "fail";
|
|
3251
3251
|
if (state.validationVerdict === "UNKNOWN") return "unknown";
|
|
3252
3252
|
return "pending";
|
|
@@ -6788,14 +6788,14 @@ function missionRuntimePrompt(mission: MissionState, settings: ReturnType<typeof
|
|
|
6788
6788
|
const last = mission.checkpoints[mission.checkpoints.length - 1];
|
|
6789
6789
|
const policy = settings.subagents.executionPolicy ?? "auto";
|
|
6790
6790
|
const subagentPolicyBlock = phasePromptPolicyBlock(settings, "Execution", "Mission Execution", preflightBlock);
|
|
6791
|
-
return `You are PI MISSION MODE RUNTIME.\n\n${professionalOutputGuidance("Mission runtime")}\n\nMISSION MODE ACTIVE\n\nMission ID: ${mission.id}\nMission goal:\n${mission.goal}\n\nAutonomy: ${mission.autonomy}\nMission status: ${mission.status}\nCurrent milestone index: ${mission.currentMilestoneIndex}\nCurrent milestone: ${milestone ? `${milestone.id} — ${milestone.title}` : "none"}\nCurrent milestone objective: ${milestone?.objective ?? "none"}\n\nMilestone steps:\n${(milestone?.steps ?? []).map((s) => `- ${s}`).join("\n") || "- none recorded"}\n\nCompleted milestones:\n${completed.map((m) => `- ${m.id}: ${m.title}`).join("\n") || "- none"}\n\nRemaining milestones:\n${remaining.map((m) => `- ${m.id}: ${m.title} (${m.status})`).join("\n") || "- none"}\n\nLast checkpoint: ${last ? `${last.id} at ${last.timestamp} — ${last.summary}` : "none"}\nLast stop reason: ${mission.lastStopReason || "none"}\nLast block reason: ${mission.lastBlockReason || "none"}\nNext action: ${mission.nextAction || missionNextActionText(mission, settings)}\n\nSafety rules:\n- This is Mission Mode, not Plan Mode. Do not produce a generic implementation plan.\n- Execute only the current approved mission milestone.\n- Do not continue to later milestones unless Mission Mode starts them.\n- Stop on unexpected risk, destructive action, secret/auth/session/log/runtime-state edit, deployment, push, database mutation, or out-of-scope work.\n- Never edit auth files, sessions, logs, .env, .factory, .cursor, or mission runtime state.\n- Keep file writes sequential unless settings explicitly allow safe scoped parallel edits.\n- Use execution sub-agents aggressively for speed, read-only inspection, risk discovery, implementation strategy, and validation preparation when consistent with executionPolicy=${policy}; if forced, do not edit until required workers have reported.\n\n${subagentPolicyBlock}${requiredSubagentPreflightSection(preflightBlock)}\n\nDiagram guidance:\n${workflowMermaidGuidance()}\n\nWeb research guidance:\n${workflowRuntimeWebResearchGuidance()}\n\nCheckpoint requirements:\n- Produce a checkpoint-ready summary with files changed, risks, validation needs, and next action.\n- Include validator-grade evidence: Acceptance criteria coverage, Commands run with exit status, Checks skipped with reason, files changed/inspected, and remaining manual verification.\n- Do not edit mission runtime state directly; Mission Mode will checkpoint through the workflow runtime.\n\nValidation requirements:\n${(milestone?.validation ?? []).map((s) => `- ${s}`).join("\n") || "- Produce validation-ready execution summary for the mission validator."}\n\n${missionRunPlan(mission)}`;
|
|
6791
|
+
return `You are PI MISSION MODE RUNTIME.\n\n${professionalOutputGuidance("Mission runtime")}\n\nMISSION MODE ACTIVE\n\nMission ID: ${mission.id}\nMission goal:\n${mission.goal}\n\nAutonomy: ${mission.autonomy}\nMission status: ${mission.status}\nCurrent milestone index: ${mission.currentMilestoneIndex}\nCurrent milestone: ${milestone ? `${milestone.id} — ${milestone.title}` : "none"}\nCurrent milestone objective: ${milestone?.objective ?? "none"}\n\nMilestone steps:\n${(milestone?.steps ?? []).map((s) => `- ${s}`).join("\n") || "- none recorded"}\n\nCompleted milestones:\n${completed.map((m) => `- ${m.id}: ${m.title}`).join("\n") || "- none"}\n\nRemaining milestones:\n${remaining.map((m) => `- ${m.id}: ${m.title} (${m.status})`).join("\n") || "- none"}\n\nLast checkpoint: ${last ? `${last.id} at ${last.timestamp} — ${last.summary}` : "none"}\nLast stop reason: ${mission.lastStopReason || "none"}\nLast block reason: ${mission.lastBlockReason || "none"}\nNext action: ${mission.nextAction || missionNextActionText(mission, settings)}\n\nSafety rules:\n- This is Mission Mode, not Plan Mode. Do not produce a generic implementation plan.\n- Execute only the current approved mission milestone.\n- Do not continue to later milestones unless Mission Mode starts them.\n- Stop on unexpected risk, destructive action, secret/auth/session/log/runtime-state edit, deployment, push, database mutation, or out-of-scope work.\n- Never edit auth files, sessions, logs, .env, .factory, .cursor, or mission runtime state.\n- Keep file writes sequential unless settings explicitly allow safe scoped parallel edits.\n- Use execution sub-agents aggressively for speed, read-only inspection, risk discovery, implementation strategy, and validation preparation when consistent with executionPolicy=${policy}; if forced, do not edit until required workers have reported.\n\n${subagentPolicyBlock}${requiredSubagentPreflightSection(preflightBlock)}\n\nDiagram guidance:\n${workflowMermaidGuidance()}\n\nWeb research guidance:\n${workflowRuntimeWebResearchGuidance()}\n\nCheckpoint requirements:\n- Before your final milestone summary, call mission_milestone_result with milestoneId, status, summary, blockers, and evidence. The typed tool result is the primary Mission execution handoff.\n- Produce a checkpoint-ready summary with files changed, risks, validation needs, and next action.\n- Include validator-grade evidence: Acceptance criteria coverage, Commands run with exit status, Checks skipped with reason, files changed/inspected, and remaining manual verification.\n- Do not edit mission runtime state directly; Mission Mode will checkpoint through the workflow runtime.\n\nValidation requirements:\n${(milestone?.validation ?? []).map((s) => `- ${s}`).join("\n") || "- Produce validation-ready execution summary for the mission validator."}\n\n${missionRunPlan(mission)}`;
|
|
6792
6792
|
}
|
|
6793
6793
|
|
|
6794
6794
|
function missionValidationPrompt(mission: MissionState, settings: ReturnType<typeof loadWorkflowSettings>, executionSummary?: string, preflightBlock?: string): string {
|
|
6795
6795
|
const milestone = mission.milestones[mission.currentMilestoneIndex];
|
|
6796
6796
|
const last = mission.checkpoints[mission.checkpoints.length - 1];
|
|
6797
6797
|
const subagentPolicyBlock = phasePromptPolicyBlock(settings, "Validation", "Mission Validation", preflightBlock);
|
|
6798
|
-
return `You are PI MISSION MODE VALIDATOR.\n\n${professionalOutputGuidance("Mission validation")}\n\nValidate the completed work against the current mission milestone only. Use read-only tools only. Do not edit or write. You may run safe read-only bash evidence commands such as git status, git diff, git log, package-script discovery, and existing typecheck/test/build commands when appropriate and safe. Do not run mutating, install, deploy, push, reset, clean, database, secret, or settings/state commands. You are the independent validator, not the mission executor; do not repair and do not accept executor claims without evidence.\n\nMission ID: ${mission.id}\nMission goal:\n${mission.goal}\n\nAutonomy: ${mission.autonomy}\nMission status: ${mission.status}\nCurrent milestone: ${milestone ? `${milestone.id} — ${milestone.title}` : "none"}\nMilestone objective: ${milestone?.objective ?? "none"}\nValidation retry: ${mission.currentValidationRetry ?? 0} of ${maxValidationRetries(mission, settings)} per milestone\nMission validation repair retries: ${missionValidationRetryCount(mission)} of ${maxMissionValidationRetries(mission, settings)} total\nLast validation failure: ${mission.lastValidationFailure || "none"}\nLast repair attempt: ${mission.lastRepairAttempt || "none"}\n\n${subagentPolicyBlock}${requiredSubagentPreflightSection(preflightBlock)}\n\nMilestone validation requirements:\n${(milestone?.validation ?? []).map((s) => `- ${s}`).join("\n") || "- Validate milestone completion and risks."}\n\nLast checkpoint: ${last ? `${last.id} at ${last.timestamp} — ${last.summary}` : "none"}\nLast stop reason: ${mission.lastStopReason || "none"}\nLast block reason: ${mission.lastBlockReason || "none"}\nNext action: ${mission.nextAction || missionNextActionText(mission, settings)}\n\nExecution summary:\n${executionSummary ?? "(none recorded)"}\n\nDiagram guidance:\n${workflowMermaidGuidance()}\n\nWeb research guidance:\n${workflowRuntimeWebResearchGuidance()}\n\nVerdict guidance:\n- PASS only when the milestone is fully satisfied with no blocking unresolved risk.\n-
|
|
6798
|
+
return `You are PI MISSION MODE VALIDATOR.\n\n${professionalOutputGuidance("Mission validation")}\n\nValidate the completed work against the current mission milestone only. Use read-only tools only. Do not edit or write. You may run safe read-only bash evidence commands such as git status, git diff, git log, package-script discovery, and existing typecheck/test/build commands when appropriate and safe. Do not run mutating, install, deploy, push, reset, clean, database, secret, or settings/state commands. You are the independent validator, not the mission executor; do not repair and do not accept executor claims without evidence.\n\nMission ID: ${mission.id}\nMission goal:\n${mission.goal}\n\nAutonomy: ${mission.autonomy}\nMission status: ${mission.status}\nCurrent milestone: ${milestone ? `${milestone.id} — ${milestone.title}` : "none"}\nMilestone objective: ${milestone?.objective ?? "none"}\nValidation retry: ${mission.currentValidationRetry ?? 0} of ${maxValidationRetries(mission, settings)} per milestone\nMission validation repair retries: ${missionValidationRetryCount(mission)} of ${maxMissionValidationRetries(mission, settings)} total\nLast validation failure: ${mission.lastValidationFailure || "none"}\nLast repair attempt: ${mission.lastRepairAttempt || "none"}\n\n${subagentPolicyBlock}${requiredSubagentPreflightSection(preflightBlock)}\n\nMilestone validation requirements:\n${(milestone?.validation ?? []).map((s) => `- ${s}`).join("\n") || "- Validate milestone completion and risks."}\n\nLast checkpoint: ${last ? `${last.id} at ${last.timestamp} — ${last.summary}` : "none"}\nLast stop reason: ${mission.lastStopReason || "none"}\nLast block reason: ${mission.lastBlockReason || "none"}\nNext action: ${mission.nextAction || missionNextActionText(mission, settings)}\n\nExecution summary:\n${executionSummary ?? "(none recorded)"}\n\nDiagram guidance:\n${workflowMermaidGuidance()}\n\nWeb research guidance:\n${workflowRuntimeWebResearchGuidance()}\n\nVerdict guidance:\n- PASS only when the milestone is fully satisfied with no blocking unresolved risk.\n- FAIL when concrete missing requirements, unexpected changes, regressions, broken checks, unsafe/out-of-scope work, or concrete code/content/citation/source/file/metadata/artifact fixes remain.\n- PARTIAL PASS is only for manual/visual/browser verification caveats or evidence gaps without a concrete repairable issue.\n- Manual visual-verification caveats alone are not repairable failures; recommend manual QA/revalidation instead of repair.\n- If concrete repairable issues remain in code, content, citations, sources, generated files, indexes, metadata, artifacts, or validation artifacts, mark Concrete Repairable Issue: yes, list them clearly under Missing Requirements or Recommended Next Action, and prefer FAIL over PARTIAL PASS.\n- Evidence gaps are not repairable defects unless you identify a concrete missing requirement or artifact. Mark Evidence Gap: yes and Concrete Repairable Issue: no when the correct next action is evidence collection or revalidation rather than repair.\n\nOutput exactly:\n# Validation Report\n## Verdict\nPASS, PARTIAL PASS, or FAIL\n## Reason\n## Mission Coverage\n## Milestone Requirements Reviewed\n## Changed Files Reviewed\n## Commands Run With Exit Status\n## Checks Skipped With Reason\n## Concrete Repairable Issue\nyes/no and short reason\n## Evidence Gap\nyes/no and exact missing evidence\n## Manual Verification Required\nyes/no and exact manual check\n## Missing Requirements\n## Unexpected Changes\n## Regression Risks\n## Test And Build Status\n## Recommended Next Action`;
|
|
6799
6799
|
}
|
|
6800
6800
|
|
|
6801
6801
|
function missionFinalValidationPrompt(mission: MissionState, settings: ReturnType<typeof loadWorkflowSettings>, executionSummary?: string, preflightBlock?: string): string {
|
|
@@ -6838,7 +6838,7 @@ Last checkpoint: ${last ? `${last.id} at ${last.timestamp} — ${last.summary}`
|
|
|
6838
6838
|
Execution summary:
|
|
6839
6839
|
${executionSummary ?? "(none recorded)"}
|
|
6840
6840
|
|
|
6841
|
-
Validate the complete mission goal across all milestones, not just the last milestone. Confirm milestone results compose into the original mission outcome. Surface integration gaps, missing cross-milestone requirements, regressions, and unresolved repair risks. Use PASS only for complete success, PARTIAL PASS for manual/visual/browser verification caveats or evidence gaps without concrete repairable issues
|
|
6841
|
+
Validate the complete mission goal across all milestones, not just the last milestone. Confirm milestone results compose into the original mission outcome. Surface integration gaps, missing cross-milestone requirements, regressions, and unresolved repair risks. Use PASS only for complete success. Use FAIL when concrete code/content/citation/source/file/metadata/artifact fixes, concrete repairable defects, or unsafe/out-of-scope work remain. Use PARTIAL PASS only for manual/visual/browser verification caveats or evidence gaps without concrete repairable issues. Evidence gaps are not repairable defects unless a concrete missing requirement or artifact is identified.
|
|
6842
6842
|
|
|
6843
6843
|
Output exactly:
|
|
6844
6844
|
# Final Mission Validation Report
|
|
@@ -7248,11 +7248,12 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7248
7248
|
const completedSteps = normalizePlanCompletedSteps(params.completedSteps);
|
|
7249
7249
|
const validationAvailable = planValidationModelAvailable(settings);
|
|
7250
7250
|
const validateAfterExecution = planAutoValidationEnabled(settings);
|
|
7251
|
+
const executedStepIndex = typeof state.planExecutionStepIndex === "number" ? state.planExecutionStepIndex : state.planProgress?.currentStepIndex;
|
|
7251
7252
|
const progressEnabled = workflowPlanProgressEnabled(settings);
|
|
7252
7253
|
const progressedPlanProgress = progressEnabled ? planProgressWithCompletedSteps(state, settings, completedSteps) : state.planProgress;
|
|
7253
7254
|
const progressedState = progressedPlanProgress ? { ...state, planProgress: progressedPlanProgress } : state;
|
|
7254
7255
|
if (status !== "completed") {
|
|
7255
|
-
updateState({ mode: state.reviewerReport ? "reviewed" : "plan_approved", executionSummary: summary, planProgress: progressEnabled && progressedPlanProgress ? mergePlanProgress(progressedState, settings, { lifecycleStatus: "blocked", validationStatus: "pending", nextAction: "continue execution" }) : state.planProgress }, ctx);
|
|
7256
|
+
updateState({ mode: state.reviewerReport ? "reviewed" : "plan_approved", executionSummary: summary, planExecutionStepIndex: undefined, planProgress: progressEnabled && progressedPlanProgress ? mergePlanProgress(progressedState, settings, { lifecycleStatus: "blocked", validationStatus: "pending", nextAction: "continue execution" }) : state.planProgress }, ctx);
|
|
7256
7257
|
queuePlanTerminalSummary(ctx, "blocked", "Plan execution stopped", { reason: summary });
|
|
7257
7258
|
showBlockedPlanRecoveryMenu(ctx);
|
|
7258
7259
|
return { ...typedToolAck(), details: { accepted: true, status, validationStarted: false } };
|
|
@@ -7264,7 +7265,7 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7264
7265
|
queueAgentTurn(pi, `The workflow_execution_result handoff was incomplete for Plan progress tracking. Do not redo work. Do not edit, write, patch, or run mutating commands. Submit a corrected workflow_execution_result now. Include status and completedSteps for every approved Plan step that was completed, plus changedFiles, commands, blockers, and summary. Reason: ${reason}`, "workflow-execution-handoff-correction");
|
|
7265
7266
|
return { ...typedToolAck(false), details: { accepted: false, status, validationStarted: false, reason }, isError: true };
|
|
7266
7267
|
}
|
|
7267
|
-
updateState({ mode: "executed", executionSummary: summary, planProgress: progressEnabled ? mergePlanProgress({ ...progressedState, mode: "executed" }, settings, { lifecycleStatus: "executing", nextAction: validationAvailable && validateAfterExecution ? "validator" : "finish workflow" }) : progressedPlanProgress }, ctx);
|
|
7268
|
+
updateState({ mode: "executed", executionSummary: summary, planStepValidationIndex: settings.workflow.validateAfterEachStep === true ? executedStepIndex : state.planStepValidationIndex, planExecutionStepIndex: undefined, planProgress: progressEnabled ? mergePlanProgress({ ...progressedState, mode: "executed" }, settings, { lifecycleStatus: "executing", nextAction: validationAvailable && validateAfterExecution ? "validator" : "finish workflow" }) : progressedPlanProgress }, ctx);
|
|
7268
7269
|
if (validationAvailable && validateAfterExecution) deferWorkflowAction(pi, "begin validation after typed execution", async () => { await beginValidation(ctx, true); });
|
|
7269
7270
|
else if (!planValidationGateActive(settings)) deferWorkflowAction(pi, "complete plan after typed execution without validation", () => completePlanWithoutValidation(ctx, validationAvailable ? "Validation skipped: validation is disabled by workflow settings." : "Validation skipped: validator disabled or not configured."));
|
|
7270
7271
|
else deferWorkflowAction(pi, "show post execution menu after typed execution", () => showPostExecutionMenu(ctx, validationAvailable));
|
|
@@ -7302,6 +7303,17 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7302
7303
|
if (typeof params.concreteRepairableIssue === "boolean") lines.push(`Concrete Repairable Issue: ${params.concreteRepairableIssue ? "yes" : "no"}`);
|
|
7303
7304
|
if (typeof params.evidenceGap === "boolean") lines.push(`Evidence Gap: ${params.evidenceGap ? "yes" : "no"}`);
|
|
7304
7305
|
if (typeof params.manualVerificationRequired === "boolean") lines.push(`Manual Verification Required: ${params.manualVerificationRequired ? "yes" : "no"}`);
|
|
7306
|
+
const issues = Array.isArray(params.issues) ? params.issues : [];
|
|
7307
|
+
if (issues.length > 0) {
|
|
7308
|
+
lines.push("Issues:");
|
|
7309
|
+
for (const issue of issues) {
|
|
7310
|
+
const item = issue && typeof issue === "object" ? issue as Record<string, unknown> : {};
|
|
7311
|
+
const title = typeof item.title === "string" && item.title.trim() ? item.title.trim() : "Validation issue";
|
|
7312
|
+
const detail = typeof item.detail === "string" && item.detail.trim() ? ` — ${item.detail.trim()}` : "";
|
|
7313
|
+
const repairable = typeof item.repairable === "boolean" ? ` Repairable: ${item.repairable ? "yes" : "no"}.` : "";
|
|
7314
|
+
lines.push(`- ${title}${detail}${repairable}`);
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7305
7317
|
return lines.join("\n");
|
|
7306
7318
|
};
|
|
7307
7319
|
|
|
@@ -7514,6 +7526,12 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7514
7526
|
}
|
|
7515
7527
|
if (state.mode === "validating" || state.mode === "revalidating" || state.mode === "repairing") {
|
|
7516
7528
|
const settings = loadWorkflowSettings(ctx.cwd);
|
|
7529
|
+
if (state.mode === "repairing" && repairTextIndicatesRevalidationReady(stoppedText)) {
|
|
7530
|
+
updateState({ mode: "revalidating", executionSummary: `${state.executionSummary ?? ""}\n\nRepair summary:\n${stoppedText}`.trim(), lastRepairStatus: "completed", lastRepairAttempt: compact(stoppedText, 1200), repairHistory: appendWorkflowRepairHistory({ timestamp: new Date().toISOString(), retry: state.currentValidationRetry ?? 0, status: "completed", validationFailure: compact(state.lastValidationFailure ?? state.validationReport ?? "", 800), repairSummary: compact(stoppedText, 800), nextAction: "Revalidate interrupted completed repair." }), planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "revalidating", lastRepairStatus: "completed" }, settings, { lifecycleStatus: "revalidating", validationStatus: "running", repairStatus: "completed", nextAction: "validation result" }, state.approvedPlan) : state.planProgress }, ctx);
|
|
7531
|
+
deferWorkflowAction(pi, "begin revalidation after interrupted plan repair", async () => { await beginValidation(ctx, true, true); });
|
|
7532
|
+
recordWorkflowInternalEvent(ctx, "Plan repair interruption recovered as completed repair pending revalidation.");
|
|
7533
|
+
return true;
|
|
7534
|
+
}
|
|
7517
7535
|
updateState({ mode: "plan_approved", validationReport: state.mode === "validating" || state.mode === "revalidating" ? stoppedText : state.validationReport, lastRepairAttempt: state.mode === "repairing" ? stoppedText : state.lastRepairAttempt, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "plan_approved" }, settings, { lifecycleStatus: "approved", nextAction: "rerun stopped gate" }, state.approvedPlan) : state.planProgress }, ctx);
|
|
7518
7536
|
queuePlanTerminalSummary(ctx, "blocked", "Plan workflow gate stopped", { validationText: state.mode === "validating" || state.mode === "revalidating" ? stoppedText : state.validationReport, reason: "Workflow gate stopped before completion." });
|
|
7519
7537
|
recordWorkflowInternalEvent(ctx, "Workflow gate stopped before completion.");
|
|
@@ -7599,6 +7617,23 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7599
7617
|
updateState({ mode: "mission_blocked", activeMissionId: blocked.id, task: blocked.goal, originalTask: blocked.goal, draftPlan: stoppedText }, ctx);
|
|
7600
7618
|
return true;
|
|
7601
7619
|
}
|
|
7620
|
+
if (state.mode === "mission_repairing" && repairTextIndicatesRevalidationReady(stoppedText)) {
|
|
7621
|
+
const repaired = saveActiveMission({
|
|
7622
|
+
...mission,
|
|
7623
|
+
status: "revalidating",
|
|
7624
|
+
lastStopReason: "",
|
|
7625
|
+
lastRepairStatus: "completed",
|
|
7626
|
+
lastRepairAttempt: compact(stoppedText, 1200),
|
|
7627
|
+
nextAction: `Revalidate ${milestone?.id ?? "current milestone"} after interrupted completed repair.`,
|
|
7628
|
+
lastSummary: `Mission repair completed for ${milestone?.id ?? "current milestone"}; interrupted output recovered for revalidation.`,
|
|
7629
|
+
repairHistory: appendRepairHistory(mission, { timestamp: new Date().toISOString(), milestoneId: milestone?.id, retry: mission.currentValidationRetry ?? 0, status: "completed", repairSummary: compact(stoppedText, 800), nextAction: "Revalidate interrupted completed repair." }),
|
|
7630
|
+
});
|
|
7631
|
+
checkpointMission(repaired, `Mission repair interruption recovered as completed for ${milestone?.id ?? "current milestone"}. ${compact(stoppedText, 500)}`, "Revalidation started after interrupted repair.", milestone?.id, { validationResult: repaired.lastValidationResult });
|
|
7632
|
+
updateState({ mode: "mission_revalidating", activeMissionId: repaired.id, task: repaired.goal, originalTask: repaired.goal, executionSummary: stoppedText }, ctx);
|
|
7633
|
+
deferWorkflowAction(pi, "begin mission revalidation after interrupted repair", () => beginMissionValidation(ctx, true, true));
|
|
7634
|
+
recordWorkflowInternalEvent(ctx, "Mission repair interruption recovered as completed repair pending revalidation.");
|
|
7635
|
+
return true;
|
|
7636
|
+
}
|
|
7602
7637
|
const paused = saveActiveMission({
|
|
7603
7638
|
...mission,
|
|
7604
7639
|
status: "paused",
|
|
@@ -7647,6 +7682,12 @@ export default function workflowModes(pi: ExtensionAPI): void {
|
|
|
7647
7682
|
if (i === index) return { ...step, status };
|
|
7648
7683
|
return step;
|
|
7649
7684
|
});
|
|
7685
|
+
if (status === "completed" || status === "skipped") {
|
|
7686
|
+
const nextOpen = steps.findIndex((step, i) => i > index && step.status === "pending");
|
|
7687
|
+
if (nextOpen >= 0 && settings.workflow.validateAfterEachStep !== true && settings.workflow.requireApprovalPerStep !== true) {
|
|
7688
|
+
steps[nextOpen] = { ...steps[nextOpen], status: "active" as PlanStepStatus };
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7650
7691
|
updateState({ planProgress: { ...progress, steps, currentStepIndex: currentStepIndexForSteps(steps, index), nextAction: planNextActionText(state) } }, ctx);
|
|
7651
7692
|
return true;
|
|
7652
7693
|
};
|
|
@@ -8265,6 +8306,7 @@ ${reportExcerpt(validation, 2400)}
|
|
|
8265
8306
|
repairHistory: undefined,
|
|
8266
8307
|
lastRepairStatus: "none",
|
|
8267
8308
|
planStepValidationIndex: undefined,
|
|
8309
|
+
planExecutionStepIndex: undefined,
|
|
8268
8310
|
planRuntime: undefined,
|
|
8269
8311
|
planProgress: undefined,
|
|
8270
8312
|
lastCompletedPlanSummary: completedPlanSummary,
|
|
@@ -8551,6 +8593,12 @@ ${renderMissionProgress(mission, settings)}
|
|
|
8551
8593
|
return;
|
|
8552
8594
|
}
|
|
8553
8595
|
if (mission.status === "paused" || mission.status === "stopped") {
|
|
8596
|
+
if (missionRepairCompletedPendingRevalidation(mission)) {
|
|
8597
|
+
const revalidating = saveActiveMission({ ...mission, status: "revalidating", lastRepairStatus: mission.lastRepairStatus === "blocked" ? "completed" : (mission.lastRepairStatus ?? "completed"), lastStopReason: "", nextAction: `Revalidate ${mission.milestones[mission.currentMilestoneIndex]?.id ?? "current milestone"} after completed repair.`, lastSummary: "Mission resumed to revalidation after completed repair." });
|
|
8598
|
+
checkpointMission(revalidating, "Mission resume detected completed repair pending revalidation.", "Resume current mission revalidation gate.", mission.milestones[mission.currentMilestoneIndex]?.id, { validationResult: revalidating.lastValidationResult });
|
|
8599
|
+
await beginMissionValidation(ctx, true, true);
|
|
8600
|
+
return;
|
|
8601
|
+
}
|
|
8554
8602
|
const approved = saveActiveMission({ ...mission, status: "approved", lastStopReason: "", nextAction: "Run /mission continue to proceed.", lastSummary: "Mission resumed by user." });
|
|
8555
8603
|
const checkpointed = checkpointMission(approved, "Mission resume requested from resume menu.", "Resume current mission milestone in Mission Mode.");
|
|
8556
8604
|
await beginMissionRun(ctx, checkpointed, "resume");
|
|
@@ -8965,9 +9013,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
8965
9013
|
return;
|
|
8966
9014
|
}
|
|
8967
9015
|
updateState({ modelsUsed: { ...(state.modelsUsed ?? {}), executor: modelLabel(route), planner: running.modelsUsed?.planner } }, ctx);
|
|
8968
|
-
|
|
8969
|
-
queueAgentTurn(pi, `Execute current mission milestone ${milestone.id}: ${milestone.title}.`, "mission-run-trigger");
|
|
8970
|
-
} else queueWorkflowPrompt(pi, missionRuntimePrompt(running, settings, phasePreflightBlocks.Execution));
|
|
9016
|
+
queueAgentTurn(pi, missionRuntimePrompt(running, settings, phasePreflightBlocks.Execution), auto ? "mission-run-trigger" : "mission-run-manual-trigger");
|
|
8971
9017
|
}
|
|
8972
9018
|
|
|
8973
9019
|
async function handleMissionApprovalHandoff(ctx: ExtensionContext, mission: MissionState, source: "menu" | "command" = "command") {
|
|
@@ -9157,7 +9203,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9157
9203
|
const forcedSubagentTaskText = (phase: SubagentPhase, agent: string, index: number, required: number, context: ForcedSubagentPreflightContext): string => {
|
|
9158
9204
|
const label = context.label ?? phase;
|
|
9159
9205
|
const mission = context.mission;
|
|
9160
|
-
const repoLock = loadWorkflowSettings().safety.repoLockEnabled === true ? " Repo Lock is enabled: keep project file discovery and commands inside the current repository.
|
|
9206
|
+
const repoLock = loadWorkflowSettings().safety.repoLockEnabled === true ? " Repo Lock is enabled: keep project file discovery and commands inside the current repository. Project .pi files may be read for context, but do not edit protected .pi control/config paths through normal tools. Do not inspect sibling repositories, unrelated home-directory paths, or the live Pi runtime." : "";
|
|
9161
9207
|
const base = `Forced ${label} sub-agent preflight worker ${index + 1}/${required}. You are running before the main ${label} agent. Stay read-only unless your agent contract explicitly allows otherwise; do not edit, commit, push, deploy, mutate databases, or touch secrets/runtime state.${repoLock} Return concise findings for the main ${label} agent.`;
|
|
9162
9208
|
if (phase === "Planning") return `${base}\n\nPlanning target:\n${context.task ?? mission?.goal ?? state.task ?? state.originalTask ?? "(not recorded)"}\n\n${mission ? `Mission ID: ${mission.id}\nAutonomy: ${mission.autonomy}\nExisting milestones: ${mission.milestones.length}\n` : ""}\nFocus for ${agent}: identify project rules, likely files/systems, ambiguity, risks, validation needs, off-limits files, and specific recommendations for the final plan.`;
|
|
9163
9209
|
if (phase === "Execution") return `${base}\n\nApproved execution contract:\n${context.approvedPlan ?? state.approvedPlan ?? (mission ? missionRunPlan(mission) : "(not recorded)")}\n\nFocus for ${agent}: inspect likely files, identify implementation hazards, propose safe edit order, regression risks, and validation checks. Do not perform the implementation.`;
|
|
@@ -9466,6 +9512,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9466
9512
|
repairHistory: undefined,
|
|
9467
9513
|
lastRepairStatus: "none",
|
|
9468
9514
|
planStepValidationIndex: undefined,
|
|
9515
|
+
planExecutionStepIndex: undefined,
|
|
9469
9516
|
planRuntime: undefined,
|
|
9470
9517
|
planProgress: undefined,
|
|
9471
9518
|
planHistoryId: undefined,
|
|
@@ -9512,6 +9559,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9512
9559
|
repairHistory: undefined,
|
|
9513
9560
|
lastRepairStatus: "none",
|
|
9514
9561
|
planStepValidationIndex: undefined,
|
|
9562
|
+
planExecutionStepIndex: undefined,
|
|
9515
9563
|
planProgress: workflowPlanProgressEnabled(settings) ? createPlanProgress(planText, settings, "approved") : undefined,
|
|
9516
9564
|
}, ctx);
|
|
9517
9565
|
persistCurrentPlan(ctx, "approved", saveReason);
|
|
@@ -9543,7 +9591,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9543
9591
|
}, ctx);
|
|
9544
9592
|
if (!options.planningPreflightSatisfied && !beginForcedSubagentPhase(ctx, "Planning", settings)) {
|
|
9545
9593
|
pi.setActiveTools(planToolsFor(settings));
|
|
9546
|
-
updateState({ mode: "plan_draft", activePlanId, draftPlan: "Planning blocked before the planner could run: forced planning sub-agent requirements are unavailable.", approvedPlan: undefined, lastReviewFailure: "Forced planning sub-agent requirements are unavailable.", executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, lastRepairStatus: "none", planStepValidationIndex: undefined, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "validated", draftPlan: undefined, approvedPlan: undefined }, settings, { lifecycleStatus: "blocked", nextAction: "fix planning sub-agent policy or revise planning", steps: [] }, undefined) : undefined, lastCompletedPlanSummary: undefined }, ctx);
|
|
9594
|
+
updateState({ mode: "plan_draft", activePlanId, draftPlan: "Planning blocked before the planner could run: forced planning sub-agent requirements are unavailable.", approvedPlan: undefined, lastReviewFailure: "Forced planning sub-agent requirements are unavailable.", executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, lastRepairStatus: "none", planStepValidationIndex: undefined, planExecutionStepIndex: undefined, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "validated", draftPlan: undefined, approvedPlan: undefined }, settings, { lifecycleStatus: "blocked", nextAction: "fix planning sub-agent policy or revise planning", steps: [] }, undefined) : undefined, lastCompletedPlanSummary: undefined }, ctx);
|
|
9547
9595
|
return;
|
|
9548
9596
|
}
|
|
9549
9597
|
const route = await applyModelForRole(pi, ctx, "planner", { cwd: ctx.cwd });
|
|
@@ -9616,7 +9664,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9616
9664
|
const steps = settings.workflow.validateAfterEachStep === true || settings.workflow.requireApprovalPerStep === true
|
|
9617
9665
|
? baseSteps.map((step, index) => index === activeIndex ? { ...step, status: "active" as PlanStepStatus } : step.status === "active" ? { ...step, status: "pending" as PlanStepStatus } : step)
|
|
9618
9666
|
: baseSteps;
|
|
9619
|
-
updateState({ mode: "executing", planProgress: { ...currentProgress, steps, currentStepIndex: currentStepIndexForSteps(steps, activeIndex) } }, ctx);
|
|
9667
|
+
updateState({ mode: "executing", planExecutionStepIndex: activeIndex, planProgress: { ...currentProgress, steps, currentStepIndex: currentStepIndexForSteps(steps, activeIndex) } }, ctx);
|
|
9620
9668
|
if (!beginForcedSubagentPhase(ctx, "Execution", settings)) {
|
|
9621
9669
|
const reason = "Execution blocked by forced sub-agent policy availability gate.";
|
|
9622
9670
|
updateState({ mode: previousMode === "reviewed" ? "reviewed" : "plan_approved", planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: previousMode === "reviewed" ? "reviewed" : "plan_approved" }, settings, { lifecycleStatus: "blocked", nextAction: "fix execution policy blocker" }, state.approvedPlan) : state.planProgress }, ctx);
|
|
@@ -9630,11 +9678,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9630
9678
|
return false;
|
|
9631
9679
|
}
|
|
9632
9680
|
updateState({ modelsUsed: { ...(state.modelsUsed ?? {}), executor: modelLabel(route) } }, ctx);
|
|
9633
|
-
|
|
9634
|
-
queueAgentTurn(pi, "Execute the approved plan. Follow each step precisely.", "workflow-execute-trigger");
|
|
9635
|
-
} else {
|
|
9636
|
-
queueWorkflowPrompt(pi, executePrompt(state, settings, phasePreflightBlocks.Execution));
|
|
9637
|
-
}
|
|
9681
|
+
queueAgentTurn(pi, executePrompt(state, settings, phasePreflightBlocks.Execution), auto ? "workflow-execute-trigger" : "workflow-execute-manual-trigger");
|
|
9638
9682
|
return true;
|
|
9639
9683
|
}
|
|
9640
9684
|
|
|
@@ -9959,6 +10003,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
9959
10003
|
currentValidationRetry: 0,
|
|
9960
10004
|
workflowValidationRetryCount: 0,
|
|
9961
10005
|
planStepValidationIndex: undefined,
|
|
10006
|
+
planExecutionStepIndex: undefined,
|
|
9962
10007
|
planRuntime: undefined,
|
|
9963
10008
|
planProgress: workflowPlanProgressEnabled(settings) ? createPlanProgress(plan.finalPlan, settings, "approved") : undefined,
|
|
9964
10009
|
}, ctx);
|
|
@@ -10045,7 +10090,7 @@ Use /mission status, /mission resume, /mission continue, or /mission retry.`);
|
|
|
10045
10090
|
|
|
10046
10091
|
const reusePlan = async (plan: SavedWorkflowPlan, amend: boolean): Promise<void> => {
|
|
10047
10092
|
activeMission = undefined;
|
|
10048
|
-
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task: plan.originalTask ?? "Reuse saved plan", originalTask: plan.originalTask ?? "Reuse saved plan", draftPlan: undefined, approvedPlan: undefined, validationReport: undefined, validationVerdict: undefined, executionSummary: undefined, reviewerReport: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, planHistoryId: undefined, approvedPlanHistoryId: undefined, planStepValidationIndex: undefined, planRuntime: undefined, planProgress: undefined }, ctx);
|
|
10093
|
+
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task: plan.originalTask ?? "Reuse saved plan", originalTask: plan.originalTask ?? "Reuse saved plan", draftPlan: undefined, approvedPlan: undefined, validationReport: undefined, validationVerdict: undefined, executionSummary: undefined, reviewerReport: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, planHistoryId: undefined, approvedPlanHistoryId: undefined, planStepValidationIndex: undefined, planExecutionStepIndex: undefined, planRuntime: undefined, planProgress: undefined }, ctx);
|
|
10049
10094
|
await beginPlanning(ctx, plan.originalTask ?? "Reuse saved plan", plan.finalPlan, amend ? "Amend this saved plan into a new approval-ready plan. Keep what still applies and update what should change." : "Reuse this saved plan as the starting point for a new approval-ready plan.");
|
|
10050
10095
|
};
|
|
10051
10096
|
|
|
@@ -10490,16 +10535,6 @@ ${renderMissionStatus(activeMission ?? paused)}`);
|
|
|
10490
10535
|
showBlockedMissionRecoveryMenu(ctx);
|
|
10491
10536
|
return;
|
|
10492
10537
|
}
|
|
10493
|
-
if (verdict === "PARTIAL PASS" && failureClass === "ambiguous" && !simplePresetActive(settings)) {
|
|
10494
|
-
const reason = "Mission validation returned PARTIAL PASS with incomplete evidence but no concrete repairable issue. Automatic repair is not appropriate for this preset.";
|
|
10495
|
-
const blocked = saveActiveMission({ ...failed, status: "blocked", lastRepairStatus: "none", lastBlockReason: reason, nextAction: "Clarify the validation gap, perform manual verification, or run /mission revalidate.", lastSummary: "Mission blocked pending clearer partial-pass validation evidence." });
|
|
10496
|
-
checkpointMission(blocked, `Mission validation PARTIAL PASS needs clearer evidence for ${milestone?.id ?? "current milestone"}. Reason: ${reason}`, blocked.nextAction ?? "Clarify validation evidence or revalidate.", milestone?.id, { validationResult: verdict });
|
|
10497
|
-
updateState({ mode: "mission_blocked", activeMissionId: blocked.id, validationReport: validationText, validationVerdict: verdict, lastMissionStopSummary: buildMissionStopSummary(ctx, blocked, "blocked", "Mission blocked", { validationText, verdict, reason }) }, ctx);
|
|
10498
|
-
queueMissionTerminalSummary(ctx, blocked, "blocked", "Mission blocked", { validationText, verdict, reason });
|
|
10499
|
-
recordWorkflowInternalEvent(ctx, "Mission validation evidence blocker suppressed from transcript.");
|
|
10500
|
-
showBlockedMissionRecoveryMenu(ctx);
|
|
10501
|
-
return;
|
|
10502
|
-
}
|
|
10503
10538
|
const unsafe = validationFailureRequiresApproval(failure, settings);
|
|
10504
10539
|
const shouldBlock = mission.autonomy === "manual"
|
|
10505
10540
|
|| settings.missions.autoRepairValidationFailures === false
|
|
@@ -13804,7 +13839,7 @@ Public workflow commands:
|
|
|
13804
13839
|
const archivedId = archiveCurrentPlanIfPresent(ctx, reason);
|
|
13805
13840
|
const settings = loadWorkflowSettings(ctx.cwd);
|
|
13806
13841
|
pi.setActiveTools(planToolsFor(settings));
|
|
13807
|
-
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task: undefined, originalTask: undefined, draftPlan: undefined, approvedPlan: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewFailure: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, maxValidationRetriesPerPlan: undefined, maxValidationRetriesPerWorkflow: undefined, lastValidationFailure: undefined, lastRepairAttempt: undefined, repairHistory: undefined, lastRepairStatus: "none", planStepValidationIndex: undefined, planRuntime: undefined, planProgress: undefined, planHistoryId: archivedId ?? state.planHistoryId, approvedPlanHistoryId: undefined, modelsUsed: undefined }, ctx);
|
|
13842
|
+
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task: undefined, originalTask: undefined, draftPlan: undefined, approvedPlan: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewFailure: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, maxValidationRetriesPerPlan: undefined, maxValidationRetriesPerWorkflow: undefined, lastValidationFailure: undefined, lastRepairAttempt: undefined, repairHistory: undefined, lastRepairStatus: "none", planStepValidationIndex: undefined, planExecutionStepIndex: undefined, planRuntime: undefined, planProgress: undefined, planHistoryId: archivedId ?? state.planHistoryId, approvedPlanHistoryId: undefined, modelsUsed: undefined }, ctx);
|
|
13808
13843
|
setWorkflowUi(ctx, state, activeSubagents);
|
|
13809
13844
|
show(pi, `# Plan Archived\n\n${archivedId ? `Archived Plan ID: ${archivedId}\n\n` : ""}Plan Mode is ready for a new planning request.`);
|
|
13810
13845
|
}
|
|
@@ -13812,7 +13847,7 @@ Public workflow commands:
|
|
|
13812
13847
|
async function startFreshPlanFromInput(ctx: ExtensionContext, task: string) {
|
|
13813
13848
|
const archivedId = archiveCurrentPlanIfPresent(ctx, "previous plan archived before starting a new plan");
|
|
13814
13849
|
activeMission = undefined;
|
|
13815
|
-
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task, originalTask: task, draftPlan: undefined, approvedPlan: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, planningDepth: undefined, clarificationMode: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewFailure: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, maxValidationRetriesPerPlan: undefined, maxValidationRetriesPerWorkflow: undefined, lastValidationFailure: undefined, lastRepairAttempt: undefined, repairHistory: undefined, lastRepairStatus: "none", planStepValidationIndex: undefined, planRuntime: undefined, planProgress: undefined, lastCompletedPlanSummary: state.lastCompletedPlanSummary, modelsUsed: undefined, planHistoryId: undefined, approvedPlanHistoryId: undefined }, ctx);
|
|
13850
|
+
updateState({ mode: "awaiting_plan_input", activeMissionId: undefined, task, originalTask: task, draftPlan: undefined, approvedPlan: undefined, clarifyingQuestions: undefined, clarifyingAnswers: undefined, clarificationAlreadyAsked: undefined, clarificationRequiredBeforePlan: undefined, clarificationRequirementReason: undefined, clarificationSkipReason: undefined, clarificationQualityRetryCount: undefined, planningDepth: undefined, clarificationMode: undefined, reviewerReport: undefined, reviewerVerdict: undefined, currentReviewRetry: 0, workflowReviewRetryCount: 0, maxReviewRetriesPerPlan: undefined, maxReviewRetriesPerWorkflow: undefined, lastReviewFailure: undefined, lastReviewAttempt: undefined, lastReviewRepairStatus: "none", reviewHistory: undefined, reviewRepairInProgress: undefined, repairRetryState: undefined, executionSummary: undefined, validationReport: undefined, validationVerdict: undefined, currentValidationRetry: 0, workflowValidationRetryCount: 0, maxValidationRetriesPerPlan: undefined, maxValidationRetriesPerWorkflow: undefined, lastValidationFailure: undefined, lastRepairAttempt: undefined, repairHistory: undefined, lastRepairStatus: "none", planStepValidationIndex: undefined, planExecutionStepIndex: undefined, planRuntime: undefined, planProgress: undefined, lastCompletedPlanSummary: state.lastCompletedPlanSummary, modelsUsed: undefined, planHistoryId: undefined, approvedPlanHistoryId: undefined }, ctx);
|
|
13816
13851
|
if (archivedId) recordWorkflowInternalEvent(ctx, `Previous plan archived: ${archivedId}`);
|
|
13817
13852
|
await beginPlanning(ctx, task);
|
|
13818
13853
|
}
|
|
@@ -14571,14 +14606,24 @@ Public workflow commands:
|
|
|
14571
14606
|
applyPlanProgressMarkers(ctx, text);
|
|
14572
14607
|
const allTrackedStepsCompleted = planProgressAllStepsCompleted(state);
|
|
14573
14608
|
if (!allTrackedStepsCompleted && blockIfForcedSubagentsMissing(ctx, "Execution")) {
|
|
14574
|
-
|
|
14609
|
+
const reason = "Execution stopped before all approved Plan steps were tracked and the forced execution worker requirement was not satisfied.";
|
|
14610
|
+
updateState({ mode: "validated", executionSummary: text, validationReport: reason, validationVerdict: "UNKNOWN", planExecutionStepIndex: undefined, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "validated", validationVerdict: "UNKNOWN" }, settings, { lifecycleStatus: "blocked", validationStatus: "unknown", nextAction: "fix execution progress/sub-agent blocker then /plan continue" }) : state.planProgress }, ctx);
|
|
14611
|
+
queuePlanTerminalSummary(ctx, "blocked", "Plan execution blocked", { validationText: reason, verdict: "UNKNOWN", reason });
|
|
14612
|
+
return;
|
|
14613
|
+
}
|
|
14614
|
+
if (!allTrackedStepsCompleted && settings.workflow.validateAfterEachStep !== true && settings.workflow.requireApprovalPerStep !== true && workflowPlanProgressEnabled(settings) && state.planProgress?.steps.length) {
|
|
14615
|
+
const reason = "Execution summary ended without workflow_progress markers or workflow_execution_result completedSteps for every approved Plan step.";
|
|
14616
|
+
updateState({ mode: "validated", executionSummary: text, validationReport: reason, validationVerdict: "UNKNOWN", planExecutionStepIndex: undefined, planProgress: mergePlanProgress({ ...state, mode: "validated", validationVerdict: "UNKNOWN" }, settings, { lifecycleStatus: "blocked", validationStatus: "unknown", nextAction: "submit corrected workflow_execution_result or rerun /plan continue" }) }, ctx);
|
|
14617
|
+
queuePlanTerminalSummary(ctx, "blocked", "Plan execution progress missing", { validationText: reason, verdict: "UNKNOWN", reason });
|
|
14618
|
+
queueAgentTurn(pi, `The Plan execution summary was not accepted because approved Plan step progress was incomplete. Do not redo work or edit files. Submit a corrected workflow_execution_result now with status, completedSteps for every approved Plan step completed, changedFiles, commands, blockers, and summary. Reason: ${reason}`, "workflow-execution-progress-correction");
|
|
14575
14619
|
return;
|
|
14576
14620
|
}
|
|
14577
14621
|
const stepValidationEnabled = settings.workflow.validateAfterEachStep === true;
|
|
14578
14622
|
const stepApprovalEnabled = settings.workflow.requireApprovalPerStep === true;
|
|
14579
|
-
const
|
|
14623
|
+
const executedStepIndex = typeof state.planExecutionStepIndex === "number" ? state.planExecutionStepIndex : state.planProgress?.currentStepIndex ?? 0;
|
|
14624
|
+
const stepValidationIndex = stepValidationEnabled ? executedStepIndex : undefined;
|
|
14580
14625
|
const validationGateActive = planValidationGateActive(settings);
|
|
14581
|
-
updateState({ mode: "executed", executionSummary: text, planStepValidationIndex: stepValidationIndex, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "executed" }, settings, { lifecycleStatus: "executing", nextAction: validationGateActive ? "validator" : "finish workflow" }) : state.planProgress }, ctx);
|
|
14626
|
+
updateState({ mode: "executed", executionSummary: text, planStepValidationIndex: stepValidationIndex, planExecutionStepIndex: undefined, planProgress: workflowPlanProgressEnabled(settings) ? mergePlanProgress({ ...state, mode: "executed" }, settings, { lifecycleStatus: "executing", nextAction: validationGateActive ? "validator" : "finish workflow" }) : state.planProgress }, ctx);
|
|
14582
14627
|
const mission = isMissionWorkflowMode(state) ? activeMission ?? loadMissionState(state.activeMissionId ?? "latest") : undefined;
|
|
14583
14628
|
if (mission?.status === "running") {
|
|
14584
14629
|
const milestone = mission.milestones[mission.currentMilestoneIndex];
|
|
@@ -14708,6 +14753,7 @@ Public workflow commands:
|
|
|
14708
14753
|
updateState({
|
|
14709
14754
|
mode: "reviewed",
|
|
14710
14755
|
planStepValidationIndex: undefined,
|
|
14756
|
+
planExecutionStepIndex: undefined,
|
|
14711
14757
|
validationVerdict: undefined,
|
|
14712
14758
|
planProgress: { ...progress, currentStepIndex: nextIndex, validationStatus: "pending", nextAction: "executor" },
|
|
14713
14759
|
}, ctx);
|
|
@@ -14721,7 +14767,7 @@ Public workflow commands:
|
|
|
14721
14767
|
});
|
|
14722
14768
|
return;
|
|
14723
14769
|
}
|
|
14724
|
-
updateState({ planStepValidationIndex: undefined }, ctx);
|
|
14770
|
+
updateState({ planStepValidationIndex: undefined, planExecutionStepIndex: undefined }, ctx);
|
|
14725
14771
|
if (returnToPlan) {
|
|
14726
14772
|
deferWorkflowAction(pi, "complete plan workflow after step validation", () => completePlanWorkflow(ctx, text, verdict));
|
|
14727
14773
|
} else {
|
|
@@ -204,7 +204,8 @@ export function formatAnswersForPlanner(questions: ClarificationQuestion[], answ
|
|
|
204
204
|
|
|
205
205
|
export function planValidationStatusForVerdict(verdict: WorkflowState["validationVerdict"]): PlanValidationStatus {
|
|
206
206
|
if (verdict === "PASS") return "pass";
|
|
207
|
-
if (verdict === "
|
|
207
|
+
if (verdict === "PARTIAL PASS") return "partial pass";
|
|
208
|
+
if (verdict === "UNKNOWN") return "unknown";
|
|
208
209
|
return "fail";
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -93,7 +93,7 @@ export interface StandardRuntimeState {
|
|
|
93
93
|
|
|
94
94
|
export type PlanLifecycleStatus = "planning" | "awaiting_clarification" | "plan_ready" | "approved" | "reviewing" | "executing" | "validating" | "repairing" | "revalidating" | "completed" | "blocked";
|
|
95
95
|
export type PlanStepStatus = "pending" | "active" | "completed" | "failed" | "blocked" | "skipped";
|
|
96
|
-
export type PlanValidationStatus = "pending" | "running" | "pass" | "fail" | "unknown";
|
|
96
|
+
export type PlanValidationStatus = "pending" | "running" | "pass" | "partial pass" | "fail" | "unknown";
|
|
97
97
|
|
|
98
98
|
export interface PlanProgressStep {
|
|
99
99
|
id: string;
|
|
@@ -235,6 +235,7 @@ export interface WorkflowState {
|
|
|
235
235
|
repairHistory?: WorkflowRepairHistoryEntry[];
|
|
236
236
|
lastRepairStatus?: "none" | "running" | "completed" | "failed" | "blocked";
|
|
237
237
|
planStepValidationIndex?: number;
|
|
238
|
+
planExecutionStepIndex?: number;
|
|
238
239
|
planRuntime?: PlanRuntimeState;
|
|
239
240
|
planProgress?: PlanProgressState;
|
|
240
241
|
standardRuntime?: StandardRuntimeState;
|