Files
SuperBizAgent-java/src/test/java/com/superbiz/agent/harness/progress/DiagnosisProgressTrackerTest.java
T
zhuyongxin 38f781b157 feat(harness): complete protocol repair stop and archive ISS-016
Add repairable INVALID_PROGRESS_PROTOCOL observations, independent
PROGRESS_PROTOCOL_VIOLATED saturation, and controlled release paths.
Archive the OpenSpec change after syncing main specs and devflow.
2026-07-27 19:10:07 +08:00

115 lines
5.3 KiB
Java

package com.superbiz.agent.harness.progress;
import com.superbiz.agent.harness.contract.EvidenceStatus;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class DiagnosisProgressTrackerTest {
@Test
void gainedResetsConsecutiveNoGainAndConsumesPendingCallOnce() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(2);
tracker.recordDuplicateScope();
tracker.recordCompleted(call("call-1", "scope-1"), EvidenceStatus.EVIDENCE_FOUND);
tracker.applyPreviousObservation(new PreviousObservation("call-1", InformationGain.GAINED));
DiagnosisProgressSnapshotState state = tracker.snapshot();
assertEquals(0, state.consecutiveNoGain());
assertEquals(DiagnosisCollectionState.COLLECTING, state.collectionState());
assertThrows(IllegalArgumentException.class, () -> tracker.applyPreviousObservation(
new PreviousObservation("call-1", InformationGain.NO_GAIN)));
}
@Test
void rejectsMissingOrOutOfOrderEvaluation() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(2);
tracker.recordCompleted(call("call-1", "scope-1"), EvidenceStatus.EVIDENCE_FOUND);
assertThrows(IllegalArgumentException.class, () -> tracker.applyPreviousObservation(null));
assertThrows(IllegalArgumentException.class, () -> tracker.applyPreviousObservation(
new PreviousObservation("call-other", InformationGain.GAINED)));
assertEquals("call-1", tracker.snapshot().pendingToolCallId());
}
@Test
void deterministicNoEvidenceSaturatesAndStopInstructionIsClaimedOnce() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(2);
tracker.recordCompleted(call("call-1", "scope-1"), EvidenceStatus.NO_EVIDENCE);
tracker.recordCompleted(call("call-2", "scope-2"), EvidenceStatus.NO_EVIDENCE);
DiagnosisProgressSnapshotState state = tracker.snapshot();
assertEquals(DiagnosisCollectionState.SATURATED, state.collectionState());
assertEquals(DiagnosisStopReason.INFORMATION_SATURATED, state.stopReason());
assertTrue(tracker.claimStopInstruction());
assertFalse(tracker.claimStopInstruction());
}
@Test
void onlySuccessfulCompletedScopesParticipateInDuplicateDetection() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(3);
assertFalse(tracker.isDuplicate("lookup_knowledge", "scope-1"));
tracker.recordCompleted(call("call-1", "scope-1"), EvidenceStatus.EVIDENCE_FOUND);
assertTrue(tracker.isDuplicate("lookup_knowledge", "scope-1"));
assertFalse(tracker.isDuplicate("query_logs", "scope-1"));
assertFalse(tracker.isDuplicate("lookup_knowledge", "scope-2"));
}
@Test
void progressProtocolViolationsAccumulateIndependentlyFromNoGain() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(3, 2);
tracker.recordDuplicateScope();
DiagnosisProgressSnapshotState first = tracker.recordProgressProtocolViolation();
assertEquals(1, first.consecutiveProgressProtocolViolations());
assertEquals(1, first.consecutiveNoGain());
assertEquals(DiagnosisCollectionState.COLLECTING, first.collectionState());
DiagnosisProgressSnapshotState second = tracker.recordProgressProtocolViolation();
assertEquals(2, second.consecutiveProgressProtocolViolations());
assertEquals(1, second.consecutiveNoGain());
assertEquals(DiagnosisCollectionState.SATURATED, second.collectionState());
assertEquals(DiagnosisStopReason.PROGRESS_PROTOCOL_VIOLATED, second.stopReason());
assertTrue(tracker.claimStopInstruction());
assertFalse(tracker.claimStopInstruction());
}
@Test
void validEvaluationClearsProtocolViolationCount() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(2, 3);
tracker.recordCompleted(call("call-1", "scope-1"), EvidenceStatus.EVIDENCE_FOUND);
tracker.recordProgressProtocolViolation();
tracker.applyPreviousObservation(new PreviousObservation("call-1", InformationGain.GAINED));
DiagnosisProgressSnapshotState state = tracker.snapshot();
assertEquals(0, state.consecutiveProgressProtocolViolations());
assertEquals(0, state.consecutiveNoGain());
assertEquals(DiagnosisCollectionState.COLLECTING, state.collectionState());
}
@Test
void rejectsUnexpectedPreviousObservationWithTypedViolation() {
DiagnosisProgressTracker tracker = new DiagnosisProgressTracker(2, 2);
ProgressProtocolViolationException failure = assertThrows(
ProgressProtocolViolationException.class,
() -> tracker.applyPreviousObservation(
new PreviousObservation("call-x", InformationGain.GAINED)));
assertEquals(ProgressProtocolViolationType.UNEXPECTED_PREVIOUS_OBSERVATION,
failure.violationType());
assertEquals("previous_observation", failure.missingField());
}
private CompletedToolCall call(String id, String scope) {
return new CompletedToolCall(id, "lookup_knowledge", scope);
}
}