Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions apps/presentation/dashboard/smoke/action-review-plan-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,48 @@ check(isStaleActionFailure({ error_code: "action_conflict" }), "Typed conflicts
check(isStaleActionFailure({ proposal: { status: "stale" } }), "Typed stale proposal survives error wrapping");
check(!isStaleActionFailure({ error_code: "canonical_action_failed", error: "conflict with unrelated external service" }), "Error wording cannot classify source state");

for (const [action_kind, operation] of [["todo.update", "complete"], ["monitor.update", "stop"]] as const) {
for (const [action_kind, operation] of [["todo.update", "complete"], ["monitor.update", "stop"], ["gate.resolve", "approve"]] as const) {
for (const status of ["applying", "failed"] as const) {
const parameters = action_kind === "gate.resolve" ? {decision: operation} : {operation};
const terminal = typedActionProposalSchema.parse({...proposal, action_kind, status,
normalized_parameters: {goal_id: "sample-goal", todo_id: "todo_work", operation},
normalized_parameters: {goal_id: "sample-goal", todo_id: "todo_work", ...parameters},
canonical_update_basis: {schema_version: "loopx_chat_canonical_terminal_basis_v0",
provider_revision: "revision-1", registry_sha256: "a".repeat(64), source_authority: "file_v0"},
failure: {error_code: "canonical_update_projection_pending", message: "Display pending", retry_safe: true}});
const plan = compileActionReviewPlan(terminal);
check(plan.canApply && plan.retryOriginal === true, "Terminal recovery retries the original proposal");
check(plan.reason === "canonical_update_projection_pending", "Pending display is distinct from failed business mutation");
check(compileActionReviewPlan({...terminal, status: "stale"}).canApply === false, "A stale terminal preview must be regenerated");
check(compileActionReviewPlan({...terminal, normalized_parameters: {...terminal.normalized_parameters, operation: "edit"}}).canApply === false,
check(compileActionReviewPlan({...terminal, normalized_parameters: {...terminal.normalized_parameters,
...(action_kind === "gate.resolve" ? {decision: "defer"} : {operation: "edit"})}}).canApply === false,
"A terminal review basis cannot enable retries of unrelated operations");
check(compileActionReviewPlan({...terminal, status: "applied", receipt: {projection_verified: true}}).interaction === "completed",
"Only current display readback completes terminal presentation");
}
}

{
const decision = typedActionProposalSchema.parse({...proposal, action_kind: "gate.resolve", status: "preview_ready",
normalized_parameters: {goal_id: "sample-goal", todo_id: "todo_gate", decision: "approve"}});
const applied = (receipt: Record<string, unknown>) => compileActionReviewPlan({...decision, status: "applied", receipt}).decisionFrame;
const verified = {projection_verified: true, outcome: "gate_resolved", decision_outcome: "approve"};
check(JSON.stringify(compileActionReviewPlan(decision).decisionFrame) === JSON.stringify({decision: "approve"}),
"A decision preview names the decision and claims no effect yet");
check(applied({...verified, unblock_resume_state: "resumed"})?.dependentEffect === "resumed", "A verified resume is reported");
check(applied({...verified, unblock_resume_state: "decision_requirements_remaining"})?.dependentEffect === "still_waiting",
"Remaining requirements are not reported as resumed work");
check(applied({...verified, decision_outcome: "reject", unblock_resume_state: "resumed"})?.dependentEffect === "unknown",
"A receipt for another decision cannot confirm this one");
check(applied({...verified, unblock_resume_state: "a_future_state"})?.dependentEffect === "unknown", "Unrecognized effects stay unknown");
check(applied({...verified})?.dependentEffect === "unknown", "A receipt without the effect fact makes no claim");
check(applied({...verified, unblock_resume_state: null})?.dependentEffect === "unknown", "An absent resume receipt does not prove no waiting work");
check(applied({...verified, unblock_resume_state: "target_or_decision_scope_not_found"})?.dependentEffect === "unknown", "A missing decision scope does not prove no waiting work");
check(applied({...verified, projection_verified: false, unblock_resume_state: "resumed"})?.dependentEffect === undefined,
"An unverified readback reports no effect");
check(compileActionReviewPlan({...decision, normalized_parameters: {...decision.normalized_parameters, decision: "defer"}}).decisionFrame === undefined
&& compileActionReviewPlan(proposal).decisionFrame === undefined, "Only recorded decision outcomes compile a decision frame");
}

const operationProposal = typedActionProposalSchema.parse({
...proposal,
proposal_id: "operation-1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,9 @@ export function canReviewAttention(item: WorkspaceAttention): boolean {
// not an authorization grant; only known inactive or missing rows are fenced.
return !["closed", "deferred", "superseded", "unavailable"].includes(item.details?.lifecycle ?? "unknown");
}

/** Approve/reject/withdraw exist only for a User gate Todo; the owner still validates each preview. */
export function canDecideAttention(item: WorkspaceAttention): boolean {
return canReviewAttention(item) && item.details?.interaction === "decision"
&& item.decisionSource !== "run_operator_gate";
}
Loading
Loading