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
319 changes: 314 additions & 5 deletions crates/devflow-cli/src/pipeline_launch.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions crates/devflow-cli/src/pipeline_outcomes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,7 @@ mod tests {
summary: None,
verdict: Some(Verdict::Pass),
decided_by_layer: Some(0),
checkpoint: None,
};
let outcome = classify_validate_outcome(&result);
assert_eq!(outcome, ValidateOutcome::Passed);
Expand Down Expand Up @@ -1691,6 +1692,7 @@ mod tests {
summary: None,
verdict: Some(Verdict::Gaps),
decided_by_layer: Some(0),
checkpoint: None,
};
let outcome = classify_validate_outcome(&result);
assert!(matches!(outcome, ValidateOutcome::Ambiguous(_)));
Expand Down Expand Up @@ -1743,6 +1745,7 @@ mod tests {
summary: None,
verdict: None,
decided_by_layer: Some(0),
checkpoint: None,
};
let outcome = classify_validate_outcome(&result);
assert!(matches!(outcome, ValidateOutcome::Ambiguous(_)));
Expand Down Expand Up @@ -1794,6 +1797,7 @@ mod tests {
summary: None,
verdict: Some(Verdict::Pass),
decided_by_layer: Some(0),
checkpoint: None,
};
assert_eq!(
classify_validate_outcome(&result),
Expand Down Expand Up @@ -1845,6 +1849,7 @@ mod tests {
summary: None,
verdict,
decided_by_layer: if layer0 { Some(0) } else { Some(1) },
checkpoint: None,
}
}

Expand Down Expand Up @@ -4673,6 +4678,7 @@ mod tests {
summary: None,
verdict: Some(Verdict::Gaps),
decided_by_layer: Some(0),
checkpoint: None,
};
let outcome = classify_validate_outcome(&result);
assert!(matches!(outcome, ValidateOutcome::Ambiguous(_)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
649 changes: 543 additions & 106 deletions crates/devflow-core/src/agent_result.rs

Large diffs are not rendered by default.

110 changes: 107 additions & 3 deletions crates/devflow-core/src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ macro_rules! gate_resolution_rule {
/// 2026-09-11). Do not widen it without a new decision.
pub const GATE_RESOLUTION_RULE: &str = gate_resolution_rule!();

/// How an agent reports a `blocking-human` gate it is not resolving: the
/// `checkpoint` field on its final marker, which the pipeline reads through
/// [`crate::agent_result::AgentResult::declares_blocking_human_checkpoint`]
/// (49-REVIEW CR-01). It names a subagent's halt too, because under GSD the
/// executor subagent stops and the orchestrator writes the marker.
///
/// It lives in [`CODE_STAGE_POLICY`] and not in [`COMPLETION_PROTOCOL`] for two
/// reasons. Only the Code stage runs plans, so only Code may declare (the
/// pipeline ignores the field elsewhere), and the auto-decide resume prompt,
/// which carries only the protocol, does not re-offer it. The example sits
/// inside a sentence, never at the start of a line, so an echoed prompt cannot
/// parse as a declaring marker. A macro for the same `concat!` reason as
/// [`gate_resolution_rule`].
macro_rules! checkpoint_declaration {
() => {
"When you report a blocking-human gate that you or a subagent you ran \
stopped at, say so on your final line with the checkpoint field, for \
example `DEVFLOW_RESULT: {\"status\": \"failed\", \"checkpoint\": \
\"blocking-human\", \"reason\": \"which gate stopped and the decision it \
needs\"}`. Use the checkpoint field for nothing else: not for a gate you \
resolved, and not for any other failure."
};
}
#[cfg(test)]
const CHECKPOINT_DECLARATION: &str = checkpoint_declaration!();

/// Shared policy for full-execute Code prompts. This advises the agent during
/// one-shot Code execution, while [`checkpoint_auto_decide_prompt`] is injected
/// into a resumed session after DevFlow's own human-blocking gate finds no
Expand Down Expand Up @@ -122,6 +148,8 @@ of the decision.\n\
This authority never extends to a package-verification checkpoint: do not \
self-resolve or approve one under any circumstances, and report it instead. ",
gate_resolution_rule!(),
" ",
checkpoint_declaration!(),
" This policy must not pause execution or request human input."
);

Expand Down Expand Up @@ -617,9 +645,13 @@ fn stage_prompt_with_project(stage: Stage, phase: PhaseId, project_root: Option<
///
/// The resume wording tells the agent to name the resolved gate in prose rather
/// than copy the checkpoint's declaration line. A failed resume capture is
/// re-read by `agent_result::text_reports_human_gate`, so a copied declaration
/// line could read as a new checkpoint. The detector accepts only a
/// `**Gate:**`-labeled line; the
/// re-read by `agent_result::latest_stated_outcome`, so a copied declaration
/// line could read as a new checkpoint. A checkpoint is reported only by a
/// `**Gate:**`-labeled line with no marker after it, or by a final
/// `DEVFLOW_RESULT` that declares `"checkpoint": "blocking-human"`
/// ([`CODE_STAGE_POLICY`] offers that field only for a gate being reported
/// rather than resolved, and this prompt does not offer it). The wording of a
/// failure reason is never read (49-REVIEW CR-01). The
/// `resume_prompt_does_not_read_as_a_blocking_human_checkpoint` test pins that
/// boundary and rejects prose such as `Resolved gate: blocking-human`.
///
Expand Down Expand Up @@ -1281,6 +1313,78 @@ mod tests {
);
}

/// 49-REVIEW CR-01: the Code policy's checkpoint example is exactly what
/// the parser accepts as a declaration, so the prompt and `AgentResult`
/// cannot drift apart. The sentence is pinned verbatim, so a widening such
/// as "declare it on any failure" fails here.
#[test]
fn checkpoint_declaration_example_is_what_the_parser_accepts() {
assert!(
CODE_STAGE_POLICY.contains(CHECKPOINT_DECLARATION),
"the Code policy must carry the declaration sentence"
);
assert!(CHECKPOINT_DECLARATION.starts_with(
"When you report a blocking-human gate that you or a subagent you ran stopped at,"
));
assert!(CHECKPOINT_DECLARATION.ends_with(
"Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure."
));
let example = CHECKPOINT_DECLARATION
.split('`')
.find(|part| part.starts_with("DEVFLOW_RESULT:"))
.expect("the sentence quotes one example marker");
let parsed = crate::agent_result::parse_devflow_result(example)
.unwrap_or_else(|| panic!("the example must parse: {example}"));
assert!(parsed.declares_blocking_human_checkpoint(), "{example}");
}

/// An echoed prompt must never read as a declaring marker. Every marker
/// line that starts a line in a delivered prompt (the parser only reads
/// line-start markers) parses as a non-declaring example, for the Code
/// prompt that carries the policy and for the policy-carrying fix prompt.
#[test]
fn no_line_start_marker_in_a_code_prompt_declares_a_checkpoint() {
let prompts = [
stage_prompt(Stage::Code, PhaseId::new(47)),
fix_prompt(FixType::FullExecute, PhaseId::new(47)),
];
for prompt in prompts {
assert!(prompt.contains(CHECKPOINT_DECLARATION));
let markers: Vec<&str> = prompt
.lines()
.filter(|line| {
line.get(.."DEVFLOW_RESULT:".len())
.is_some_and(|head| head.eq_ignore_ascii_case("DEVFLOW_RESULT:"))
})
.collect();
assert!(!markers.is_empty(), "the protocol examples are present");
for marker in markers {
let parsed = crate::agent_result::parse_devflow_result(marker)
.unwrap_or_else(|| panic!("protocol example must parse: {marker}"));
assert!(!parsed.declares_blocking_human_checkpoint(), "{marker}");
}
assert!(
!crate::agent_result::parse_devflow_result(&prompt)
.is_some_and(|result| result.declares_blocking_human_checkpoint()),
"a whole echoed prompt must not parse as a declaration"
);
}
}

/// Only Code may declare. The resume prompt and every non-Code stage
/// prompt carry no checkpoint form, so a resumed agent is not re-offered
/// it and Define, Plan, Validate and Ship are not invited to use it.
#[test]
fn checkpoint_declaration_is_offered_only_to_the_code_stage() {
let resume = checkpoint_auto_decide_prompt(PhaseId::new(47));
assert!(!resume.contains("\"checkpoint\""), "{resume}");
assert!(!COMPLETION_PROTOCOL.contains("checkpoint"));
for stage in [Stage::Define, Stage::Plan, Stage::Validate, Stage::Ship] {
let prompt = stage_prompt(stage, PhaseId::new(47));
assert!(!prompt.contains("\"checkpoint\""), "{stage:?}: {prompt}");
}
}

#[test]
fn checkpoint_auto_decide_prompt_terminates_with_completion_protocol() {
let prompt = checkpoint_auto_decide_prompt(PhaseId::new(28));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When you reach a `decision` checkpoint and no operator is available, resolve it

An option marked recommended is evidence to weigh, not an automatically decisive answer. In your final message, record the reasoning and comparison that produced the choice: which options you considered and why the chosen one won, not merely a sentence asserting the choice. The final message is the only record of the decision.

This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. This policy must not pause execution or request human input.
This authority never extends to a package-verification checkpoint: do not self-resolve or approve one under any circumstances, and report it instead. A blocking-human gate is human-only, with one exception: if DevFlow resumed you specifically to resolve that gate, resolve it yourself and record your reasoning in your final message; otherwise report it instead. When you report a blocking-human gate that you or a subagent you ran stopped at, say so on your final line with the checkpoint field, for example `DEVFLOW_RESULT: {"status": "failed", "checkpoint": "blocking-human", "reason": "which gate stopped and the decision it needs"}`. Use the checkpoint field for nothing else: not for a gate you resolved, and not for any other failure. This policy must not pause execution or request human input.

## Completion Protocol (REQUIRED)

Expand Down
Loading