mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-14 03:33:43 +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())) {
|
||||
// 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);
|
||||
setChecklistProgress(w, current);
|
||||
} else {
|
||||
w.set(GoalEntity::getCompletionScore, result.score())
|
||||
.set(GoalEntity::getProgressSummary, result.gap());
|
||||
@ -675,6 +670,7 @@ public class GoalServiceImpl implements GoalService {
|
||||
LambdaUpdateWrapper<GoalEntity> w = baseLockedUpdate(fresh)
|
||||
.set(GoalEntity::getCriteria, criteriaJson)
|
||||
.set(GoalEntity::getExitCriteria, mergedText);
|
||||
setChecklistProgress(w, list);
|
||||
bumpVersionAndTime(w);
|
||||
return w;
|
||||
});
|
||||
@ -688,6 +684,16 @@ public class GoalServiceImpl implements GoalService {
|
||||
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 ====================
|
||||
|
||||
/**
|
||||
|
||||
@ -238,6 +238,24 @@ class GoalPersistenceIntegrationTest {
|
||||
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
|
||||
void rolledBackCompletionDoesNotSyncMemory() {
|
||||
GoalEntity goal = readyForCompletion("completion-memory-rollback", "report");
|
||||
|
||||
@ -627,6 +627,28 @@ class GoalServiceTest {
|
||||
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
|
||||
void appendCriterion_rejectsBlankInput() {
|
||||
// Validation happens before selectById, so we do NOT stub the mapper.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user