mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-14 03:33:43 +08:00
fix: derive goal progress from the current merged checklist
This commit is contained in:
parent
c024e2d9f0
commit
6a00c3869d
@ -473,8 +473,6 @@ public class GoalServiceImpl implements GoalService {
|
||||
// persistent pause/input boundary established while the call ran.
|
||||
if (result != null && (!Boolean.TRUE.equals(fresh.getPersistentExecution())
|
||||
|| fresh.getStatus() == GoalStatus.ACTIVE)) {
|
||||
w.set(GoalEntity::getCompletionScore, result.score())
|
||||
.set(GoalEntity::getProgressSummary, result.gap());
|
||||
// Persist the checklist by carrier: bootstrap writes the fresh
|
||||
// draft; verdict merges the per-criterion delta into the
|
||||
// current list (re-read on the locked `fresh` to avoid races).
|
||||
@ -482,6 +480,21 @@ public class GoalServiceImpl implements GoalService {
|
||||
if (criteriaJson != null) {
|
||||
w.set(GoalEntity::getCriteria, criteriaJson);
|
||||
}
|
||||
List<GoalCriterion> current = GoalCriteriaCodec.parse(
|
||||
criteriaJson != null ? criteriaJson : fresh.getCriteria(), objectMapper);
|
||||
if (!current.isEmpty() && !GoalEvaluationResult.DECISION_FALLBACK.equals(result.decision())) {
|
||||
// The model may have seen fewer criteria. Derive the public
|
||||
// projection from the same fresh checklist this CAS writes.
|
||||
List<GoalCriterion> remaining = GoalCriteriaCodec.remaining(current);
|
||||
double score = (double) (current.size() - remaining.size()) / current.size();
|
||||
String gap = remaining.isEmpty() ? "" : "Still missing: " + remaining.stream()
|
||||
.map(GoalCriterion::text).collect(java.util.stream.Collectors.joining("; "));
|
||||
w.set(GoalEntity::getCompletionScore, score)
|
||||
.set(GoalEntity::getProgressSummary, gap);
|
||||
} else {
|
||||
w.set(GoalEntity::getCompletionScore, result.score())
|
||||
.set(GoalEntity::getProgressSummary, result.gap());
|
||||
}
|
||||
}
|
||||
bumpVersionAndTime(w);
|
||||
return w;
|
||||
@ -489,9 +502,11 @@ public class GoalServiceImpl implements GoalService {
|
||||
|
||||
Map<String, Object> detail = new LinkedHashMap<>();
|
||||
if (result != null) {
|
||||
detail.put("completionScore", result.score());
|
||||
detail.put("gap", result.gap());
|
||||
detail.put("completionScore", g.getCompletionScore());
|
||||
detail.put("gap", g.getProgressSummary());
|
||||
detail.put("decision", result.decision());
|
||||
detail.put("evaluatorScore", result.score());
|
||||
detail.put("evaluatorGap", result.gap());
|
||||
detail.put("evaluatorModel", result.evaluatorModel());
|
||||
detail.put("latencyMs", result.latencyMs());
|
||||
}
|
||||
|
||||
@ -95,6 +95,30 @@ class GoalPersistenceIntegrationTest {
|
||||
assertEquals(2, saved.getAgentLlmCallsUsed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lateVerdictProjectsProgressFromCurrentChecklist() throws Exception {
|
||||
GoalEntity created = goalService.create(req("late-verdict-current-progress", "prepare a report"), "alice");
|
||||
goalService.appendCriterion(created.getId(), "write the report", "alice");
|
||||
// The model evaluated only C1. A second condition commits before the result.
|
||||
goalService.appendCriterion(created.getId(), "include an appendix", "alice");
|
||||
var delayed = new vip.mate.goal.model.GoalEvaluationResult(1.0, "", "completed", true,
|
||||
"fixture", 1, 0, java.util.List.of(
|
||||
new vip.mate.goal.model.GoalChecklistVerdict.CriterionVerdict("C1", true, "report written")), null);
|
||||
goalService.recordEvaluation(created.getId(), delayed, 2, 1);
|
||||
GoalEntity saved = goalService.getById(created.getId());
|
||||
assertEquals(0.5, saved.getCompletionScore());
|
||||
var event = goalService.listEvents(created.getId(), 20).stream()
|
||||
.filter(e -> "evaluated".equals(e.getEventType())).findFirst().orElseThrow();
|
||||
var detail = new com.fasterxml.jackson.databind.ObjectMapper().readTree(event.getDetailJson());
|
||||
assertEquals(0.5, detail.get("completionScore").asDouble());
|
||||
assertEquals(1.0, detail.get("evaluatorScore").asDouble());
|
||||
org.junit.jupiter.api.Assertions.assertTrue(detail.get("gap").asText().contains("include an appendix"));
|
||||
org.junit.jupiter.api.Assertions.assertTrue(saved.getProgressSummary().contains("include an appendix"));
|
||||
assertEquals(GoalStatus.ACTIVE, saved.getStatus());
|
||||
assertEquals(1, saved.getEvalLlmCallsUsed());
|
||||
assertThrows(MateClawException.class, () -> goalService.markEvaluatedCompleted(created.getId(), delayed));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GoalStatus values persist as lowercase literals — load-bearing for uk_agent_goal_active_conv")
|
||||
void status_persistsAsLowercaseString() {
|
||||
|
||||
@ -665,6 +665,31 @@ class GoalServiceTest {
|
||||
assertFalse(setsProperty(writes.getAllValues().get(1), "criteria"), "retry must not replace newly established criteria");
|
||||
}
|
||||
|
||||
@Test
|
||||
void verdictProgressRecomputesAfterCasConflict() {
|
||||
GoalEntity original = persisted(1L, GoalStatus.ACTIVE);
|
||||
original.setCriteria("[{\"id\":\"C1\",\"text\":\"report\",\"passed\":false,\"evidence\":\"\"}]");
|
||||
GoalEntity fresh = persisted(1L, GoalStatus.ACTIVE);
|
||||
fresh.setVersion(1);
|
||||
fresh.setCriteria("[{\"id\":\"C1\",\"text\":\"report\",\"passed\":false,\"evidence\":\"\"},"
|
||||
+ "{\"id\":\"C2\",\"text\":\"appendix\",\"passed\":false,\"evidence\":\"\"}]");
|
||||
when(goalMapper.selectById(1L)).thenReturn(original, original, fresh, fresh);
|
||||
when(goalMapper.update(any(), any(LambdaUpdateWrapper.class))).thenReturn(0, 1);
|
||||
var result = new GoalEvaluationResult(1.0, "", "completed", true, "fixture", 1, 0,
|
||||
java.util.List.of(new vip.mate.goal.model.GoalChecklistVerdict.CriterionVerdict(
|
||||
"C1", true, "report written")), null);
|
||||
service.recordEvaluation(1L, result, 2, 1);
|
||||
ArgumentCaptor<LambdaUpdateWrapper> writes = ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
|
||||
verify(goalMapper, times(2)).update(any(), writes.capture());
|
||||
var first = writes.getAllValues().getFirst();
|
||||
var retry = writes.getAllValues().getLast();
|
||||
assertTrue(setsProperty(first, "completionScore"));
|
||||
assertTrue(setsProperty(retry, "completionScore"));
|
||||
assertTrue(first.getParamNameValuePairs().containsValue(1.0));
|
||||
assertTrue(retry.getParamNameValuePairs().containsValue(0.5));
|
||||
assertTrue(retry.getParamNameValuePairs().containsValue("Still missing: appendix"));
|
||||
}
|
||||
|
||||
// ==================== criteria checklist ====================
|
||||
|
||||
@Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user