feat: surface explicit auto-next obligation failure in force-recall hook
This commit is contained in:
@@ -356,6 +356,11 @@ function buildApprovedPlanContinuityInput(wrapperResult: any, autoChainPlanResul
|
|||||||
: (wrapperResult?.handoff?.mode === "button_path" ? "waiting_user" : "completed");
|
: (wrapperResult?.handoff?.mode === "button_path" ? "waiting_user" : "completed");
|
||||||
|
|
||||||
const dispatchReceipt = wrapperResult?.dispatchReceipt ?? null;
|
const dispatchReceipt = wrapperResult?.dispatchReceipt ?? null;
|
||||||
|
const nextTaskKnown = wrapperResult?.nextTaskKnown === true
|
||||||
|
|| (plannerDerivedAction != null && typeof autoChainPlanResult?.derivedAction === 'string' && autoChainPlanResult.derivedAction !== 'none');
|
||||||
|
const sameApprovedPlan = wrapperResult?.sameApprovedPlan === true || plannerDerivedAction != null;
|
||||||
|
const taskBoundaryStop = wrapperResult?.taskBoundaryStop === true || replyClosureState === 'completed';
|
||||||
|
const highRiskStop = wrapperResult?.highRiskStop === true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
planId: wrapperResult?.planId ?? "hook-preflight-approved-plan",
|
planId: wrapperResult?.planId ?? "hook-preflight-approved-plan",
|
||||||
@@ -364,6 +369,10 @@ function buildApprovedPlanContinuityInput(wrapperResult: any, autoChainPlanResul
|
|||||||
nextDerivedAction,
|
nextDerivedAction,
|
||||||
replyClosureState,
|
replyClosureState,
|
||||||
dispatchReceipt,
|
dispatchReceipt,
|
||||||
|
nextTaskKnown,
|
||||||
|
sameApprovedPlan,
|
||||||
|
taskBoundaryStop,
|
||||||
|
highRiskStop,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +396,12 @@ function buildApprovedPlanContinuityBlock(result: ApprovedPlanContinuityResult |
|
|||||||
|
|
||||||
if (result.ok === false) {
|
if (result.ok === false) {
|
||||||
lines.push("- HARD_GATE: Do not close out this reply as normal completion.");
|
lines.push("- HARD_GATE: Do not close out this reply as normal completion.");
|
||||||
lines.push("- HARD_GATE: Route back to continuity failure until a real next dispatch receipt exists, unless closure state is waiting_user, blocked, or pending_verification.");
|
if (result.reason === 'missing_auto_next_dispatch') {
|
||||||
|
lines.push("- HARD_GATE: Do not stop at this completed-task boundary.");
|
||||||
|
lines.push("- HARD_GATE: Auto-dispatch the next task in the same approved plan, unless waiting_user, blocked, pending_verification, or high-risk stop applies.");
|
||||||
|
} else {
|
||||||
|
lines.push("- HARD_GATE: Route back to continuity failure until a real next dispatch receipt exists, unless closure state is waiting_user, blocked, or pending_verification.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push("[/APPROVED_PLAN_CONTINUITY_GATE]", "");
|
lines.push("[/APPROVED_PLAN_CONTINUITY_GATE]", "");
|
||||||
|
|||||||
@@ -312,8 +312,10 @@ async function main() {
|
|||||||
assert.match(passInjected, /\[APPROVED_PLAN_CONTINUITY_GATE\]/, 'hook pass-path should emit approved-plan continuity gate block');
|
assert.match(passInjected, /\[APPROVED_PLAN_CONTINUITY_GATE\]/, 'hook pass-path should emit approved-plan continuity gate block');
|
||||||
assert.match(passInjected, /status=continuity_failure/, 'hook pass-path should fail continuity when planner only returns dry-run dispatch without a real receipt');
|
assert.match(passInjected, /status=continuity_failure/, 'hook pass-path should fail continuity when planner only returns dry-run dispatch without a real receipt');
|
||||||
assert.match(passInjected, /verdict=continuity_failure/, 'hook pass-path should expose continuity failure verdict when no real dispatch receipt exists');
|
assert.match(passInjected, /verdict=continuity_failure/, 'hook pass-path should expose continuity failure verdict when no real dispatch receipt exists');
|
||||||
assert.match(passInjected, /reason=missing_dispatch_receipt/, 'hook pass-path should require a real dispatch receipt instead of treating dry-run dispatch as one');
|
assert.match(passInjected, /reason=missing_auto_next_dispatch/, 'hook pass-path should require auto-next dispatch proof instead of treating dry-run dispatch as enough');
|
||||||
assert.match(passInjected, /Route back to continuity failure until a real next dispatch receipt exists/, 'hook pass-path should hard-gate normal closeout until a real receipt exists');
|
assert.match(passInjected, /Do not stop at this completed-task boundary/, 'hook pass-path should explicitly forbid stopping at the completed-task boundary');
|
||||||
|
assert.match(passInjected, /Auto-dispatch the next task in the same approved plan, unless waiting_user, blocked, pending_verification, or high-risk stop applies/, 'hook pass-path should explain the auto-next obligation exceptions');
|
||||||
|
assert.match(passInjected, /Do not stop at this completed-task boundary/, 'hook pass-path should hard-gate the completed-task boundary');
|
||||||
assert.doesNotMatch(passInjected, /\[APPROVED_PLAN_CONTINUITY_GATE\][\s\S]*status=pass/, 'hook pass-path should not let approved-plan continuity pass on dry-run dispatch alone');
|
assert.doesNotMatch(passInjected, /\[APPROVED_PLAN_CONTINUITY_GATE\][\s\S]*status=pass/, 'hook pass-path should not let approved-plan continuity pass on dry-run dispatch alone');
|
||||||
|
|
||||||
const failInjected = await withPatchedWrapper(buildWrapperScript({
|
const failInjected = await withPatchedWrapper(buildWrapperScript({
|
||||||
|
|||||||
Reference in New Issue
Block a user