fix(agent): gadfly round 1 — user-boundary scan, back-ref precedence, regex legibility
Two behavioral fixes from the review: - modeSummary's backward scan now stops at the most recent user message. With the dwarf ratio rejecting the current turn's 1x-3x answer, the old unbounded scan could walk into WithHistory content and resurrect a stale answer to a DIFFERENT question — strictly worse than keeping the closer (opus, correctness). Other modes keep their historical unbounded scan. - A terminal matching BOTH the ack shape and a back-reference is now classified back-ref: it carries no answer content, so the looser bar is the right one (opus, error-handling). Plus the nits: summaryCloserRe assembled from named fragments, the leading marker class gains '+' (parity with citationLabelRe), verb-first form takes 'all the', dwarf ratio hoisted into one named local, and the 151-vs-153 char/byte comment inaccuracy corrected. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
+58
-2
@@ -73,8 +73,9 @@ func TestIsCitationsOnly(t *testing.T) {
|
||||
}
|
||||
|
||||
// b3cb9ee9Closer is the verbatim terminal turn from mort run b3cb9ee9: a
|
||||
// 2,089-char answer was front-loaded into the cite-call turn and this 151-char
|
||||
// compression was all that got delivered.
|
||||
// 2,089-char answer was front-loaded into the cite-call turn and this 153-byte
|
||||
// compression (151 runes — the em dash is 3 bytes, and byte length is what the
|
||||
// thresholds compare) was all that got delivered.
|
||||
const b3cb9ee9Closer = "Citations are logged. Short version: the bulk of that ~$64M was AIPAC and dark-money super PACs, not the party committees — and it still wasn't enough."
|
||||
|
||||
func TestIsSummaryCloser(t *testing.T) {
|
||||
@@ -91,6 +92,8 @@ func TestIsSummaryCloser(t *testing.T) {
|
||||
{"done-prefix", "Done — citations logged.", true},
|
||||
{"ack-then-tldr", "Sources have been recorded! TL;DR: the GPU was the bottleneck.", true},
|
||||
{"references-noted", "References noted. In short: yes, it ships Tuesday.", true},
|
||||
{"plus-list-marker", "+ Citations are logged.", true},
|
||||
{"logged-all-the", "Logged all the citations.", true},
|
||||
|
||||
{"empty", "", false},
|
||||
{"ack-continues-midsentence", "The citations are recorded in the court transcript, which shows the filing dates.", false},
|
||||
@@ -141,6 +144,9 @@ func TestFinalOutput(t *testing.T) {
|
||||
// A >=200-byte real answer that merely OPENS with a conversational word
|
||||
// ("Sure,"). The preamble filter must NOT veto it (gadfly regression guard).
|
||||
longConversationalAnswer := "Sure, here's the rundown: it currently sells for about $2,700 used on eBay, typically $2,400 to $2,900 depending on condition and bundle, with the sealed Founders Edition commanding the top of that range while used AIB cards go a bit lower."
|
||||
// Matches BOTH the summary ack and backRefRe, within the 120-byte weak cap,
|
||||
// and long enough (>~92 bytes) that longAnswer would fail the summary bar.
|
||||
bothMatchCloser := "Citations are logged. As I mentioned above, the full detail on the money sources is in my earlier message."
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -347,6 +353,56 @@ func TestFinalOutput(t *testing.T) {
|
||||
terminal: b3cb9ee9Closer,
|
||||
want: b3cb9ee9Closer,
|
||||
},
|
||||
{
|
||||
// Gadfly (opus, correctness): the modeSummary scan must stop at the
|
||||
// most recent user message. Here the current turn's answer sits in
|
||||
// the 1x-3x band (rejected by the ratio) while a dwarfing answer to
|
||||
// a DIFFERENT question sits in history — resurrecting it would be
|
||||
// strictly worse than keeping the closer.
|
||||
name: "summary closer never resurrects a stale answer across the user boundary",
|
||||
msgs: []llm.Message{
|
||||
llm.UserText("earlier, unrelated question?"),
|
||||
asst(hugeAnswer),
|
||||
llm.UserText("q?"),
|
||||
asst(conciseAnswer, cite...),
|
||||
llm.ToolResultsMessage(llm.ToolResult{ID: "c1", Name: "cite", Content: "ok"}),
|
||||
asst(b3cb9ee9Closer),
|
||||
},
|
||||
terminal: b3cb9ee9Closer,
|
||||
want: b3cb9ee9Closer,
|
||||
},
|
||||
{
|
||||
// The boundary must not break the legitimate multi-turn case: the
|
||||
// dwarfing front-loaded answer in THIS turn's window is recovered
|
||||
// even with history behind it.
|
||||
name: "summary closer recovery still works with history present",
|
||||
msgs: []llm.Message{
|
||||
llm.UserText("earlier, unrelated question?"),
|
||||
asst(longAnswer),
|
||||
llm.UserText("q?"),
|
||||
asst(hugeAnswer, cite...),
|
||||
llm.ToolResultsMessage(llm.ToolResult{ID: "c1", Name: "cite", Content: "ok"}),
|
||||
asst(b3cb9ee9Closer),
|
||||
},
|
||||
terminal: b3cb9ee9Closer,
|
||||
want: hugeAnswer,
|
||||
},
|
||||
{
|
||||
// Gadfly (opus, error-handling): a closer matching BOTH the ack
|
||||
// shape and a back-reference carries no answer content, so the
|
||||
// back-ref test must win and the ordinary recovery bar apply —
|
||||
// under the summary bar this ~106-byte terminal would demand a
|
||||
// ~318-byte prior and wrongly keep the closer over longAnswer.
|
||||
name: "back-reference wins over the summary ack when both match",
|
||||
msgs: []llm.Message{
|
||||
llm.UserText("q?"),
|
||||
asst(longAnswer, cite...),
|
||||
llm.ToolResultsMessage(llm.ToolResult{ID: "c1", Name: "cite", Content: "ok"}),
|
||||
asst(bothMatchCloser),
|
||||
},
|
||||
terminal: bothMatchCloser,
|
||||
want: longAnswer,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user