Skip to content

Fork a per-class exploration anchor for dynamic-access attempts#8937

Open
kimeta wants to merge 1 commit into
masterfrom
mm/snapshotting-class-level-sessions
Open

Fork a per-class exploration anchor for dynamic-access attempts#8937
kimeta wants to merge 1 commit into
masterfrom
mm/snapshotting-class-level-sessions

Conversation

@kimeta

@kimeta kimeta commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Closes #8936

What this does

The dynamic_access_iterative strategy used to run every attempt for a class on
one shared agent session, so each attempt re-sent the full static context and
carried the previous attempts' turns forward — the class exploration was paid
for again as the conversation grew. This reworks the per-class loop so each
class is explored once into an anchor session, and every attempt forks
that anchor with only a slim prompt carrying the latest report delta
(§WF-dynamic-access-iterative-strategy in
forge/docs/workflows/dynamic-access.md).

How the design was chosen

The shape came out of working through the trade-offs of forking — a
conversation-branch primitive already implemented on both agents but so far
unused:

  • A fork branches the conversation, not the working tree. All forks share
    one checkout, so per-attempt forks can't run in parallel, and even
    sequentially a fork's memory drifts from disk: once one attempt commits a
    passing test, the next fork (branched from the pre-generation anchor) has no
    memory of it. Rather than re-anchoring after every commit — which would defeat
    the reuse — the slim prompt tells the fork to read the on-disk tests first
    and extend them additively, never rewrite
    , so it can't clobber coverage it
    didn't author.
  • Complete fork, not compacted — keeps the full exploration reasoning
    available to every attempt. The trade-off is a larger cached prefix per fork,
    and the token win is only realized if the provider serves that shared prefix
    from cache across forked threads; for a class resolved in a single attempt the
    extra explore turn is net cost.

Fork token accounting

A fork child inherits the anchor's counters, spends its own tokens, and is then
discarded — so without help its tokens vanish from run metrics (which read
totals off the single agent object). The fold is built into the fork lifecycle
rather than bolted onto each call site:

  • The fork child is a context manager. On block exit it folds its delta —
    child_total − baseline, where baseline is captured at fork time before
    the first turn — into the parent's fork-usage accumulator. Subtracting the
    baseline is what prevents double-counting the inherited history.
  • Each agent's token properties report own + forked usage, so the anchor's
    own-thread counter stays clean.
  • Call sites just do with agent.fork(prompt) as child: — adding a fork
    anywhere else needs no bespoke accounting.

What changed

Area Change
dynamic-access-explore.md (new) Read-only anchor turn: study the active class and the complete report, plan coverage, write nothing.
dynamic-access-iteration.md Rewritten into the slim per-attempt prompt: latest report delta + "read on-disk tests, extend additively".
dynamic_access_iterative_strategy.py Per class: clear_context → explore anchor → with agent.fork(...) per attempt; the inner test-repair loop runs on the fork.
agent.py Fork-usage accumulators, add_forked_usage, fork-child baseline stamping, and the context-manager fold in __exit__.
codex_agent.py / pi_agent.py Token properties report own + forked; fork/compact_fork stamp the child's baseline.

Existing strategy bundles need no config change: the explore prompt is
auto-defaulted in the strategy (prompts.setdefault) and stays overridable.

@kimeta
kimeta force-pushed the mm/snapshotting-class-level-sessions branch 3 times, most recently from 1d91c52 to 81bd621 Compare July 3, 2026 10:52
The dynamic-access iterative strategy previously ran every attempt for a
class on one shared session, so each attempt re-sent the full static
context and dragged the prior attempts' turns forward.

Now each class is explored once in a read-only anchor turn (new
dynamic-access-explore prompt) that plans coverage without writing files.
Every attempt forks that anchor and sends only a slim generation prompt
(dynamic-access-iteration, rewritten) carrying the latest report delta.

Because a fork branches the conversation but not the working tree, the
slim prompt tells the fork to read the on-disk tests and extend them
additively so it does not clobber coverage authored by a sibling attempt
it has no memory of.

A discarded fork child would otherwise drop its tokens from run metrics.
The fork child is now a context manager: on block exit it folds its token
delta (current total minus the baseline captured at fork, before its first
turn) into the anchor's fork-usage accumulator, and the token properties
report own plus forked usage. Call sites just wrap the fork in `with`, so
no per-site accounting is needed.
@kimeta
kimeta force-pushed the mm/snapshotting-class-level-sessions branch from 81bd621 to 87a6a2d Compare July 3, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce token usage in the dynamic-access iterative strategy by reusing per-class exploration

1 participant