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.
This commit is contained in:
@@ -10,35 +10,63 @@ import java.util.Set;
|
||||
public final class DiagnosisProgressTracker {
|
||||
|
||||
private final int stopAfterConsecutiveNoGain;
|
||||
private final int stopAfterConsecutiveProgressProtocolViolations;
|
||||
private final Set<ToolScopeIdentity> completedScopes = new LinkedHashSet<>();
|
||||
private final List<CompletedToolCall> completedToolCalls = new ArrayList<>();
|
||||
private int consecutiveNoGain;
|
||||
private int consecutiveProgressProtocolViolations;
|
||||
private DiagnosisCollectionState collectionState = DiagnosisCollectionState.COLLECTING;
|
||||
private DiagnosisStopReason stopReason;
|
||||
private String pendingToolCallId;
|
||||
private boolean stopInstructionDelivered;
|
||||
|
||||
public DiagnosisProgressTracker(int stopAfterConsecutiveNoGain) {
|
||||
this(stopAfterConsecutiveNoGain, 2);
|
||||
}
|
||||
|
||||
public DiagnosisProgressTracker(
|
||||
int stopAfterConsecutiveNoGain,
|
||||
int stopAfterConsecutiveProgressProtocolViolations) {
|
||||
if (stopAfterConsecutiveNoGain <= 0) {
|
||||
throw new IllegalArgumentException("stopAfterConsecutiveNoGain must be positive");
|
||||
}
|
||||
if (stopAfterConsecutiveProgressProtocolViolations <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"stopAfterConsecutiveProgressProtocolViolations must be positive");
|
||||
}
|
||||
this.stopAfterConsecutiveNoGain = stopAfterConsecutiveNoGain;
|
||||
this.stopAfterConsecutiveProgressProtocolViolations =
|
||||
stopAfterConsecutiveProgressProtocolViolations;
|
||||
}
|
||||
|
||||
public synchronized void applyPreviousObservation(PreviousObservation observation) {
|
||||
if (pendingToolCallId == null) {
|
||||
if (observation != null) {
|
||||
throw new IllegalArgumentException("No Tool observation is pending evaluation");
|
||||
throw new ProgressProtocolViolationException(
|
||||
ProgressProtocolViolationType.UNEXPECTED_PREVIOUS_OBSERVATION,
|
||||
"No Tool observation is pending evaluation",
|
||||
"previous_observation",
|
||||
null);
|
||||
}
|
||||
clearProtocolViolations();
|
||||
return;
|
||||
}
|
||||
if (observation == null) {
|
||||
throw new IllegalArgumentException("Previous Tool observation must be evaluated");
|
||||
throw new ProgressProtocolViolationException(
|
||||
ProgressProtocolViolationType.MISSING_PREVIOUS_OBSERVATION,
|
||||
"Previous Tool observation must be evaluated",
|
||||
"previous_observation",
|
||||
pendingToolCallId);
|
||||
}
|
||||
if (!pendingToolCallId.equals(observation.toolCallId())) {
|
||||
throw new IllegalArgumentException("Previous Tool observation ID is out of order");
|
||||
throw new ProgressProtocolViolationException(
|
||||
ProgressProtocolViolationType.OUT_OF_ORDER_PREVIOUS_OBSERVATION,
|
||||
"Previous Tool observation ID is out of order",
|
||||
"previous_observation.tool_call_id",
|
||||
pendingToolCallId);
|
||||
}
|
||||
pendingToolCallId = null;
|
||||
clearProtocolViolations();
|
||||
applyGain(observation.informationGain());
|
||||
}
|
||||
|
||||
@@ -47,6 +75,7 @@ public final class DiagnosisProgressTracker {
|
||||
}
|
||||
|
||||
public synchronized void recordDuplicateScope() {
|
||||
clearProtocolViolations();
|
||||
applyGain(InformationGain.NO_GAIN);
|
||||
}
|
||||
|
||||
@@ -64,12 +93,26 @@ public final class DiagnosisProgressTracker {
|
||||
}
|
||||
completedToolCalls.add(call);
|
||||
if (evidenceStatus == EvidenceStatus.NO_EVIDENCE) {
|
||||
clearProtocolViolations();
|
||||
applyGain(InformationGain.NO_GAIN);
|
||||
} else {
|
||||
pendingToolCallId = call.toolCallId();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized DiagnosisProgressSnapshotState recordProgressProtocolViolation() {
|
||||
if (collectionState == DiagnosisCollectionState.SATURATED) {
|
||||
return snapshot();
|
||||
}
|
||||
consecutiveProgressProtocolViolations++;
|
||||
if (consecutiveProgressProtocolViolations
|
||||
>= stopAfterConsecutiveProgressProtocolViolations) {
|
||||
collectionState = DiagnosisCollectionState.SATURATED;
|
||||
stopReason = DiagnosisStopReason.PROGRESS_PROTOCOL_VIOLATED;
|
||||
}
|
||||
return snapshot();
|
||||
}
|
||||
|
||||
public synchronized boolean claimStopInstruction() {
|
||||
if (collectionState != DiagnosisCollectionState.SATURATED) {
|
||||
return false;
|
||||
@@ -90,6 +133,7 @@ public final class DiagnosisProgressTracker {
|
||||
public synchronized DiagnosisProgressSnapshotState snapshot() {
|
||||
return new DiagnosisProgressSnapshotState(
|
||||
consecutiveNoGain,
|
||||
consecutiveProgressProtocolViolations,
|
||||
collectionState,
|
||||
stopReason,
|
||||
pendingToolCallId,
|
||||
@@ -101,6 +145,10 @@ public final class DiagnosisProgressTracker {
|
||||
return stopAfterConsecutiveNoGain;
|
||||
}
|
||||
|
||||
public int stopAfterConsecutiveProgressProtocolViolations() {
|
||||
return stopAfterConsecutiveProgressProtocolViolations;
|
||||
}
|
||||
|
||||
private void applyGain(InformationGain gain) {
|
||||
if (collectionState == DiagnosisCollectionState.SATURATED) {
|
||||
throw new IllegalStateException("Collection is already saturated");
|
||||
@@ -115,4 +163,8 @@ public final class DiagnosisProgressTracker {
|
||||
stopReason = DiagnosisStopReason.INFORMATION_SATURATED;
|
||||
}
|
||||
}
|
||||
|
||||
private void clearProtocolViolations() {
|
||||
consecutiveProgressProtocolViolations = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user