@@ -253,6 +253,119 @@ describe("verifyCompactionSummary", () => {
253253 true ,
254254 ) ;
255255 } ) ;
256+
257+ test ( "dotted names are whole tokens so suffixes and prefixes do not cover" , ( ) => {
258+ const cases : {
259+ tool : string ;
260+ args : Record < string , string > ;
261+ summary : string ;
262+ detail : string ;
263+ } [ ] = [
264+ {
265+ tool : "read_file" ,
266+ args : { path : "src/config.ts" } ,
267+ summary : "Fix auth now. Updated vite.config.ts." ,
268+ detail : "src/config.ts" ,
269+ } ,
270+ {
271+ tool : "read_file" ,
272+ args : { path : "src/test.ts" } ,
273+ summary : "Fix auth now. Updated auth.test.ts." ,
274+ detail : "src/test.ts" ,
275+ } ,
276+ {
277+ tool : "read_file" ,
278+ args : { path : "src/auth.ts" } ,
279+ summary : "Fix auth now. Updated foo.auth.ts." ,
280+ detail : "src/auth.ts" ,
281+ } ,
282+ {
283+ tool : "read_file" ,
284+ args : { path : "src/auth.ts" } ,
285+ summary : "Fix auth now. Kept auth.ts.bak." ,
286+ detail : "src/auth.ts" ,
287+ } ,
288+ {
289+ tool : "web_fetch" ,
290+ args : { url : "https://api.com/v1" } ,
291+ summary : "Fix auth now. Called www.api.com." ,
292+ detail : "https://api.com/v1" ,
293+ } ,
294+ ] ;
295+ for ( const { tool, args, summary, detail } of cases ) {
296+ const facts = extractContinuationFacts ( [
297+ textTurn ( "user" , "Fix auth now" ) ,
298+ {
299+ role : "assistant" ,
300+ content : [
301+ {
302+ type : "tool_call" ,
303+ id : "c1" ,
304+ name : tool ,
305+ arguments : args ,
306+ } ,
307+ ] ,
308+ timestamp : 2 ,
309+ } ,
310+ ] ) ;
311+ const report = verifyCompactionSummary ( summary , facts ) ;
312+ expect ( report . misses . some ( ( m ) => m . kind === "exactName" ) ) . toBe ( true ) ;
313+ expect ( report . misses . some ( ( m ) => m . detail === detail ) ) . toBe ( true ) ;
314+ }
315+ } ) ;
316+
317+ test ( "basename in a path still covers the exact name" , ( ) => {
318+ const facts = extractContinuationFacts ( [
319+ textTurn ( "user" , "Fix auth now" ) ,
320+ {
321+ role : "assistant" ,
322+ content : [
323+ {
324+ type : "tool_call" ,
325+ id : "c1" ,
326+ name : "read_file" ,
327+ arguments : { path : "src/auth.ts" } ,
328+ } ,
329+ ] ,
330+ timestamp : 2 ,
331+ } ,
332+ ] ) ;
333+ const report = verifyCompactionSummary (
334+ "Fix auth now. Read auth.ts next." ,
335+ facts ,
336+ ) ;
337+ expect ( report . misses . some ( ( m ) => m . kind === "exactName" ) ) . toBe ( false ) ;
338+ } ) ;
339+
340+ test ( "uppercase HTTP URL scores hostname case-insensitively" , ( ) => {
341+ const facts = extractContinuationFacts ( [
342+ textTurn ( "user" , "Fix auth now" ) ,
343+ {
344+ role : "assistant" ,
345+ content : [
346+ {
347+ type : "tool_call" ,
348+ id : "c1" ,
349+ name : "web_fetch" ,
350+ arguments : { url : "HTTP://API.COM/v1" } ,
351+ } ,
352+ ] ,
353+ timestamp : 2 ,
354+ } ,
355+ ] ) ;
356+ expect (
357+ verifyCompactionSummary (
358+ "Fix auth now. Called api.com." ,
359+ facts ,
360+ ) . misses . some ( ( m ) => m . kind === "exactName" ) ,
361+ ) . toBe ( false ) ;
362+ expect (
363+ verifyCompactionSummary (
364+ "Fix auth now. Hit endpoint v1." ,
365+ facts ,
366+ ) . misses . some ( ( m ) => m . kind === "exactName" ) ,
367+ ) . toBe ( true ) ;
368+ } ) ;
256369} ) ;
257370
258371describe ( "verifyOrRepair" , ( ) => {
@@ -360,7 +473,6 @@ describe("verifyOrRepair", () => {
360473 "Always keep the public API stable." ,
361474 ] ,
362475 nextAction : "Fix the token refresh assertion next." ,
363- state : "Fix the token refresh assertion next." ,
364476 verification : [ "bun run test auth" , "bun run check" ] ,
365477 blockers : [ "token refresh assertion failed" , "ECONNREFUSED on staging" ] ,
366478 exactNames : [ "src/auth.ts" , "packages/runtime/config.json" ] ,
@@ -389,7 +501,6 @@ describe("verifyOrRepair", () => {
389501 expect ( repair ) . not . toContain ( "src/auth.ts" ) ;
390502 expect ( repair ) . not . toContain ( "Goal:" ) ;
391503 expect ( repair ) . not . toContain ( "Next:" ) ;
392- expect ( repair ) . not . toMatch ( / ^ S t a t e : / m) ;
393504 } ) ;
394505
395506 test ( "repair lines are goal, next, exact names, then the rest" , ( ) => {
@@ -408,26 +519,6 @@ describe("verifyOrRepair", () => {
408519 expect ( ranAt ) . toBeGreaterThan ( blockersAt ) ;
409520 } ) ;
410521
411- test ( "repair does not emit a State line even when state is independent" , ( ) => {
412- const facts = {
413- goal : "Migrate the auth module to opaque tokens" ,
414- constraints : [ ] as string [ ] ,
415- nextAction : "Fix the token refresh assertion next." ,
416- state : "The cache is still cold after warmup." ,
417- verification : [ ] as string [ ] ,
418- blockers : [ "token refresh assertion failed" ] ,
419- exactNames : [ ] as string [ ] ,
420- } ;
421- const summary = "Work continues." ;
422- const misses = verifyCompactionSummary ( summary , facts ) . misses ;
423- expect ( misses . some ( ( m ) => m . kind === "blocker" ) ) . toBe ( true ) ;
424- expect ( misses . some ( ( m ) => m . kind === "nextAction" ) ) . toBe ( true ) ;
425- const repaired = repairSummary ( summary , facts , misses ) ;
426- const repair = repaired . slice ( repaired . indexOf ( VERIFY_REPAIR_HEADING ) ) ;
427- expect ( repair ) . not . toMatch ( / ^ S t a t e : / m) ;
428- expect ( repair ) . not . toContain ( "cache is still cold" ) ;
429- } ) ;
430-
431522 test ( "a tight cap keeps goal, next, and names before aborting on the tail" , ( ) => {
432523 const facts = extractContinuationFacts ( droppedTurns ( ) ) ;
433524 const misses = verifyCompactionSummary ( "Work continues." , facts ) . misses ;
0 commit comments