Cross-Run Memory
How do agents learn from prior run outcomes on the same repository, and how should that knowledge feed forward without violating the ephemeral sandbox invariant?
The memory problem
Agents are stateless by default. Each run starts with no working knowledge of prior attempts on the same repository. The sandbox is ephemeral by design: "Created per-run, destroyed after extraction. No state carries between runs" (architecture.md). This is a sound security and isolation decision, but it has a compounding cost: agents rediscover the same lessons on every run.
If an agent repeatedly fails on a repo for a specific reason — "this repo requires running make generate after interface changes", "tests require a running database container", "the CI lint config enforces an import ordering the agent doesn't default to" — it burns through the same retry loops and escalates for the same reason across multiple issues. The knowledge exists somewhere, but not in a form the next run can safely consume.
This problem sits between two true statements:
- Stateless runs are safer. They limit persistence, reduce cross-run contamination, and make agent behavior easier to reason about.
- Stateless runs are wasteful. They throw away operational lessons that would make the next run faster, cheaper, and less frustrating.
The hard question is not whether memory is useful. It is how to preserve useful lessons without creating a second, less-reviewed instruction channel for future agents.
What exists today
Two mechanisms partially address this:
Per-repo AGENTS.md / skills. Humans, or accepted retro-agent proposals, encode repo-specific knowledge as skills or agent instructions. This is the right long-term solution for stable, generalizable patterns. But it requires human intervention to convert a run outcome into guidance, and it does not capture transient or tactical information such as "the last three runs on this repo all failed because the CI runner was misconfigured; do not retry lint failures until #142 is resolved."
Retro agent (14 retro agent runtime). The retro agent analyzes completed workflows and files improvement proposals as GitHub issues. This is valuable for systemic learning, but it intentionally enters a human review and triage path before changing future agent behavior. That is slower than the run-to-run feedback loop this problem points at.
Neither mechanism provides automatic, immediate feedback from one run's outcome to the next run's context. That gap is the problem space.
The feedback-loop tension
There are at least three different kinds of memory, and they should not be treated the same way:
- Stable repo guidance — durable facts about how a repository works, such as test commands, generated files, or review conventions. This belongs in reviewed repo instructions or skills.
- Recent operational state — temporary facts about the current environment, such as a broken runner, a flaky dependency, or a known upstream outage. This may be useful immediately but can become stale quickly.
- Agent self-assessment — lessons an agent claims to have learned from its own failure, such as "the reviewer objected because X" or "the correct fix was Y." This is often useful, but it is also the least trustworthy because it is generated by the same class of system that made the mistake.
The temptation is to collapse these into one memory stream. That would make memory easy to inject, but it would also blur review requirements. A stable build instruction, a transient CI observation, and an agent-authored interpretation of review feedback have different risk profiles.
Memory as an attack surface
Cross-run memory creates a new prompt-injection and governance problem: if memory influences future agents, then writing memory is a way to influence future agents.
The dangerous case is not just stale or inaccurate memory. It is adversarial memory poisoning. For example:
- An attacker opens an issue or pull request containing instructions aimed at the triage agent.
- The triage agent records a plausible-looking "lesson" from the run.
- A later review agent receives that lesson as trusted context.
- The review agent becomes more likely to approve a change it should reject.
In that scenario, the attacker did not need direct access to the review agent's prompt or policy. They influenced an upstream agent that produced context for a downstream agent. This turns memory into an inter-agent communication channel, which connects directly to the agent-to-agent prompt injection concerns in the security threat model.
This is especially sensitive if memory is non-review-gated. A memory system that agents can write and other agents automatically trust would bypass the existing model where durable instructions flow through reviewed files, CODEOWNERS, and policy configuration.
What must be separated
Any eventual design needs to split several concerns that are easy to conflate:
- Observation vs. instruction. "The last run failed with
make generatemissing" is different from "always runmake generatebefore review." Observations can inform; instructions constrain. - Agent-authored vs. system-derived. A post-script can record exit codes, failing checks, and labels without trusting the agent's prose. Agent-written summaries need more skepticism.
- Tactical vs. durable. Short-lived incident context should expire. Durable repo conventions should graduate into reviewed instructions.
- Same-role vs. cross-role. A code agent learning from its own prior code failures has a different risk profile from a triage agent influencing a review agent.
- Human-reviewed vs. automatic. Review-gated memory is slower but safer. Ungated memory is faster but opens the poisoning path above.
These splits suggest that "memory" may not be one feature. It may be several feedback paths with different trust levels, retention windows, and promotion rules.
Industry precedent
Kiro Crew ships a three-tier stack — Memory (preferences/project context), Lessons (corrections promoted into "durable lessons"), Skills (repeated patterns promoted into inspectable, editable, removable artifacts) — that lands close to the stable/tactical/self-assessment split above, independently arrived at. It is a useful existence proof that the split is practically necessary rather than a theoretical nicety. But its promotion model differs from what this document is asking: Kiro Crew promotes automatically and makes the result inspectable after the fact ("you decide what your crew carries forward" is a review step that happens post hoc, by the same user who is already trusting the tool). The open question here is whether promotion needs to be gated before a lesson can reach a different agent — particularly across roles, per What must be separated above — rather than left to after-the-fact inspection by whoever is already running the crew.
Constraints
- The sandbox should remain ephemeral. Cross-run learning should not imply persistent sandbox state.
- Agents should not be able to rewrite their own guardrails. Durable instructions still need the same review path as other policy or repo guidance.
- Memory must be scoped. Lessons from one repository should not silently leak into another repository, and lessons from one organization should not leak into another organization.
- Memory must be attributable. Future agents and human reviewers should be able to see where a remembered fact came from: which run, which role, which issue or PR, and whether it was system-derived or agent-authored.
- Memory must decay. Some lessons become wrong. A useful memory system needs a way to expire, supersede, or promote entries.
Relationship to other problem areas
- Security Threat Model — Memory is a persistence mechanism, so it expands the prompt-injection surface. The key risk is poisoned context moving from one agent run to another.
- Codebase Context — Memory is a potential fourth context source alongside code, per-repo instructions, and org-level architecture docs. It needs the same discipline around scope, provenance, and structure.
- Testing Agents — Run outcomes are eval signals. A memory mechanism could help measure whether agents improve over time on a given repo, but only if the signal is structured and auditable.
- Operational Observability — Memory summaries overlap with observability data already being collected, such as transcripts, workflow logs, and review outcomes. The question is which subset is safe to feed back into agent context.
- Agent Architecture — The retro agent and memory are complementary. Retro proposes systemic improvements that humans can review. Memory raises the question of whether any lower-latency path can be safe enough.
Error recovery and retry mechanics
Cross-run memory is one piece of a broader question: what happens when an agent fails? The memory problem asks how agents learn from prior failures. The recovery problem asks what the system does immediately after a failure, before any learning occurs.
Retry vs. escalate
When a run fails (CI failure, merge conflict, model refusal, timeout), the system must decide: retry the same approach, try a different approach, or escalate to a human. Execution limits bound how much work can occur, but reaching a limit does not establish whether another attempt is likely to produce a different outcome.
The key distinction: transient failures (network timeout, flaky test, rate limit) benefit from retry. Structural failures (wrong approach, missing context, incompatible change) do not. Retrying a structural failure wastes resources and can make things worse if each attempt introduces new problems.
Retry budgets
A per-run execution limit does not bound the total work spent on a task across runs. A cross-run retry budget would account for that cumulative work, including attempts that fail or produce no commit. It is distinct from per-run limits:
- Per-run limit: an execution ceiling within one run, such as the runner's per-iteration timeout or a runtime-supported turn or cost limit.
- Retry budget: a cumulative limit on attempts or cost across runs for the same task.
A task might allow 3 retries with a total budget of $10. Each individual run stays within its per-run limits, but the system tracks cumulative spend and attempt count.
Escalation thresholds
When should the system stop retrying and ask a human?
- After N identical failures. If the same error occurs on consecutive attempts, retrying is unlikely to help. Escalate with context: "failed 3 times with the same lint error on line 42."
- After budget exhaustion. When the retry budget is consumed, escalate regardless of failure type.
- On novel failure types. If each retry fails for a different reason, the task may be beyond the agent's current capability. Escalate after 2-3 distinct failure types.
- On regression. If a retry makes things worse (introduces new test failures that the previous attempt did not have), stop immediately.
Where escalation thresholds would live
The triggers above are stated qualitatively ("after N identical failures", "2-3 distinct failure types"). Nothing in the system evaluates them as stated.
The existing fix iteration cap counts prior fullsend-fix commits on the PR (Iteration limits). A push that adds a matching commit advances the count; a failed, timed-out, or no-op attempt that adds no such commit does not. It therefore bounds commit-producing review-fix rounds rather than all attempts.
Execution limits and evaluation thresholds
Cross-run escalation policy is distinct from both execution limits and evaluation thresholds:
- Runner timeout.
timeout_minutesbounds each iteration. A timed-out iteration ends further validation retries, but post-loop validation still runs and can accept valid output (ADR 0105). - Output-validation retries.
validation_loop.max_iterationslimits iterations used to obtain valid output within one run; it does not count attempts across runs (ADR 0022). - Runtime behavioral limits. A supported runtime can interrupt execution at a turn or cost ceiling. The runner recognizes
error_max_turnsanderror_max_budget_usdas behavioral exit reasons and makes the reason available to the post-script when it runs. These exit reasons are not configuration keys or cumulative budgets; support depends on the runtime (How a run ends). - Evaluation thresholds. The functional-test suite's
max_turnsandmax_cost_usdannotations are checked against completed runs; they do not interrupt execution. The suite now lives in the agents repository (Functional Tests).
Exposing a value inside the sandbox does not itself enforce it. ADR 0105 distinguishes advisory budget information available to the agent from the runner's independently enforced timeout.
Configuration options
Three places could hold tunable thresholds, with different trade-offs:
- Per-agent harness configuration. Harness definitions already carry per-agent execution limits such as
timeout_minutesandvalidation_loop(ADR 0024), and those existing values are tunable without a code change. New threshold keys are not: harness YAML decodes into a fixed struct, so an unrecognized key is silently ignored rather than rejected, and first-class support would mean schema, validation, and enforcement changes. Scope is a second limit: a harness describes one agent, and a budget spanning a task's whole review-fix history is not any single agent's property. - A dedicated block in
.fullsend/config.yaml. This is where repo-level policy that outlives one agent belongs, and ADR 0080 draws the line between pipeline configuration and per-agent environment. ADR 0091 establishes the agent-keyed layout, a list of named entries, for per-agent overrides. The cost is a new schema to define, validate, and version. - CEL conditions. The most expressive option in principle, but the available context is the binding constraint: CEL trigger expressions match the normalized event model (cel-triggers.md), which describes forge transitions such as labels, comments, and reviews. Failure history and cumulative cost are not in that context, so CEL could not express "three identical failures" or "cumulative spend exceeded" without extending the event model first.
Prior art caps retries by count: Kubernetes Jobs use backoffLimit, GitLab CI uses retry: max:, and Argo Workflows use a retryStrategy limit. None of them models identical-versus-distinct failure signatures, so they inform the budget cap and not the rest of the shape.
What history the triggers require
Most of the triggers above cannot be evaluated from an attempt count at all. They need failure history accumulated across attempts and attributed to the same task:
- Repeated failures need a stable failure signature, so that one root cause surfacing with two slightly different messages still counts as a recurrence.
- Distinct failures need those signatures bucketed and counted as a set rather than a total.
- Regressions need the failing set from the current attempt compared against the previous one, to separate "still broken" from "newly broken."
- Cumulative cost needs per-attempt cost summed across the task, independent of any per-run ceiling.
ADR 0141 and the entity-context-snapshot specification already specify forge-observable, per-entity run receipts in history/agent-runs.json, alongside check metadata. These contracts provide outcome and revision information, but do not define failure signatures, per-attempt cost, or a task identity spanning multiple entities.
Two properties constrain how that history can be gathered. The signature has to be derived from system evidence such as check names, exit reasons, and structured results, rather than from the agent's own account of what went wrong, because history written by one agent and trusted by a later one is the poisoning path described in Memory as an attack surface. And the history needs a stable task identity that survives across runs: an attempt count scoped to a PR and a failure history scoped to an issue would disagree about what "the same task" is.
How these records should be combined into task-level failure history and cumulative accounting remains open. The existing contracts provide inputs, not a complete escalation mechanism.
Interaction with cross-run memory
Error recovery and memory are complementary:
- Memory records what happened for future runs to learn from.
- Recovery determines what to do right now in response to a failure.
- A retry that succeeds generates a positive memory signal ("the second attempt with approach B worked after approach A failed").
- A retry that fails generates a negative signal ("this task has failed 3 times; do not auto-assign to agents until the underlying issue is resolved").
The risk: if recovery decisions feed into memory without review, a poorly-chosen retry strategy can poison future run context. For example, recording "approach B works" when it only worked due to a transient condition creates a false lesson.
Relationship to flapping
Retry loops can become flapping when the system does not converge. See flapping-convergence.md for detection and circuit-breaking mechanisms. Error recovery addresses the single-task question (when to retry vs. escalate); flapping addresses the systemic question (when to stop the system from oscillating).
Open questions
- Which run outcomes are safe to feed forward automatically, and which require human review first?
- Should memory entries be observations only, or can any of them become instructions?
- How should future agents distinguish system-derived facts from agent-authored interpretations?
- Can non-review-gated memory be made safe enough, or should all durable memory promotion go through reviewed repo instructions or skills?
- How should memory interact with separate agent roles? Should triage memory ever influence review behavior directly?
- What retention model prevents stale memory from dominating: time-based, count-based, outcome-based, or explicit supersession?
- Should the retro agent curate memory by pruning stale entries and proposing durable skill additions, or would that give it too much influence over future runs?
- How should memory interact with structured agent output? Should agent output include an "observations" field that post-scripts can validate and classify?
- Which system-derived signals should define stable failure signatures and per-attempt failure sets, beyond the outcomes already specified by entity-context-snapshot?
- How should task identity connect runs across issues and PRs, and when should failure history and cumulative accounting reset?
- Who may relax an escalation threshold, and what authorization and review should that change require?
