File size: 2,577 Bytes
fc88b2a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""
server/handoff_validator.py

Validates structured handoff notes written by the agent at the end of Session 1.
Enforces a 6-section required format. Rejects (not penalises) invalid notes so
the agent can retry within its retry budget.
"""

from dataclasses import dataclass


@dataclass
class ValidationResult:
    valid: bool
    reason: str = ""


class HandoffValidator:
    """
    Validates that the agent's handoff note:
      1. Contains all 6 required section headers.
      2. Does not embed large code blocks (prevents code-dump gaming).
      3. Does not exceed the token budget (prevents padding gaming).
    """

    REQUIRED_SECTIONS = [
        "TASK:",
        "COMPLETED:",
        "REMAINING:",
        "KEY FUNCTIONS:",
        "EDGE CASES:",
        "NEXT STEPS:",
    ]
    MAX_CODE_BLOCK_LINES = 5   # prevents "just paste the code" shortcut
    MAX_TOKENS = 400            # hard ceiling — forces compression

    def validate(self, content: str) -> ValidationResult:
        if not content or not content.strip():
            return ValidationResult(valid=False, reason="Handoff note is empty.")

        for section in self.REQUIRED_SECTIONS:
            if section not in content:
                return ValidationResult(
                    valid=False,
                    reason=f"Missing required section: '{section}'. "
                           f"All required: {self.REQUIRED_SECTIONS}",
                )

        code_lines = self._count_code_block_lines(content)
        if code_lines > self.MAX_CODE_BLOCK_LINES:
            return ValidationResult(
                valid=False,
                reason=(
                    f"Code block too long ({code_lines} lines, max {self.MAX_CODE_BLOCK_LINES}). "
                    "Use function signatures only, not full implementations."
                ),
            )

        token_count = len(content.split())
        if token_count > self.MAX_TOKENS:
            return ValidationResult(
                valid=False,
                reason=(
                    f"Handoff too long ({token_count} tokens, max {self.MAX_TOKENS}). "
                    "Be more concise."
                ),
            )

        return ValidationResult(valid=True)

    def _count_code_block_lines(self, content: str) -> int:
        in_block = False
        count = 0
        for line in content.split("\n"):
            stripped = line.strip()
            if stripped.startswith("```"):
                in_block = not in_block
            elif in_block:
                count += 1
        return count