Stop paying twice for a block a retry can still throw away
A memory whose block ends on the newest action is the one memory a player can reach: retry and take-switching both refuse anything else. Each retry of that turn withdrew the memory and wrote it again, and a block closes every six actions while a normal turn writes two, so that was one turn in three. SETTLE_SLACK asks for one action past a block before the block is summarized. The block is still MEMORY_INTERVAL actions; only the moment moves. This is not the pre-SP4 holdback returning: that one was about a retry rewriting text in place, which sibling attempts and forget_node settled, and correctness still rests on the withdrawal rather than on the slack. Undo and delete can carry a summarized node back to the tip, so the withdrawal path stays reachable, just rarely. The slack buys nothing back. The block that just closed is still in the history window in full, so a memory of it says what the model can already read. Tests: the settling suite asserts the new rule and that a retry at the tip finds nothing to withdraw; the rewrite suite builds thirteen actions so both of its blocks settle; the one path test that ended on a block boundary sets the slack to zero, because it is about which actions a block is read from rather than about when a block forms. 632 green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NcrxCJjqgDvAkamKeLWdn
This commit is contained in:
+16
-4
@@ -392,7 +392,7 @@ raw turns → memories → story summary
|
||||
|
||||
| Layer | Cadence | Purpose |
|
||||
|---|---|---|
|
||||
| **Memory** | Every 6 actions, starting at 12 | One or two past-tense sentences of concrete fact. |
|
||||
| **Memory** | Every 6 actions, starting at 12, once one action sits past the block | One or two past-tense sentences of concrete fact. |
|
||||
| **Story summary** | Every 15 actions | A single ≤250-word overview of the whole plot, rewritten by folding in the new memories. |
|
||||
| **Retrieval** | Every turn | Embed the last 4 actions (≤600 tokens), cosine-rank the bank, inject the top K (default 5). |
|
||||
|
||||
@@ -413,8 +413,18 @@ This is also how repair works. When a turn's text is replaced or removed (a retr
|
||||
a deleted action), `forget_node` withdraws the memory attached to that coordinate and
|
||||
rewinds both marks to just before the stretch it covered, so the ground is summarized again
|
||||
from what the story now says. An earlier version instead held the newest action back a turn
|
||||
so it could never be summarized before it stopped being retryable. That is no longer
|
||||
needed, because the repair exists whether or not the invalidation happens at the tip.
|
||||
so it could never be summarized before it stopped being retryable. Correctness no longer
|
||||
rests on that, because the repair exists whether or not the invalidation happens at the tip.
|
||||
|
||||
**A block still waits for one action to settle past it** (`SETTLE_SLACK`), and that is a
|
||||
cost rule rather than a correctness one. Retry and take-switching both refuse anything but
|
||||
the newest action, so a memory whose block ends on the tip is the one memory a player can
|
||||
still throw away: every retry of that turn writes it, withdraws it, and writes it again. A
|
||||
block closes every 6 actions and a normal turn writes 2, so without the slack that is one
|
||||
turn in three. The slack costs nothing in return, because the block that just closed is
|
||||
still in the history window in full — a memory of it says what the model can already read.
|
||||
Memories earn their place once the raw text has scrolled out, which is never the turn the
|
||||
block closed.
|
||||
|
||||
**Cursors only advance on success.** Every AI call in this module is best-effort. If
|
||||
summarization fails, the function returns and the cursor is unchanged, so the same block is
|
||||
@@ -724,7 +734,9 @@ is ambiguous once two branches both have a node 41.
|
||||
|
||||
`position_of_index`, `note_action_removed`, `settled_story_actions` and the cursor-rewind
|
||||
machinery were **deleted**, not left unused. So was the one-turn memory holdback that
|
||||
existed because a retry could rewrite an action the mark had already passed.
|
||||
existed because a retry could rewrite an action the mark had already passed. (`SETTLE_SLACK`
|
||||
later put one action of slack back, for what redoing a block costs rather than for what it
|
||||
could get wrong. See "The memory system".)
|
||||
|
||||
### Derived work attaches to the node that produced it
|
||||
|
||||
|
||||
@@ -988,7 +988,7 @@ ORDER BY depth DESC LIMIT 32 -- → A0 A1 A2 A3 B4 B5 C6 C7</code></pre
|
||||
<h4>Cursors become anchors</h4>
|
||||
<p>The two marks, how far the memory bank has got and how far the summary has got, used to be counts. A count is a position in a list. Every rule about sliding, rewinding, and translating between positions and <code>Action.index</code> existed to patch up the fact that the list moves.</p>
|
||||
<p>A cursor is now an <strong>anchor</strong>: <code>(branch_id, depth)</code>, the node up to and including which the work is done. Deleting an action does not move it. “What is not covered yet?” becomes a question about the story instead of a list index, and it answers correctly no matter what has been deleted in front of it. The branch half is what makes it survive forking. A depth alone is ambiguous once two branches both have a node 41.</p>
|
||||
<p><code>position_of_index</code>, <code>note_action_removed</code>, <code>settled_story_actions</code>, and the cursor-rewind machinery were <strong>deleted</strong>, not left unused. So was the one-turn memory holdback that existed because a retry could rewrite an action the mark had already passed.</p>
|
||||
<p><code>position_of_index</code>, <code>note_action_removed</code>, <code>settled_story_actions</code>, and the cursor-rewind machinery were <strong>deleted</strong>, not left unused. So was the one-turn memory holdback that existed because a retry could rewrite an action the mark had already passed. (<code>SETTLE_SLACK</code> later put one action of slack back, for what redoing a block costs rather than for what it could get wrong.)</p>
|
||||
|
||||
<div class="trap">
|
||||
<span class="lab">Three findings only hand-driving produced</span>
|
||||
|
||||
+13
-3
@@ -733,7 +733,7 @@ into the inn, it should come back.</p>
|
||||
<table>
|
||||
<thead><tr><th>Layer</th><th>Cadence</th><th>Purpose</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>Memory</strong></td><td>every 6 actions, from 12</td><td>One or two past-tense sentences of concrete fact.</td></tr>
|
||||
<tr><td><strong>Memory</strong></td><td>every 6 actions, from 12, once one action sits past the block</td><td>One or two past-tense sentences of concrete fact.</td></tr>
|
||||
<tr><td><strong>Story summary</strong></td><td>every 15 actions</td><td>A single ≤250-word overview, rewritten by folding in the new memories.</td></tr>
|
||||
<tr><td><strong>Retrieval</strong></td><td>every turn</td><td>Embed the last 4 actions (≤600 tokens), cosine-rank the bank, inject the top 5.</td></tr>
|
||||
</tbody>
|
||||
@@ -755,8 +755,18 @@ across a fork: the ones above the fork point already sit on ancestors both lines
|
||||
action — <code>forget_node</code> withdraws the memory hanging off that coordinate <em>and</em>
|
||||
rewinds both marks to just before the stretch it covered, so that ground is summarized again from
|
||||
what the story now says. An earlier version instead held the newest action back a turn so it could
|
||||
never be summarized before it stopped being retryable; that is no longer needed, because the repair
|
||||
exists whether or not the invalidation happens at the tip.</p>
|
||||
never be summarized before it stopped being retryable; correctness no longer rests on that, because
|
||||
the repair exists whether or not the invalidation happens at the tip.</p>
|
||||
|
||||
<p><strong>A block still waits for one action to settle past it</strong>
|
||||
(<code>SETTLE_SLACK</code>), and that is a cost rule rather than a correctness one. Retry and
|
||||
take-switching both refuse anything but the newest action, so a memory whose block ends on the tip
|
||||
is the one memory a player can still throw away: every retry of that turn writes it, withdraws it,
|
||||
and writes it again. A block closes every 6 actions and a normal turn writes 2, so without the
|
||||
slack that is one turn in three. The slack costs nothing in return, because the block that just
|
||||
closed is still in the history window in full — a memory of it says what the model can already
|
||||
read. Memories earn their place once the raw text has scrolled out, which is never the turn the
|
||||
block closed.</p>
|
||||
|
||||
<p><strong>Cursors only advance on success.</strong> Every AI call here is best-effort. If
|
||||
summarization fails, the function returns and the cursor is unchanged, so the same block is
|
||||
|
||||
Reference in New Issue
Block a user