From 34ae5004708eabcceab9d03ae355d3dba3bd9688 Mon Sep 17 00:00:00 2001 From: Coder8124 Date: Sat, 3 Oct 2026 09:32:39 -0700 Subject: [PATCH 1/3] Searching for WG-4471, ENG-1234 or 0.4.10 exactly as written finds the note that contains it --- internal/index/hybrid.go | 18 ++++++++++-------- internal/index/hybrid_test.go | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/internal/index/hybrid.go b/internal/index/hybrid.go index 1eb5c4e..8280fa2 100644 --- a/internal/index/hybrid.go +++ b/internal/index/hybrid.go @@ -111,17 +111,19 @@ func (ix *Index) memoryHits(p *provider.Provider, model, query string, k int) [] // ftsQuery turns free text into a safe FTS5 OR-query. FTS5 treats bare // punctuation as syntax, so we keep only alphanumeric tokens and OR them — // recall-oriented, since fusion with vectors sorts out precision. +// +// Punctuation inside a word splits it into a phrase rather than being dropped. +// The tokenizer indexed WG-4471 as wg then 4471, so gluing the query into +// "WG4471" matched nothing; the phrase "WG 4471" matches the identifier as a +// unit, and not a note that only has wg and 4471 apart. func ftsQuery(q string) string { var tokens []string for _, f := range strings.Fields(q) { - var b strings.Builder - for _, r := range f { - if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' { - b.WriteRune(r) - } - } - if b.Len() > 1 { - tokens = append(tokens, `"`+b.String()+`"`) + pieces := strings.FieldsFunc(f, func(r rune) bool { + return !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9') + }) + if len(strings.Join(pieces, "")) > 1 { + tokens = append(tokens, `"`+strings.Join(pieces, " ")+`"`) } } return strings.Join(tokens, " OR ") diff --git a/internal/index/hybrid_test.go b/internal/index/hybrid_test.go index e80f4fc..b70448f 100644 --- a/internal/index/hybrid_test.go +++ b/internal/index/hybrid_test.go @@ -151,3 +151,22 @@ func contains(s, sub string) bool { } return false } + +// The tokenizer splits WG-4471 into wg and 4471. A query that glued the pieces +// back into "WG4471" matched neither, so the identifiers a user types to find +// an exact note — part numbers, ticket ids, versions, kebab-case names — were +// the ones the lexical arm could not find. +func TestAnIdentifierWithPunctuationIsFoundExactlyAsWritten(t *testing.T) { + ix := newTestIndex(t) + seed(t, ix, "quote", "Quote", "part WG-4471 ships in ENG-1234, fixed in 0.4.10 on auto-extract") + seed(t, ix, "other", "Other", "wg 9 and 4471 apart, 1234 before eng") + for _, q := range []string{"WG-4471", "ENG-1234", "0.4.10", "auto-extract", "where is WG-4471?"} { + got, err := ix.lexical(q, 5) + if err != nil { + t.Fatal(err) + } + if len(got) == 0 || got[0] != "quote" { + t.Errorf("lexical(%q) = %v, want the note that contains it first", q, got) + } + } +} From 30b11a6d7c820c756c38344485fe0be8cb2f2042 Mon Sep 17 00:00:00 2001 From: Coder8124 Date: Sat, 3 Oct 2026 17:39:00 -0700 Subject: [PATCH 2/3] A file edit that succeeded is no longer accepted as proof that a distilled claim was verified --- internal/ingest/distil.go | 18 ++++++++++++------ internal/ingest/distil_test.go | 26 ++++++++++++++++++++++++++ internal/mcpserver/tools.go | 2 +- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/internal/ingest/distil.go b/internal/ingest/distil.go index f8d8ff0..60a7bb1 100644 --- a/internal/ingest/distil.go +++ b/internal/ingest/distil.go @@ -90,8 +90,8 @@ func citedTurns(claim string) []int { // - Every claim names a turn. No turn number, no entry. // - The turn must exist and must have been served: a citation to an abridged // turn is a citation to evidence the distiller never saw. -// - A `verified` entry additionally needs an observed successful tool result -// in one of its cited turns. The distiller may summarise evidence; it may +// - A `verified` entry additionally needs an observed successful command in +// one of its cited turns. The distiller may summarise evidence; it may // not supply it. "The build passes" backed only by an assistant saying so is // the exact claim that makes the next agent skip running the build. // @@ -126,7 +126,13 @@ func Filter(ev Evidence, d Distillation) (Distillation, []Drop) { } ok = true t := ev.Turns[n-1] - if t.Role == "tool" && t.Status == "ok" { + // Only a command that ran demonstrates anything. A successful + // Edit proves the file was written, and "the crash is gone" + // citing one was kept as verified. Matching the shell names + // rather than excluding the edit names fails safe: a tool not + // on the list costs a claim, a missed edit name would let one + // through. + if t.Role == "tool" && t.Status == "ok" && isShellTool(t.Tool) { sawSuccess = true } } @@ -135,7 +141,7 @@ func Filter(ev Evidence, d Distillation) (Distillation, []Drop) { continue } if needSuccess && !sawSuccess { - drops = append(drops, Drop{field, claim, "cites no turn holding a successful command or tool result"}) + drops = append(drops, Drop{field, claim, "cites no turn holding a command that ran successfully — a file edit or read is not a demonstration"}) continue } kept = append(kept, claim) @@ -295,14 +301,14 @@ func (ev Evidence) Render() string { if ev.Record != "" { fmt.Fprintf(&b, "Distil this into verified / failed / decided and send it back with ingest_distil, session %q.\n", ev.Record) b.WriteString("Every verified, failed and decided entry must name the turn it came from, as \"turn 12\".\n") - b.WriteString("A verified entry needs a successful command or tool result in the turn it cites;\n") + b.WriteString("A verified entry needs a command that ran successfully in the turn it cites — a file edit is not one;\n") b.WriteString("an assistant saying something worked is not an observation of it working.\n") b.WriteString("Uncited entries are dropped before anything is written, and what is kept is shown as inferred, never as the session's own record.\n") return b.String() } b.WriteString("Distil this into verified / failed / next and send it back with ingest_distil.\n") b.WriteString("Every verified and failed entry must name the turn it came from, as \"turn 12\".\n") - b.WriteString("A verified entry needs a successful command or tool result in the turn it cites;\n") + b.WriteString("A verified entry needs a command that ran successfully in the turn it cites — a file edit is not one;\n") b.WriteString("an assistant saying something worked is not an observation of it working.\n") b.WriteString("Uncited entries are dropped before anything is written.\n") return b.String() diff --git a/internal/ingest/distil_test.go b/internal/ingest/distil_test.go index 88b01c0..0395193 100644 --- a/internal/ingest/distil_test.go +++ b/internal/ingest/distil_test.go @@ -333,3 +333,29 @@ func TestAcceptValidatesAgainstTheWindowThatWasServed(t *testing.T) { t.Fatalf("a claim about an unshown turn was written: failed=%v drops=%+v", c.Failed, drops) } } + +// A file edit that succeeded proves the file was written, not that anything it +// changed works. A real distillation kept "the crash is gone (turn 2)" where +// turn 2 was a Claude Code Edit with status ok, and the next agent would have +// read that as a demonstration and skipped the test run that was never made. +func TestAVerifiedClaimCitingOnlyASuccessfulFileEditIsDropped(t *testing.T) { + ev := ingest.Evidence{ + Turns: []transcript.Turn{ + {Role: "user", Text: "fix the crash on an empty cart"}, + {Role: "tool", Tool: "Edit", Input: "internal/cart/cart.go", Status: "ok"}, + {Role: "tool", Tool: "Bash", Input: "go test ./internal/cart", Status: "ok"}, + }, + Served: map[int]bool{1: true, 2: true, 3: true}, + } + + got, drops := ingest.Filter(ev, ingest.Distillation{ + Verified: []string{"the crash is gone (turn 2)", "the cart tests pass (turn 3)"}, + }) + + if len(got.Verified) != 1 || !strings.Contains(got.Verified[0], "turn 3") { + t.Fatalf("verified = %v, want only the claim citing the command that ran", got.Verified) + } + if len(drops) != 1 || !strings.Contains(drops[0].Claim, "turn 2") { + t.Fatalf("drops = %+v, want the edit-only claim reported", drops) + } +} diff --git a/internal/mcpserver/tools.go b/internal/mcpserver/tools.go index 94db771..aaa811d 100644 --- a/internal/mcpserver/tools.go +++ b/internal/mcpserver/tools.go @@ -238,7 +238,7 @@ var toolDefs = []map[string]any{ { "name": "ingest_distil", "annotations": writes(false, true), - "description": "Send back your distillation of a session served by ingest_harvest: what was verified, what didn't work, what's next. Every verified and failed entry must name the turn it came from (\"turn 12\"), and a verified entry needs a successful command or tool result in that turn — uncited or unsupported entries are dropped and reported. For a queued session it writes a candidate for the user to review, never a checkpoint; for an auto record it writes verified, failed and decided into that record as inferred from the transcript, shown apart from what an agent stated." + relay, + "description": "Send back your distillation of a session served by ingest_harvest: what was verified, what didn't work, what's next. Every verified and failed entry must name the turn it came from (\"turn 12\"), and a verified entry needs a command that ran successfully in that turn (a file edit is not one) — uncited or unsupported entries are dropped and reported. For a queued session it writes a candidate for the user to review, never a checkpoint; for an auto record it writes verified, failed and decided into that record as inferred from the transcript, shown apart from what an agent stated." + relay, "inputSchema": obj(map[string]any{ "session": str("the session id or auto record path you were served"), "verified": arrStr("what the session actually established, each entry citing its turn, e.g. 'the suite passes after the region fix (turn 14)'"), From e990ed3fa4c7d063d6250e67a67858dc3a1c7978 Mon Sep 17 00:00:00 2001 From: Coder8124 Date: Sat, 3 Oct 2026 17:39:00 -0700 Subject: [PATCH 3/3] A package or directory a command names is no longer listed as a file the session touched --- internal/ingest/harvest.go | 6 +++++- internal/ingest/ingest_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/internal/ingest/harvest.go b/internal/ingest/harvest.go index c56dcec..a8bc6ca 100644 --- a/internal/ingest/harvest.go +++ b/internal/ingest/harvest.go @@ -121,7 +121,11 @@ func filesInCommand(cmd string) []string { if strings.HasPrefix(tok, "-") { continue } - if !strings.Contains(tok, "/") && !hasSourceExt(tok) { + // A slash alone is not a file: `go test ./cart`, `cd /repo` and a sed + // expression all have one and name a package, a directory and a + // substitution. A file at the end of a path has an extension; one + // without is lost here, which costs a bullet the command line still shows. + if !hasSourceExt(tok) && !(strings.Contains(tok, "/") && len(path.Ext(tok)) > 1) { continue } if strings.ContainsAny(tok, "*?$`") { diff --git a/internal/ingest/ingest_test.go b/internal/ingest/ingest_test.go index 6e697c9..fd0f497 100644 --- a/internal/ingest/ingest_test.go +++ b/internal/ingest/ingest_test.go @@ -425,3 +425,22 @@ func TestACandidateWhoseTimestampCannotBeReadSaysSoInsteadOfLookingHealthy(t *te t.Error("a candidate with no timestamps at all was reported as damaged") } } + +// `go test ./cart` names a package, not a file. Taking every argument with a +// slash in it as a file put "cart" in a record's Files beside the cart.go that +// was actually edited, and the next agent reads Files as what was touched. +func TestAPackageArgumentToACommandIsNotListedAsATouchedFile(t *testing.T) { + c := ingest.Harvest(&transcript.Session{ + Harness: "claude-code", + ID: "s", + Turns: []transcript.Turn{ + {Role: "tool", Tool: "Edit", Input: "/Users/alice/shop/cart/cart.go", Status: "ok"}, + {Role: "tool", Tool: "Bash", Input: "cd /Users/alice/shop && go test ./cart ./internal/... && sed -i '' s/a/b/ cart/notes.md", Status: "ok"}, + }, + }) + + want := []string{"/Users/alice/shop/cart/cart.go", "cart/notes.md"} + if strings.Join(c.Files, " ") != strings.Join(want, " ") { + t.Fatalf("files = %v, want %v — only what a command names as a file", c.Files, want) + } +}