fix(agent): gadfly round 3 — the pointer must be all there is
CI / Tidy (pull_request) Successful in 9m22s
CI / Build & Test (pull_request) Successful in 9m48s

Round 2 fixed this class on isSummaryCloser and left its twin standing
on isWeakFinal. Three of the four findings are that twin, and every one
of them reproduced:

    "That's the chain above. Ship Tuesday."          weak=true
    "See the summary above. Option B wins."          weak=true
    "Anything above 100 boils. See the note above."  weak=true
    "Anything above, say, 40 degrees is a problem."  weak=true

Each of those is 37-58 bytes with the answer sitting right next to the
pointer, and each was disposable — a >=200-byte earlier turn replaced it
and "Ship Tuesday" went in the bin. pointsAbove asks whether a pointer is
PRESENT; disposability needs it to be ALL THERE IS.

bareAbovePointer cuts the reference's own clause — from the end of the
previous sentence through the match — and requires what remains to be
filler: nothing, punctuation, or a throat-clearing "Done —". Cutting the
clause rather than testing position is what makes the other two shapes
safe for free: a mixed terminal keeps its first sentence, and a
comparative "above," with an interjection keeps the rest of its own.

The fourth finding (kimi) is the same idea one level up:
compressionMarkerRe matched anywhere, so "Given the analysis above, the
bottom line is that we need a different vendor" read as an announced
summary when it is a conclusion. The marker must now OPEN a sentence.

Both classes now enforce one rule from opposite ends: a terminal is
disposable only when it carries no answer of its own — proved in
isWeakFinal by nothing standing beside the pointer, and in
isSummaryCloser by the model declaring what stands beside it a
compression.

One of my own round-1b fixtures had to change: "…is above, so there is
no point repeating it" is a pure pointer to a human, but prose after the
reference is indistinguishable from an answer, so the rule correctly
stops treating it as disposable. Replaced with a genuinely bare 96-byte
pointer, still sized so the two recovery bars disagree about it.

Break-check: thirteen mutations, each killed by a named test, control
survives. M12 initially "passed" by failing to compile — reformulated so
it builds, and TestIsWeakFinal/prepositional-then-deictic-with-content
kills it properly.
This commit is contained in:
2026-08-22 00:16:47 -04:00
parent 3f1c016e74
commit 1756910ef0
2 changed files with 103 additions and 10 deletions
+59 -3
View File
@@ -158,6 +158,46 @@ func pointsAbove(t string) bool {
return loc != nil && loc[0] <= backRefHeadChars
}
// bareAbovePointer reports whether the terminal is a deictic back-reference and
// essentially NOTHING ELSE — the form isWeakFinal may discard outright.
//
// pointsAbove alone is not enough for that, because a pointer can share a short
// terminal with the answer: "That's the chain above. Ship Tuesday." is 37 bytes
// and the decision is the four words the pointer is not. Discarding it in
// favour of an earlier turn throws away the only thing the user needed. Same
// shape as the summary-closer fix — a pointer says where the detail is, it does
// not say the text beside it is disposable.
//
// So the reference's own clause is cut out and whatever remains must be filler:
// nothing, punctuation, or a throat-clearing "Done —". Cutting the CLAUSE (from
// the end of the previous sentence through the reference) rather than testing
// position is also what makes a mixed terminal safe — "Anything above 100
// boils. See the note above." keeps its first sentence and is correctly not
// bare, and a comparative "above," with an interjection after it keeps the rest
// of its own sentence for the same reason.
func bareAbovePointer(t string) bool {
loc := aboveRefRe.FindStringIndex(t)
if loc == nil || loc[0] > backRefHeadChars {
return false
}
start := 0
if k := strings.LastIndexAny(t[:loc[0]], ".!?\n"); k >= 0 {
start = k + 1
}
rest := strings.TrimSpace(t[:start]) + " " + strings.TrimSpace(t[loc[1]:])
return bareRemainderRe.MatchString(strings.Trim(rest, pointerResidueCutset))
}
// pointerResidueCutset is trimmed from both ends of what survives cutting the
// pointer's clause — the brackets, quotes, and punctuation a model wraps a
// back-reference in ("(Already answered above.)").
const pointerResidueCutset = " \t\r\n.,;:!?()[]{}\"'“”‘’*_-—"
// bareRemainderRe matches a remainder that carries no answer: empty, or only
// the filler a closer opens with. Shares fillerWords with summaryPreface so the
// two lists cannot drift.
var bareRemainderRe = regexp.MustCompile(`(?i)^(` + fillerWords + `[\s,.!:—-]*)*$`)
// compressionMarkerRe matches a model announcing that what follows is the
// short form of something longer ("Short version: …", "TL;DR: …", "In short,
// …"). It is the second half of the deictic summary-closer test: the pointer
@@ -173,7 +213,12 @@ func pointsAbove(t string) bool {
// A marker WITHOUT a pointer stays out of scope, as summaryCloserRe's own
// comment explains: a user who asked for brevity is answered with exactly that
// shape.
var compressionMarkerRe = regexp.MustCompile(`(?i)\b(short version|shorter version|short answer|tl;?dr|in short|in brief|in summary|in sum|bottom line|net[- ]net|the gist)\b`)
// The marker must OPEN a sentence. Mid-sentence the same words are ordinary
// prose carrying new content — "Given the analysis above, the bottom line is
// that we need a different vendor" states a conclusion, it does not announce a
// condensation — and treating that as disposable is the very failure the
// two-part test exists to prevent.
var compressionMarkerRe = regexp.MustCompile(`(?i)(^|[.!?:;—]\s*|\n\s*)(short version|shorter version|short answer|tl;?dr|in short|in brief|in summary|in sum|bottom line|net[- ]net|the gist)\b`)
// summaryCloserRe matches a terminal turn that OPENS with a bookkeeping
// acknowledgment of the citation round — "Citations are logged.", "Sources
@@ -190,7 +235,11 @@ var compressionMarkerRe = regexp.MustCompile(`(?i)\b(short version|shorter versi
// behavior (fail closed). Assembled from named fragments so the alternations
// stay legible and extendable.
const (
summaryPreface = `((done|all set|ok(ay)?)[\s,.!:—-]+)?` // optional "Done —" style opener
// fillerWords are the throat-clearing tokens a closer opens with. Shared
// with bareRemainderRe, which has to recognise exactly the same set as
// "not answer content".
fillerWords = `(done|all set|ok(ay)?)`
summaryPreface = `(` + fillerWords + `[\s,.!:—-]+)?` // optional "Done —" style opener
summaryNouns = `(citations?|sources?|references?|claims?)`
// "all" appears here AND in summaryArticle on purpose: as a quantifier
// between noun and verb ("Citations all logged.") and as a determiner
@@ -278,12 +327,19 @@ const (
// cap is what keeps a genuine answer that merely contains a back-reference
// phrase mid-sentence out of the class; past the cap a deferring terminal is
// isSummaryCloser's business, under the stricter dwarf bar.
//
// The deictic half additionally demands that the pointer be the WHOLE message
// (bareAbovePointer), not merely present in it. Both classes now enforce the
// same rule from opposite ends: a terminal is only disposable when it carries
// no answer of its own — proved here by there being nothing beside the pointer,
// and in isSummaryCloser by the model declaring what is beside it a
// compression.
func isWeakFinal(s string) bool {
t := strings.TrimSpace(s)
if t == "" {
return true
}
return len(t) <= weakFinalMaxChars && (backRefRe.MatchString(t) || pointsAbove(t))
return len(t) <= weakFinalMaxChars && (backRefRe.MatchString(t) || bareAbovePointer(t))
}
// isCitationsOnly reports whether a terminal turn is essentially just a