Repository navigation
v2.31.0 — severity alignment and the non-interactive dispatch guard
Ports two stride-side changes into this GitHub Copilot port: the exploratory severity ladder is aligned with the reviewer's issue vocabulary and given an escalation policy, and the Step 5.5 / Phase 3.5 dispatch is narrowed to surfaces that can complete without a human. Prompt text only; the companion stride-copilot-exploratory-testing plugin is untouched.
Added — the exploratory severity ladder mapped onto the reviewer issue vocabulary
The companion plugin rates each bug on a four-level ladder (Critical > High > Moderate > Minor, owned by its bug-advocacy skill); reviewer_result has three (critical / important / minor). Nothing said how one became the other, so a finding could reach the completion payload on whichever scale the agent happened to be holding.
skills/stride-completing-tasks/SKILL.mdgains a### Severity mappingsubsection: the four-row table, and the reason the four-into-three collapse falls on High/Moderate — the reviewer enum's values are dispositions at the completion gate rather than descriptions, andcriticalandimportantshare one (fix before proceeding), so that is the boundary whose loss costs least. Collapsing Moderate intominorinstead would file a broken export alongside a truncated label. The section maps; it never re-rates — the plugin's rubric stays the sole source of truth for what level a finding is, and a mapped reviewer value is never written back ontobugs[].severity.- Mapping a severity is not appending an
issues[]entry. Only acriticalthe escalation rules find introduced becomes one; everything else — including a discoveredcritical— goes tocompletion_notesand thetesting_strategynote only. Appending a non-escalating finding would leaveissues[]disagreeing with apassedtesting_strategyverdict, which the pre-submission self-check refuses outright — manufacturing exactly the blocked completion the policy promises not to cause. - Absent or unrecognized severity →
important; never dropped, nevercritical.criticalis the one value that triggers escalation, so an unparsed string must never reach a blocking path;minorwould be a silent downgrade. The raw value is quoted to 40 characters in inline backticks only when it carries nothing from the protected classes — a credential or token, customer data, an internal hostname — otherwise it is replaced with a redaction sentinel and its length reported. Judge by class, never by length: an email address or internal hostname is short and legible, so a length bound would emit the whole thing while looking like a mitigation.
Added — a Critical exploratory finding now escalates, but only when this task introduced it
Before this release a Critical exploratory finding was advisory prose while a partial/unmitigated security verdict was fail-closed — an asymmetry that was an accident rather than a decision. It is now a decision, and a bounded one.
skills/stride-workflow/SKILL.mdgains### Escalation: what happens when a session returns a Critical findingin Step 5.5, andskills/stride-subagent-workflow/SKILL.mdgains the matching**Escalating a Critical finding.**digest in Phase 3.5 — identical in substance, with reciprocal keep-in-sync pointers naming each other's exact heading.- One question decides it: are the responsible lines among the lines this task changed? Introduced escalates fail-closed —
testing_strategy.status→"failed"plus acategory: "testing"/severity: "critical"entry with matchingissue_counts.criticalandissues_foundincrements — which the pre-submission self-check refuses at submission time, so the defect is fixed, the charter re-run, and the review re-run before completing. Discovered appends no issue and flips no verdict: it is recorded at its exploratory severity incompletion_notesand one line ofcompletion_summary, added as an advisory to thetesting_strategynote, and filed as a follow-up defect so it has an owner. A pre-existing bug never blocks an unrelated task. - Provenance is computed from your own artifacts, never from the finding's text, because the application under test controls that text and a blocking escalation must not be triggerable by content an attacker can influence. Falling back to the task's
key_filesis explicitly barred — that would hand the blocking footprint to task-author text. - The change-set mechanics are this port's own, not the reference's. The canonical plugin reads the claim-time dirty baseline from a separate
.stride-dirty-baselinefile. This port has no such file: it carries the baseline as a base64-encodedTASK_DIRTY_BASELINE=line inside.stride-env-cache, alongsideTASK_BASE_REF— one lookup rather than two artifacts — decoding to one<blob-hash>\t<path>line per path, which makes line-level attribution recoverable where a baseline path was touched again. Porting the reference's file path verbatim would have been a reference no agent could resolve. Two port-specific signals are named too:TASK_BASE_REF_TRUSTED='1'marks a base written by the post-before_doingcapture, and a cache carrying noTASK_BASE_REFline at all is the undeterminable branch rather than an error to work around. - Lines the agent did not author are outside the change set, and a non-production determination is required before dispatch. The dirty baseline is computed once, right after
before_doing, so it cannot cover a file the application under test writes into the working tree during a session — counting those would put an app-controlled footprint inside the branch that blocks completion, the exact property this test exists to deny. Anything that appeared without the agent's authorship is excluded and resolves to discovered; the test is authorship, not the file's category, so output the task deliberately generated by a build or codegen step stays in. Separately, narrowing dispatch to theexploreragent takesstride-exploratory-testing-explore's own authorization gate off the automated path — it was never reachable there as a mandatory control, since the previous text already offered direct agent dispatch as an equal alternative, but the agent treats a caller-named target as authorized by construction, so both skills now require establishing that the environment context names a local or explicitly non-production instance before dispatching, and skipping gracefully when it cannot be established. - The unrecognized-severity quote cannot break out of its frame. The 40-character prefix is quoted only when it contains no backtick and no line break — either closes the inline-code span early and lets application-controlled text render as live Markdown in a persisted, reviewer-facing field — and otherwise a sentinel and the length are recorded instead.
- Every uncertain state resolves to
discovered— an undeterminable change set, a base ref failing its sanity check, a baselined path whose per-path attribution could not be resolved, a fault site unidentified after a bounded attempt — and is labelled provenance undetermined, never pre-existing, because those branches never established provenance. Blocking on a link you could not draw would be a denial-of-progress surface. - No structured review block in the payload → no escalation, and nothing may be synthesized. A small task (0-1
key_files) whose review the decision matrix skipped, and a review whose JSON would not parse, both reach this. Never fabricate a structured block, anissues[], anissue_counts, a section verdict or adispatched: true— and never downgrade a review that did run to a self-reported skip. - The remedy is a re-review, and that is a stated exception. Phase 3 says that after fixing reviewer-reported issues you do not need to re-run the reviewer. That rule governs issues the reviewer reported; an escalation the orchestrator wrote into
reviewer_resultleaves afailedverdict and a stale Critical entry that only a fresh review regenerates away — the same remedy the security sub-step already requires. - The graceful-skip contract is unchanged. No trigger condition moved, the fallback text is untouched, and both skills now say plainly that no exploratory finding can block completion on a task that never ran a session.
Changed — Step 5.5 and Phase 3.5 dispatch only non-interactive surfaces
Both skills previously offered the stride-exploratory-testing-explore skill and the explorer agent as equally valid dispatch targets. That skill opens with a question round, so an autonomous workflow that chose it would hang with no error — the worst failure shape, because it looks like a stall rather than a violation.
- The principle governs, not the list: dispatch only a surface that runs to completion without requiring a human, because the orchestrator does not prompt between steps. "Requires a human" is read broadly — a surface that waits on an out-of-band approval fails identically to one that prompts. A surface the plugin gains later qualifies by satisfying the principle, never by being added to a list, and the entries recorded are reasoning rather than a standing guarantee: the companion versions separately, so re-establish a surface from its own frontmatter whenever its version changes.
- The sanctioned surface is the
exploreragent, and it is the only one. This port's argument is its own, not a translation of the canonical plugin's (which reasons from a withheldAgent/WebFetchallowlist that does not exist here): an agent declares atools:list and a skill has no tool-restriction field at all — an asymmetry the companion's own frontmatter harness makes explicit by requiringtoolson everyagents/*.agent.mdand checking onlyname/descriptionon everyskills/*/SKILL.md. A skill's unattended-safety therefore rests on its prose alone, which the companion says of its own-pairskill in as many words. Theexploreragent additionally states "Never ask the user a question." - Never auto-dispatched:
stride-exploratory-testing-explore— disqualified twice over, by an unconditional question round no argument pre-empts (it must ask, precisely because the agent it dispatches cannot) and by a Step 4 authorization-and-non-production safety gate that fails closed;-recon(the same safety-control confirmation);-pair(human-at-the-keyboard by construction);-nightmare-headline(looping elicitation rounds); and thestride-exploratory-testingrouting skill, which can route to any of them and is what the bare plugin name resolves to, so "dispatch the plugin" lands on it.-charter,-debriefand-hardenclear the bar but run no session. - Disqualification turns on prompts a surface can raise, by a stated test: a prompt you pre-empt with an input you control does not disqualify; one fired by a condition you do not control does; a safety-control prompt disqualifies regardless.
- This narrows what may be run, never what counts as installed. Detection is unchanged and still availability-only, with the never-execute rule intact — it now says explicitly that detection confers no dispatch licence. The
stride-subagent-workflowgate additionally gains the same seven-surface detection list Step 5.5 uses, so the two gates share one trigger — the-pairand-hardenskills and the routing skill are named in the prose as present-but-barred rather than added as detection entries, which would have widened the gate.
Fixed — three flow artifacts that routed around the policy
stride-subagent-workflow's Quick Reference Card named the -explore skill as a dispatch target this release bars, and stride-workflow's ASCII workflow and Quick Reference Card described a Step 5.5 with no escalation branch — while already carrying the "app not running → do NOT fail" branch, which establishes them as the place Step 5.5's outcomes are enumerated. All three are corrected.
Fixed — the enforcement anchor, corrected before release
The reference anchors this escalation in a completion self-check checkbox ("Section verdict and issues[] agree in both directions") that this port does not have. The first draft re-anchored both citations to Phase 3's "Fix all Critical issues before proceeding" gate — which is real, but is upstream of the phase that writes the escalation: Phase 3 has already been passed by the time Step 5.5 runs, so a Critical appended there cannot flow back through it, and the claim described a mechanism that would never fire. stride-completing-tasks' pre-submission self-check therefore gains the missing checkbox, on the same shape as the one that already backs the security_considerations escalation: a passed testing_strategy alongside a category: "testing" Critical is a hard fail. That makes the escalation genuinely fail-closed at submission time, which is what patterns_to_follow's "follow the fail-closed escalation already documented for the security-review integration" actually requires.
Scope
Prompt text and documentation only. No completion field is added, removed, or made required; no seventh workflow_steps name; no enum, schema, hook-script or agent change. The escalation writes only into fields that already exist (reviewer_result.issues[], issue_counts, testing_strategy.status and .note, completion_notes, completion_summary), as a named bounded exception to the whole-object-copy rule on the same terms the security_considerations escalation already is. Deliberately not corrected here: stride-subagent-workflow's flowchart does not mention Phase 3.5 at all, and stride-completing-tasks' flowchart and Quick Reference Card do not mention Step 5.5 at all. Both are pre-existing gaps this release does not introduce, and both are left as follow-ups rather than folded in — which is the discovered versus introduced policy this release itself adds, applied to its own diff. Also deliberately not in this release: the explorer session budget and named environment-context inputs, the richer session recording, the .exploratory/ gitignore guidance, and the optional -harden sub-step.