You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(goal_curation): progress-evidence gate fails closed on semantic verdict parse-miss (#2569)
The progress-evidence gate previously FAILED OPEN (Accept) whenever the
LLM/recipe reviewer produced no recognizable accept/reject verdict — even on a
successful, non-empty run — letting hallucinated "0%->100% with no verdict"
bumps land as false "done" states.
Adopt the merge judge's infra-vs-semantic split across both live tiers
(RecipeProgressChecker primary + LlmReviewerProgressChecker direct-LLM fallback):
- INFRA failure (transport error / spawn failure / non-zero exit / output that
strips to empty) -> keep fail-OPEN, so goals aren't blocked on infra hiccups.
- SEMANTIC parse-miss (successful, non-empty response with no verdict, or an
unknown verdict string) -> fail-CLOSED (Reject). Reject only keeps the prior
percent + logs a hallucination alert; it does not stall the goal.
Also parse the structured {"verdict": ...} JSON first in the recipe tier (was a
naive substring scan that wrongly Rejected an `accept` whose rationale mentioned
"reject", and could Accept "unacceptable"), reusing the direct-LLM tier's
tolerant parse_reviewer_response — matching the merge judge.
The merge path reported in #2569 was already fail-closed on main
(#2486/#2490/#2504, merged before #2569 was filed against released v0.22.0);
this adds a regression test pinning the reporter's exact SUCCESS-with-no-verdict
banners (30s AND 102s) -> Verdict::Unclear, and fixes the analogous fail-open in
the progress path.
Docs (progress-evidence-api, text-parsing-wire-formats §2a,
progress-evidence-gating, text-based-brain-protocol) updated to the new policy
and corrected to reflect that progress_reviewer.rs is the live direct-LLM
fallback tier (not deleted). Adds a gadugi outside-in scenario plus
parser/decision regression tests.
Closes#2569
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|`orient.rs`|`OrientJudgment` from LLM response | Same extraction pattern |
43
43
|`rustyclawd.rs`|`EngineerLifecycleDecision` from LLM response | JSON fallback path after DECISION marker — two parsers, double failure surface |
44
-
|`progress_reviewer.rs`|`ReviewerResponse` from LLM response |Dead code — replaced by recipe_progress_checker |
44
+
|`progress_reviewer.rs`|`ReviewerResponse` from LLM response |Retained as the live direct-LLM fallback tier (used when `recipe-runner-rs` is unavailable); its tolerant `parse_reviewer_response` is now also reused by `recipe_progress_checker.rs` for JSON-first verdict extraction|
45
45
|`merge_judge.rs`|`JudgeOutcome` from LLM response | Dead code — replaced by recipe_merge_judge |
46
46
47
47
Every one of these was brittle and unnecessary:
@@ -227,9 +227,12 @@ Several modules were deleted or cleaned up as part of the text-migration changes
227
227
`parse_orient_from_text`, `parse_lifecycle_from_text`) rewritten as trivial
228
228
first-word/first-float extractors.
229
229
230
-
The daemon wiring in `operator_commands_ooda/daemon/mod.rs` was updated to
231
-
match: the `LlmReviewerProgressChecker` fallback arm was removed. The chain
232
-
is now `RecipeProgressChecker` → `NoopProgressEvidenceChecker`.
230
+
The daemon wiring in `operator_commands_ooda/daemon/mod.rs` resolves the
231
+
progress-evidence gate in three tiers: `RecipeProgressChecker` (recipe-runner
232
+
backed, primary) → `LlmReviewerProgressChecker` (direct-LLM fallback, in
233
+
`progress_reviewer.rs`) → `NoopProgressEvidenceChecker`. Both live LLM tiers
234
+
share the same infra-fail-open / semantic-parse-miss-fail-closed verdict policy
235
+
(see [Progress-evidence API](../reference/progress-evidence-api.md)).
| Recipe stdout contains `"accept"` keyword |`Accept`|`"progress-assessment-reviewer: accept — <surrounding text as rationale>"`|
100
-
| Recipe stdout contains `"reject"` keyword |`Reject`|`"progress-assessment-reviewer: reject — <surrounding text as rationale>"`|
101
-
| No keyword found in recipe stdout |`Accept` (fail-open) |`"progress-assessment-reviewer: no verdict keyword found; accepting to avoid blocking goal"`|
| Recipe stdout contains `"accept"` keyword |`Accept`|`"recipe-progress-checker: accept — <surrounding text as rationale>"`|
109
+
| Recipe stdout contains `"reject"` keyword |`Reject`|`"recipe-progress-checker: reject — <surrounding text as rationale>"`|
110
+
| No keyword in **non-empty** recipe stdout |`Reject` (fail-closed: semantic parse-miss) |`"recipe-progress-checker: no verdict keyword in non-empty recipe output; rejecting unverified progress"`|
111
+
|**Empty** recipe stdout on a successful run |`Accept` (fail-open: infra gap) |`"recipe-progress-checker: empty recipe output; accepting to avoid blocking goal on infra"`|
0 commit comments