mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 03:55:09 +08:00
fix(goal): refresh progress when requirements are appended
This commit is contained in:
parent
47066c6091
commit
0498530f3a
@ -549,12 +549,7 @@ public class GoalServiceImpl implements GoalService {
|
|||||||
if (!current.isEmpty() && !GoalEvaluationResult.DECISION_FALLBACK.equals(result.decision())) {
|
if (!current.isEmpty() && !GoalEvaluationResult.DECISION_FALLBACK.equals(result.decision())) {
|
||||||
// The model may have seen fewer criteria. Derive the public
|
// The model may have seen fewer criteria. Derive the public
|
||||||
// projection from the same fresh checklist this CAS writes.
|
// projection from the same fresh checklist this CAS writes.
|
||||||
List<GoalCriterion> remaining = GoalCriteriaCodec.remaining(current);
|
setChecklistProgress(w, 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 {
|
} else {
|
||||||
w.set(GoalEntity::getCompletionScore, result.score())
|
w.set(GoalEntity::getCompletionScore, result.score())
|
||||||
.set(GoalEntity::getProgressSummary, result.gap());
|
.set(GoalEntity::getProgressSummary, result.gap());
|
||||||
@ -675,6 +670,7 @@ public class GoalServiceImpl implements GoalService {
|
|||||||
LambdaUpdateWrapper<GoalEntity> w = baseLockedUpdate(fresh)
|
LambdaUpdateWrapper<GoalEntity> w = baseLockedUpdate(fresh)
|
||||||
.set(GoalEntity::getCriteria, criteriaJson)
|
.set(GoalEntity::getCriteria, criteriaJson)
|
||||||
.set(GoalEntity::getExitCriteria, mergedText);
|
.set(GoalEntity::getExitCriteria, mergedText);
|
||||||
|
setChecklistProgress(w, list);
|
||||||
bumpVersionAndTime(w);
|
bumpVersionAndTime(w);
|
||||||
return w;
|
return w;
|
||||||
});
|
});
|
||||||
@ -688,6 +684,16 @@ public class GoalServiceImpl implements GoalService {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Both evaluation and user edits project the checklist written by this CAS. */
|
||||||
|
private static void setChecklistProgress(LambdaUpdateWrapper<GoalEntity> update, List<GoalCriterion> criteria) {
|
||||||
|
List<GoalCriterion> remaining = GoalCriteriaCodec.remaining(criteria);
|
||||||
|
double score = criteria.isEmpty() ? 0.0 : (double) (criteria.size() - remaining.size()) / criteria.size();
|
||||||
|
String gap = remaining.isEmpty() ? "" : "Still missing: " + remaining.stream()
|
||||||
|
.map(GoalCriterion::text).collect(java.util.stream.Collectors.joining("; "));
|
||||||
|
update.set(GoalEntity::getCompletionScore, score)
|
||||||
|
.set(GoalEntity::getProgressSummary, gap);
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Internals ====================
|
// ==================== Internals ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -238,6 +238,24 @@ class GoalPersistenceIntegrationTest {
|
|||||||
return goalService.getById(goal.getId());
|
return goalService.getById(goal.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appendedRequirementImmediatelyRefreshesProgressWithoutLosingPriorEvidence() {
|
||||||
|
GoalEntity goal = readyForCompletion("append-progress", "report");
|
||||||
|
assertEquals(1.0, goal.getCompletionScore());
|
||||||
|
GoalEntity appended = goalService.appendCriterion(goal.getId(), "appendix", "alice");
|
||||||
|
assertEquals(0.5, appended.getCompletionScore());
|
||||||
|
assertEquals("Still missing: appendix", appended.getProgressSummary());
|
||||||
|
var criteria = vip.mate.goal.model.GoalCriteriaCodec.parse(appended.getCriteria(), new com.fasterxml.jackson.databind.ObjectMapper());
|
||||||
|
assertEquals(true, criteria.getFirst().passed());
|
||||||
|
assertEquals("report evidence", criteria.getFirst().evidence());
|
||||||
|
assertEquals(false, criteria.getLast().passed());
|
||||||
|
assertEquals(goal.getEvaluationRevision(), appended.getEvaluationRevision());
|
||||||
|
GoalEntity again = goalService.appendCriterion(goal.getId(), "sources", "alice");
|
||||||
|
assertEquals(1.0 / 3, again.getCompletionScore(), 0.00001);
|
||||||
|
assertEquals("Still missing: appendix; sources", again.getProgressSummary());
|
||||||
|
assertEquals(GoalStatus.ACTIVE, goalService.getById(goal.getId()).getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rolledBackCompletionDoesNotSyncMemory() {
|
void rolledBackCompletionDoesNotSyncMemory() {
|
||||||
GoalEntity goal = readyForCompletion("completion-memory-rollback", "report");
|
GoalEntity goal = readyForCompletion("completion-memory-rollback", "report");
|
||||||
|
|||||||
@ -627,6 +627,28 @@ class GoalServiceTest {
|
|||||||
assertTrue(evCaptor.getValue().getDetailJson().contains("tests pass"));
|
assertTrue(evCaptor.getValue().getDetailJson().contains("tests pass"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void appendProgressUsesFreshChecklistAfterCasConflict() {
|
||||||
|
GoalEntity original = persisted(1L, GoalStatus.ACTIVE);
|
||||||
|
original.setCriteria("[{\"id\":\"C1\",\"text\":\"report\",\"passed\":true,\"evidence\":\"report written\"}]");
|
||||||
|
GoalEntity fresh = persisted(1L, GoalStatus.ACTIVE);
|
||||||
|
fresh.setVersion(1);
|
||||||
|
fresh.setCriteria("[{\"id\":\"C1\",\"text\":\"report\",\"passed\":true,\"evidence\":\"report written\"},"
|
||||||
|
+ "{\"id\":\"C2\",\"text\":\"sources\",\"passed\":false,\"evidence\":\"\"}]");
|
||||||
|
when(goalMapper.selectById(1L)).thenReturn(original, fresh, fresh);
|
||||||
|
when(goalMapper.update(any(), any(LambdaUpdateWrapper.class))).thenReturn(0, 1);
|
||||||
|
service.appendCriterion(1L, "appendix", "alice");
|
||||||
|
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(0.5));
|
||||||
|
assertTrue(retry.getParamNameValuePairs().containsValue(1.0 / 3));
|
||||||
|
assertTrue(retry.getParamNameValuePairs().containsValue("Still missing: sources; appendix"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void appendCriterion_rejectsBlankInput() {
|
void appendCriterion_rejectsBlankInput() {
|
||||||
// Validation happens before selectById, so we do NOT stub the mapper.
|
// Validation happens before selectById, so we do NOT stub the mapper.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user