@@ -656,22 +656,54 @@ function pinnedBlockHashes(priorCanonical) {
656656 return hashes ;
657657}
658658
659- // Is `msg` a suppressible duplicate of a currently-pinned block? Narrow by
660- // definition (BACKLOG #76606 part (c)): STANDALONE only (single block after
661- // the same string->one-block fold canonicalMessageShape already applies
662- // elsewhere in this file), and its wrapper-stripped bytes must exactly
663- // equal a hash in `pinnedHashes` — never a positional or role heuristic.
664- // Returns the matched hash (for telemetry) or null (genuine content: no
665- // suppression, existing rules apply unchanged).
666- export function findSuppressibleDuplicate ( msg , pinnedHashes ) {
659+ // The merged-standalone shape (measured 2026-07-30, capture s-633915a8,
660+ // msg864, the 587k window): CC sometimes migrates ALL of a message's
661+ // volatile blocks out TOGETHER, joined into one standalone message,
662+ // rather than one standalone per block. pinnedBlockHashes above can never
663+ // match that — it hashes one block at a time, and a merged message is one
664+ // block whose text spans two reminders. A second set covers exactly the
665+ // observed join: for each pinned entry with >=2 volatile blocks, hash the
666+ // concatenation of ALL its volatile blocks' wrapper-stripped texts, in
667+ // WIRE order, joined with "\n\n" — the exact separator measured on the
668+ // real merged standalone (both hook reminders, 627 chars). No
669+ // subset-merges: partial joins were never observed and would only invite
670+ // false suppression on coincidental partial matches. "\n\n" is hardcoded
671+ // to the one observed instance, not a general N-ary merge grammar — other
672+ // separators are unobserved, and the census keeps watching for them.
673+ function pinnedJoinHashes ( priorCanonical ) {
674+ const hashes = new Set ( ) ;
675+ if ( ! Array . isArray ( priorCanonical ) ) return hashes ;
676+ for ( const entry of priorCanonical ) {
677+ if ( entry . d || ! entry . m || ! Array . isArray ( entry . m . content ) ) continue ;
678+ const volatileTexts = entry . m . content . filter ( isVolatileBlock ) . map ( ( b ) => unwrapVolatileText ( b ) . text ) ;
679+ if ( volatileTexts . length < 2 ) continue ;
680+ const h = hashMessageContent ( { content : [ { type : "text" , text : volatileTexts . join ( "\n\n" ) } ] } ) ;
681+ if ( h !== null ) hashes . add ( h ) ;
682+ }
683+ return hashes ;
684+ }
685+
686+ // Is `msg` a suppressible duplicate of a currently-pinned block (or, since
687+ // 2026-07-30, a currently-pinned entry's FULL joined volatile-block set)?
688+ // Narrow by definition (BACKLOG #76606 part (c)): STANDALONE only (single
689+ // block after the same string->one-block fold canonicalMessageShape
690+ // already applies elsewhere in this file), and its wrapper-stripped bytes
691+ // must exactly equal a hash in `pinnedHashes` or `joinHashes` — never a
692+ // positional or role heuristic. `joinHashes` is optional (existing callers
693+ // checking single-block duplicates only are unaffected). Returns the
694+ // matched hash (for telemetry) or null (genuine content: no suppression,
695+ // existing rules apply unchanged).
696+ export function findSuppressibleDuplicate ( msg , pinnedHashes , joinHashes ) {
667697 const shaped = canonicalMessageShape ( msg ) ;
668698 if ( ! Array . isArray ( shaped . content ) || shaped . content . length !== 1 ) return null ;
669699 const h = hashMessageContent ( { content : [ unwrapVolatileText ( shaped . content [ 0 ] ) ] } ) ;
670- if ( h === null || ! pinnedHashes . has ( h ) ) return null ;
671- return h ;
700+ if ( h === null ) return null ;
701+ if ( pinnedHashes . has ( h ) ) return h ;
702+ if ( joinHashes && joinHashes . has ( h ) ) return h ;
703+ return null ;
672704}
673705
674- export { pinnedBlockHashes } ;
706+ export { pinnedBlockHashes , pinnedJoinHashes } ;
675707
676708// Pin-mode classification. Differences from classifyInsertion:
677709// - identities exclude volatile blocks (flip absorption);
@@ -822,11 +854,25 @@ export function classifyPinned(messages, priorCanonical) {
822854 // findSuppressibleDuplicate returns null, the entry is untouched here,
823855 // and whatever the existing rules above already decided (append/splice/
824856 // edit-shaped reset) stands — no new reset path is introduced.
857+ // TAIL GUARD (BACKLOG.md, "suppression can strip a request's FINAL
858+ // message", 2026-07-30). Three real 400s ("must end with a user
859+ // message"): report-enforcer injects identical instruction bytes at
860+ // every SubagentStop; the first occurrence gets pinned, and when the
861+ // SAME bytes arrive again as the resume request's ONLY/new final
862+ // message, suppressing it left the forwarded array ending on the prior
863+ // assistant turn. A tail-position duplicate is never a stray migration
864+ // copy of already-pinned content — CC just sent it as the live,
865+ // load-bearing final entry of THIS request, and the model needs to see
866+ // it. Applies uniformly to both single-block and join-hash matches: the
867+ // guard is positional, not about which hash set matched.
868+ const lastIdx = messages . length - 1 ;
825869 const pinnedHashes = pinnedBlockHashes ( priorCanonical ) ;
870+ const pinnedJoin = pinnedJoinHashes ( priorCanonical ) ;
826871 const suppressions = [ ] ;
827872 for ( const e of newEntries ) {
828873 if ( e . r === "assistant" ) continue ;
829- const h = findSuppressibleDuplicate ( messages [ e . index ] , pinnedHashes ) ;
874+ if ( e . index === lastIdx ) continue ;
875+ const h = findSuppressibleDuplicate ( messages [ e . index ] , pinnedHashes , pinnedJoin ) ;
830876 if ( h !== null ) suppressions . push ( { index : e . index , hash : h } ) ;
831877 }
832878 const suppressedIdx = new Set ( suppressions . map ( ( s ) => s . index ) ) ;
0 commit comments