Skip to content
16 changes: 16 additions & 0 deletions src/mcp_as_a_judge/db/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,19 @@ async def get_recent_sessions(self, limit: int = 10) -> list[tuple[str, int]]:
List of tuples: (session_id, last_activity_timestamp), ordered by most recent first
"""
pass

@abstractmethod
async def delete_previous_plan(self, session_id: str) -> int:
"""
Delete all previous judge_coding_plan records except the most recent one.

This method removes all but the last conversation record with source='judge_coding_plan'
for the given session to avoid keeping multiple failed plan attempts.

Args:
session_id: Session identifier

Returns:
Number of records deleted
"""
pass
45 changes: 44 additions & 1 deletion src/mcp_as_a_judge/db/providers/sqlite_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import time
import uuid

from sqlalchemy import create_engine, func
from sqlalchemy import create_engine, delete, func
from sqlmodel import Session, SQLModel, asc, desc, select

from mcp_as_a_judge.core.constants import MAX_CONTEXT_TOKENS
Expand Down Expand Up @@ -303,3 +303,46 @@ async def get_recent_sessions(self, limit: int = 10) -> list[tuple[str, int]]:
results = session.exec(stmt).all()
# results are tuples (session_id, last_activity)
return [(row[0], int(row[1])) for row in results]

async def delete_previous_plan(self, session_id: str) -> int:
"""
Delete all previous judge_coding_plan records except the most recent one.

Uses SQL ORM to find all judge_coding_plan records for the session,
keeps only the most recent one, and deletes the rest.
"""
with Session(self.engine) as session:
# Find all judge_coding_plan records for this session, ordered by timestamp DESC
stmt = (
select(ConversationRecord)
.where(ConversationRecord.session_id == session_id)
.where(ConversationRecord.source == "judge_coding_plan")
.order_by(
desc(ConversationRecord.timestamp),
desc(ConversationRecord.id),
)
)
plan_records = list(session.exec(stmt).all())

if len(plan_records) <= 1:
# No previous plans to delete
logger.info(f"No previous judge_coding_plan records to delete for session {session_id}")
return 0

# Keep the first record (most recent), delete the rest
records_to_delete = plan_records[1:] # Skip the first (most recent)
record_ids_to_delete = [record.id for record in records_to_delete]

logger.info(f"Deleting {len(records_to_delete)} previous judge_coding_plan records for session {session_id}")

# Delete records using SQL IN clause
delete_stmt = delete(ConversationRecord).where(
ConversationRecord.id.in_(record_ids_to_delete)
)
result = session.exec(delete_stmt)
session.commit()

deleted_count = result.rowcount if hasattr(result, 'rowcount') else len(records_to_delete)
logger.info(f"Successfully deleted {deleted_count} previous judge_coding_plan records")

return deleted_count
5 changes: 5 additions & 0 deletions src/mcp_as_a_judge/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,11 @@ async def judge_coding_plan(
# Mark plan as approved for completion validation and update state
updated_task_metadata.mark_plan_approved()
updated_task_metadata.update_state(TaskState.PLAN_APPROVED)

# Delete previous failed plan attempts, keeping only the most recent approved one
deleted_count = await conversation_service.db.delete_previous_plan(updated_task_metadata.task_id)
logger.info(f"Plan approved - deleted {deleted_count} previous plan attempts")

# Force next step to code review implementation gate
workflow_guidance.next_tool = "judge_code_change"
if not workflow_guidance.reasoning:
Expand Down
Loading