scripts/internal/continuous-co-review/stop-intent-contract.ps1
|
# FR-045a STOP-INTENT classification (T019 piece 4b; dogfood + corrections 2026-07-13). Classifies each host Stop # into THREE outcomes so an authorized workflow is neither stalled nor falsely handed back: # # continue - the CURRENT assistant turn declares the `continue` marker AND lifecycle authorization confirms # an already-authorized phase with NO unapproved boundary to cross, AND no async is pending, AND # the message carries no review request / question / completion / blocker / hand-back → SUPPRESS # the Stop (no packet); the agent performs the NEXT authorized action (not another status packet). # MARKER-AND-GATE: neither the marker nor the phase alone suffices — the marker asserts only that # executable work remains; the lifecycle state supplies authorization; work is never self- # authorized across a pending boundary. # intermediate - required owned ASYNC work is in flight (the T019 registry, OR the assistant's `intermediate` # marker as the fallback for host-native work with no registry entry) → ONE concise, rate-limited # progress line + the marker; no packet, no verdict marker; NEVER launch duplicate work. # real - a pending boundary, human judgment/authorization/an external action, terminal completion, an # unrecoverable failure, or an intentional hand-back → existing boundary / non-boundary packet. # # THE FALSE PREMISE THIS CORRECTS: "no in-flight background work ⇒ real stop" is WRONG. Absence of async work only # means the event is not an async yield; it does NOT create a reason to hand control to the user. And "the session # is long / context is thin / a natural checkpoint" is an internal concern, NEVER a boundary — compaction handles # session length, so it can never justify `real`. # # PURE + DETERMINISTIC. The Stop hook computes the boolean inputs (the marker in THIS turn's assistant message via # Get-ContinuousCoReviewStopIntentMarkerIntent, the lifecycle/authorization gate state, the T019 in-flight # registry, whether the message carries a review request / completion / hand-back, and the loop-guard counter) and # this function classifies. NOT a per-host capability matrix: a host with no background execution never produces # an async signal, so it is only ever `continue` (marker + gate) or `real`. # # MARKER RULES: a marker is honoured ONLY as the CURRENT assistant turn's own declaration — a marker quoted in # USER content or carried from an EARLIER transcript turn is IGNORED (not a signal, and it does NOT force `real`). # Only an AUTHORITATIVELY-KNOWN-TERMINAL task invalidates a stale `intermediate` marker; an UNKNOWN/UNREGISTERED # task does not (that is what the fallback is for). # # LOOP GUARD: a `continue` requires INTERVENING material progress / changed workflow state between consecutive # continues. Repeated `continue` markers with neither must not loop forever — after the bounded retry the hook # trips the guard and this returns a REAL stop (a specific internal-routing failure), never an infinite loop. $script:ContinuousCoReviewStopIntentContinueMarker = '<!-- SPECREW-STOP-INTENT: continue -->' $script:ContinuousCoReviewStopIntentIntermediateMarker = '<!-- SPECREW-STOP-INTENT: intermediate -->' function Get-ContinuousCoReviewStopIntentMarker { param([ValidateSet('continue', 'intermediate')][string]$Intent = 'intermediate') if ($Intent -eq 'continue') { return $script:ContinuousCoReviewStopIntentContinueMarker } return $script:ContinuousCoReviewStopIntentIntermediateMarker } # Pure parser: the stop-intent a marker declares in $Text — 'continue' | 'intermediate' | ''. The CALLER passes # ONLY the current assistant turn's text (never quoted user content / earlier turns); provenance is the hook's # job. Both markers present ⇒ ambiguous ⇒ '' (fail-closed to no clear intent, so neither continue nor the marker # async-signal fires). function Get-ContinuousCoReviewStopIntentMarkerIntent { param([AllowNull()][AllowEmptyString()][string]$Text) if ([string]::IsNullOrEmpty($Text)) { return '' } $hasContinue = $Text.Contains($script:ContinuousCoReviewStopIntentContinueMarker) $hasIntermediate = $Text.Contains($script:ContinuousCoReviewStopIntentIntermediateMarker) if ($hasContinue -and $hasIntermediate) { return '' } if ($hasContinue) { return 'continue' } if ($hasIntermediate) { return 'intermediate' } return '' } function Resolve-ContinuousCoReviewStopIntent { param( # REAL-forcing (precedence 1) — a genuine handoff. Any one wins, overriding a marker. [bool]$LifecycleBoundaryPending = $false, # a lifecycle verdict boundary is pending (a human handoff is due) [bool]$UserActionRequired = $false, # a substantive review request, a user question, or a required external action [bool]$AgentBlockedOrHandingBack = $false, # execution cannot continue (a blocker), or the agent intentionally hands control back [bool]$RequestedWorkComplete = $false, # terminal completion; a final report is due # ASYNC / intermediate (precedence 2): [bool]$OwnedWorkInFlight = $false, # required owned async work is running / awaiting a result (the T019 registry) [bool]$RuntimeWorkKnownTerminal = $false, # the registry AUTHORITATIVELY knows the referenced task is terminal (invalidates a stale marker) # MARKER — the current assistant turn's declared intent: [ValidateSet('', 'continue', 'intermediate')][string]$MarkerIntent = '', [bool]$MarkerFromAssistant = $true, # the marker is the CURRENT assistant turn (else IGNORED: user-quoted / stale) # CONTINUE authorization (precedence 3 — the GATE half of marker-and-gate): [bool]$AuthorizedWorkRemains = $false, # lifecycle confirms an already-authorized phase AND no unapproved boundary to cross # LOOP GUARD: [bool]$ContinueLoopGuardTripped = $false # repeated continue with no intervening material progress / unchanged workflow → bounded to a real stop ) $mk = { param([string]$outcome, [bool]$enforcePacket, [bool]$emitProgress, [string]$reason) [pscustomobject]@{ outcome = $outcome; enforce_packet = $enforcePacket; emit_progress = $emitProgress; reason = $reason } } # A marker counts ONLY as the current assistant turn's own declaration (never user-quoted / stale / earlier turn). $effectiveMarker = if ($MarkerFromAssistant) { $MarkerIntent } else { '' } # 1. REAL — a pending boundary, a required human/external action, terminal completion, or a failure / hand-back. if ($LifecycleBoundaryPending) { return & $mk 'real' $true $false 'a lifecycle verdict boundary is pending (a human handoff is due)' } if ($UserActionRequired) { return & $mk 'real' $true $false 'a substantive review request, a user question, or a required external action' } if ($AgentBlockedOrHandingBack) { return & $mk 'real' $true $false 'execution cannot continue automatically, or the agent intentionally hands control back' } if ($RequestedWorkComplete) { return & $mk 'real' $true $false 'the requested work is complete and ready for a final report' } # 2. INTERMEDIATE — required owned ASYNC work in flight (the registry OR the assistant's `intermediate` marker), # unless authoritatively known-terminal. Async takes precedence over continue. $asyncInFlight = ($OwnedWorkInFlight -or ($effectiveMarker -eq 'intermediate')) -and (-not $RuntimeWorkKnownTerminal) if ($asyncInFlight) { return & $mk 'intermediate' $false $true 'required owned async work is in flight; a one-line operational yield - the agent resumes when it completes' } # 3. CONTINUE — MARKER-AND-GATE: the current-assistant `continue` marker AND confirmed authorization (neither # alone). The loop guard bounds repeated no-progress continues to a real routing failure. if (($effectiveMarker -eq 'continue') -and $AuthorizedWorkRemains) { if ($ContinueLoopGuardTripped) { return & $mk 'real' $true $false 'continue-loop-guard tripped: repeated continue with no intervening material progress / unchanged workflow - an internal routing failure surfaced as a real stop' } return & $mk 'continue' $false $false 'the current assistant turn declares continue AND lifecycle authorization confirms remaining in-phase work; suppress the stop and perform the next authorized action' } # 4. Otherwise → normal real-stop enforcement, with an explicit reason (never an empty handoff). return & $mk 'real' $true $false 'no current-turn continue marker with authorization, no async in flight, and no other trigger; normal real-stop enforcement applies' } # FR-045a PACKET CONSISTENCY (maintainer correction 2026-07-13). A real-stop packet's five sections MUST agree on # whether control has transferred to the user. If "What Needs Your Review" carries a SUBSTANTIVE item (a decision, # approval request, unresolved tradeoff, or requested confirmation) then it is a review-required real stop: "What # I Need From You" MUST state the exact requested response, "What Happens Next" MUST say the work is HELD pending # that response, and the packet MUST NOT say "nothing blocking" / "flag this if…" / "I'll proceed" / otherwise # auto-continue. An informational note is NOT a review item and must not sit under that section. Returns # { consistent; reason }. function Test-ContinuousCoReviewStopPacketConsistency { param( [bool]$HasSubstantiveReviewItems = $false, # "What Needs Your Review" contains a real decision/approval/tradeoff/confirmation [bool]$NeedsFromUserStatesResponse = $false, # "What I Need From You" states the EXACT requested response [bool]$HappensNextSaysHeld = $false, # "What Happens Next" says the work is HELD pending that response [bool]$SaysNothingRequiredOrWillProceed = $false # the packet says "nothing blocking" / "I'll proceed" / auto-continues ) if (-not $HasSubstantiveReviewItems) { # No review request: a completion report, a non-boundary note, or a `continue`/`intermediate` message is fine. return [pscustomobject]@{ consistent = $true; reason = $null } } if ($SaysNothingRequiredOrWillProceed) { return [pscustomobject]@{ consistent = $false; reason = 'a substantive review request cannot coexist with "nothing required" / "I''ll proceed" / automatic continuation - a review-required stop transfers control to the user' } } if (-not $NeedsFromUserStatesResponse) { return [pscustomobject]@{ consistent = $false; reason = 'a review request must state the EXACT response needed under "What I Need From You" (never "nothing")' } } if (-not $HappensNextSaysHeld) { return [pscustomobject]@{ consistent = $false; reason = 'a review request must say under "What Happens Next" that the work is HELD pending the response' } } return [pscustomobject]@{ consistent = $true; reason = $null } } |