42 lines
2.3 KiB
Go
42 lines
2.3 KiB
Go
package pipeline
|
||
|
||
// wave.go: the W0 (deterministic, $0) pre-compute of the wave runner (WS1 §1б). This is the FIRST piece
|
||
// of the wave executor built ahead of the driver switch (R1 residual): the sticky-chain memory selection
|
||
// is a CROSS-CHUNK sequential dependency (a chunk's sticky_prev is the union of the prior chunks' exact
|
||
// matches in the same chapter), so the parallel waves cannot recompute it per-chunk — it must be
|
||
// precomputed once in W0. precomputeSticky replicates EXACTLY the sequential driver's inline computation
|
||
// (bookrun.go's per-chapter reset + memory.Select + sticky advance), and the sequential driver now
|
||
// CONSUMES it — so it is byte-identical by construction, and the golden test proves the injection bytes
|
||
// did not move. When the driver switches to waves (residual), the waves consume this same precompute.
|
||
//
|
||
// Select is a PURE deterministic function of (chunk, chapter, sticky_prev, budget, frozen-bank) with NO
|
||
// model output, so precomputing it reproduces byte-for-byte the injection the sequential runner rendered
|
||
// (parallelism does not change the bytes — the order is fixed and the input carries no model state).
|
||
|
||
// precomputeSticky computes the per-chunk memory selection for EVERY chunk in one ordered W0 pass
|
||
// (WS1 §1б): the sticky window is reset at each chapter boundary (a new chapter is a scene change) and
|
||
// advanced by each chunk's exact matches, exactly as the sequential loop did inline. Returns a slice
|
||
// aligned index-for-index with `chunks`. A nil bank (report path / no glossary) yields zero-valued
|
||
// selections (no injection), so a caller can index it unconditionally.
|
||
func precomputeSticky(chunks []Chunk, mem *MemoryBank, budget int) []memorySelection {
|
||
out := make([]memorySelection, len(chunks))
|
||
if mem == nil {
|
||
return out
|
||
}
|
||
var stickyWin []map[string]injectionDisposition
|
||
prevChapter := 0
|
||
for i, ch := range chunks {
|
||
if ch.Chapter != prevChapter {
|
||
stickyWin = nil // chapter boundary: reset scene-inertia (A5)
|
||
prevChapter = ch.Chapter
|
||
}
|
||
sel := mem.Select(ch.Text, ch.Chapter, unionSticky(stickyWin), budget)
|
||
out[i] = sel
|
||
// Advance the sticky window with THIS chunk's exact matches (keep the last stickyDepth).
|
||
stickyWin = append(stickyWin, sel.activeIDs)
|
||
if len(stickyWin) > stickyDepth {
|
||
stickyWin = stickyWin[len(stickyWin)-stickyDepth:]
|
||
}
|
||
}
|
||
return out
|
||
}
|