Memory Is an Input: Write-Provenance Rules for Shared Agent Memory
Shared agent memory lets one agent teach the rest of a repo. Inventory each store, record the source of every write and keep untrusted-input lanes read-only.
Go deeper. Build your own.
GitHub’s agentic autofix started keeping notes on Sep 25. When it creates a fix for a security alert, it “stores the fix pattern as a memory for future use”, and GitHub says those memories can teach other Copilot features, naming code review and the cloud agent. One agent’s conclusion is now another agent’s context, for every customer who has enabled Copilot Memory.
That makes shared agent memory an input like any other, and inputs have writers. A lane that reads text an outsider wrote can leave behind a note that a trusted lane reads next week and acts on. Cursor’s docs say as much about their own automations. Nobody reviews that note on its way in, because nobody thinks of memory as a pull request.
So the move for Tuesday is a memory inventory with write rules. For every lane, write down where memory lives, its scope, who writes it, who reads it, when it expires and who can delete it. Then apply four rules.
Lanes that read untrusted input get memory read-only or off. Every write records its source ID. A named owner reviews a weekly diff of repo-level memory. And a purge-and-rebuild drill proves you can start clean.
Sep 15 to 25: memory one run writes and the next run reads
GitHub’s Sep 25 changelog says agentic autofix “reviews existing memories for context that can help resolve security alerts” and stores the pattern of each fix it creates. Those memories can “teach other GitHub Copilot features (e.g., Copilot code review, Copilot cloud agent) about secure development patterns unique to your repository.” It applies “for customers who’ve enabled it”, and “Both agentic autofix and Copilot Memory are available in public preview.” When we checked on the evening of Sep 28 (UTC), the Memory docs still said public preview, and GitHub’s changelog had no newer post on either feature.
Screenshot: GitHub Changelog, “Agentic autofix now uses Copilot Memory - GitHub Changelog” (Sep 25, 2026), captured Sep 28, 2026.
GitHub’s Memory docs fill in the shape. “Copilot Memory is currently used by Copilot cloud agent, Copilot code review, Copilot CLI, and agentic autofix,” and “Facts and preferences captured by one Copilot feature can be used by another.” Facts carry citations to the code that supports them. Before use, Copilot “checks those citations against the current branch to confirm the information is still accurate. Only validated facts are used.”
The documented write gate: “Copilot only creates repository-level facts in response to actions by users with write access to the repository who have Copilot Memory enabled.” One more line belongs in any threat model: “Facts can also be captured from pull requests that were closed without merging.”
Sharing across agents isn’t new. GitHub announced it in January, and the Sep 25 change adds a security tool as a writer.
Ten days earlier, Claude Code closed a different memory hole. Version 2.1.273, released Sep 15, fixed permissions.blockReadsOutsideWorkingDirectories so that “a memory directory chosen by a repository’s settings is no longer loaded into the prompt, recalled, indexed, or used by memory extraction”. Put plainly, before that release a repository you opened and trusted could choose where your agent’s memory came from, even with the guard on.
Cursor’s Automations get the warning in writing. Memories let the agent “read and write persistent notes across runs for the same automation”, and they’re “enabled by default but can be disabled.” Cursor’s docs then say it outright: “Memories persist across runs and should be used with caution if your automation handles untrusted input. Inputs may lead to misleading or malicious memories that unintentionally impact future automation runs.”
Screenshot: Cursor Docs, “Automations | Cursor Docs” (undated page), captured Sep 28, 2026.
Why an agent’s memory is a write surface, not a notebook
A chat assistant’s memory shapes the next answer for the person who wrote it. Shared agent memory shapes the next action of a different agent, on a different task, for a different person, and that agent may push code. Chatbots suggest; agents act, and here they act on what another agent wrote down.
GitHub’s guard is real and narrow. Citation validation checks that the code a fact points to still supports it on the current branch, which is a staleness check. GitHub documents no review step before a new fact is used, and doesn’t say whether a fact records its source beyond those citations. Whether a fact that cites real code but draws the wrong lesson from it would fail validation, the docs don’t say.
Memory has been covered here as a retention question in zero retention versus agent memory, a recall-quality question in the Tuesday memory bakeoff, a cost question in memory that burns quota and a hand-off protocol in cross-CLI session memory. This piece treats it as an intake path.
Step 1: Inventory shared agent memory per lane
Seven columns: lane, store, scope, writers, readers, expiry and delete owner. The rows use each vendor’s documented behavior, and where a page is silent, the cell says so.
| Lane | Store | Scope | Writers | Readers | Expiry | Delete owner |
|---|---|---|---|---|---|---|
| Copilot cloud agent, code review, CLI, agentic autofix | Copilot Memory, repository-level facts | One repository; available to everyone with Memory access there | Copilot features, in response to actions by users with write access who have Memory on; closed, unmerged PRs count | All four features; code review reads repository facts only | Deleted after 28 days unused; the timer may reset on each validated use | Repository owners, under Settings → Copilot → Memory |
| Copilot for one user | User-level preferences | Follow the user across repositories | That user’s Copilot use | Copilot features for that user; the CLI applies the initiating user’s preferences | The same 28-day idle rule | On Business and Enterprise, admins can view, export as JSONL and delete them |
| A Cursor automation | A named entry, MEMORIES.md by default, outside the agent’s working filesystem |
That automation, across its runs | The automation’s agent; on by default | Later runs of the same automation | Not covered by the docs page | Agents can delete memory files during runs; people use the tool configuration UI |
| Claude Code on one machine | Auto memory in ~/.claude/projects/<project>/memory/; MEMORY.md loads into every session |
Machine-local, per project | Sessions on that machine; autoMemoryDirectory can move it from any settings scope |
Every session for that project on that machine | Not covered by the docs page | Whoever owns the machine |
| Your own memory layer | An MCP memory server or a vector store | Whatever you configured | Every agent holding the write tool | Every agent holding the read tool | Your policy | Your team |
Fill the cells from settings, not from anyone’s recollection. For Copilot, read the enterprise or organization policy under “Features & clients” → “Copilot Memory”, then each repository’s Memory list under Settings → Copilot → Memory. For Cursor, open every automation’s tool configuration and note whether Memories is on. For Claude Code, check autoMemoryDirectory in every scope it’s read from (user, project, local, policy and --settings), because a project value is honored under the same workspace trust rule as hooks, so a repository you cloned and trusted can set it.
Enablement decides which rows exist. Copilot Memory “is enabled per user, not per repository.” It’s on by default for individual plans. For organization- and enterprise-managed subscriptions it’s “off by default and must be enabled in the enterprise or organization settings”, after which it’s on for users unless they opt out, and with several licensing organizations the most restrictive setting applies.
One inference, ours, from two documented lines: Memory is in preview, so the Oct 22 default for unconfigured Copilot features doesn’t reach it yet, but that policy covers features moving from preview to GA. The default-on sweep covers that toggle.
From GitHub’s Copilot Memory docs and its Sep 25 changelog, read Sep 28, 2026. Copilot Memory is in public preview.
Step 2: Make memory read-only or off for every lane that reads untrusted input
Untrusted input means fork PRs, public issues and error events: anything someone outside your org can write. The auto-remediation register finds those lanes; mark the same lanes in the memory inventory. Then apply the rule with whatever lever each vendor documents. Where no read-only mode exists, the rule falls back to off.
| Store | Rule for lanes that read untrusted input | Documented lever | What the docs don’t say |
|---|---|---|---|
| Cursor automation memories | Off for any automation fed by Sentry events, outside webhooks or public issues | Memories are on by default and “can be disabled” per automation | Whether memory can be read-only per trigger or per source |
| Copilot Memory | Off for the users who work in that repository, until the fork drill in step 5 answers the open question | The enterprise or organization policy under “Features & clients” → “Copilot Memory”; Memory is enabled per user | Any per-lane or per-repository read-only switch; whether a fork-PR review can write memory |
| Claude Code auto memory | No memory directory chosen by the repository | 2.1.273 or later with permissions.blockReadsOutsideWorkingDirectories on |
Nothing needed here: the fix is in the changelog and the memory docs |
| Your own memory layer | Read tool only, or no memory tools | The lane’s tool list | Nothing: it’s your config |
Read-only means the write tool isn’t in the lane’s tool list at all; a prompt asking the agent not to write is not read-only. With the block on, the memory docs say Claude Code “loads no auto memory from a directory that a repository-supplied settings file chooses and saves none to it, wherever that directory sits.” The settings key below comes from Claude Code’s changelog and memory docs. Putting it in managed settings, which a repository can’t override, is our recommendation.
{
"permissions": {
"blockReadsOutsideWorkingDirectories": true
}
}
Review lanes are the sharpest case. Review bots now run PR code, and whether a review of a fork PR can write memory isn’t documented by GitHub, Cursor or Anthropic. GitHub’s only statement is the write-access rule, which doesn’t say whose action an automatic review of an outsider’s PR counts as, so treat it as a drill question, not an answer (step 5). Cursor’s PR triggers, for what it’s worth, “don’t run on PRs opened from forks”, except “Pull request merged”.
Controls sit on the write path and on the store, never only in the prompt.
A read-only setting is a guardrail, and guardrails fail quietly. A new automation starts with memories on because that’s the default, or a policy flips when an admin enables Memory for an org. The weekly diff in step 4 is where you’ll see it, and the purge in step 5 is how you undo it.
Step 3: Record a source ID on every memory write
A fact without a source can’t be judged, only trusted or deleted. So every write gets a record: which lane wrote it, in which run, from which source, and how far that source can be trusted. Your own memory layer can enforce this inside the write tool; vendor stores mostly can’t.
GitHub doesn’t say whether autofix memories are labeled with the feature that wrote them, and Cursor keeps notes in an entry the agent itself edits. For those stores, keep a sidecar log: capture the memory list after runs and attribute each new entry to the runs in that window.
Decide each lane’s source ID before you need it. Our illustrative mapping:
- Agentic autofix: the security alert it fixed.
- Code review: the PR number and its head commit.
- Cloud agent or CLI task: the issue or task that started it.
- Cursor automation: the trigger event, such as the Sentry issue, the PagerDuty incident or the webhook delivery.
- Claude Code session: the session ID and the repository it ran in.
The record below is illustrative, and the field names are ours.
{
"memory_id": "repo-fact-0142",
"store": "copilot-memory:acme/payments",
"first_seen": "2026-09-28T14:05:00Z",
"writer_lane": "agentic-autofix",
"writer_run": "autofix-run-8812",
"source_type": "security-alert",
"source_id": "alert-417",
"source_trust": "internal",
"triggered_by": "j.doe (write access, Memory on)",
"citations": ["src/payments/validate.ts:88"],
"reviewed_by": null
}
Two fields do most of the work. source_trust is how the weekly diff sorts entries into keep and question. triggered_by answers the question GitHub’s write gate raises, which is whose action caused the write. If you can’t fill it in, write unknown: that’s a finding, not a blank.
Step 4: Review a weekly diff of repo-level memory
Once a week, one named owner compares each store’s repository-level memory with last week’s. For Copilot, that’s the list repository owners see in the repository’s Memory settings, and GitHub’s admin page is blunt about the remedy: “If you think any are inappropriate, misleading, or incorrect, you can delete them.” Cursor memories can be viewed and edited in the tool configuration UI, and your own layer can be dumped. The drill below is illustrative.
# weekly memory diff (illustrative): one owner, every store in the inventory
1. Snapshot each store: Copilot repository facts, each automation's
memory entry, your own layer's dump.
2. Diff against last week's snapshot: added, changed, removed.
3. Match every added or changed entry to a provenance record.
4. No record, or source_trust is untrusted: delete it and log the ID.
5. Open every citation. Delete facts whose cited code is gone from
the default branch or doesn't say what the fact claims.
6. Read each survivor as an instruction another agent will follow.
If you wouldn't approve it as a review comment, delete it.
7. Treat removals as writes: agents can delete memory files during
a Cursor automation run. Log who removed what.
8. Save the snapshot and write the counts into the inventory.
Most weeks the diff is short and dull, which is the point. A diff that jumps after a vendor change, such as Sep 25’s new writer, tells you a lane started writing before your inventory knew it could. Add one stop rule while you’re there, illustrative like the drill: if one lane adds more entries in a week than every other lane combined, freeze its memory writes until someone has read them.
Step 5: Keep a purge-and-rebuild drill, and use it to answer the undocumented questions
Copilot’s 28 days is an idle timeout, not a maximum age: “The 28-day timer may reset whenever Copilot successfully validates and uses an entry.” A misleading fact that agents keep using stays alive for as long as they use it, so purging has to be a decision. Run this once a quarter, our illustrative cadence, on one repository:
- Snapshot every store in that repository’s inventory rows.
- Purge. A repository owner deletes the repository-level facts, someone clears the automation’s memories in Cursor’s tool configuration UI, and Claude Code’s project memory directory gets moved aside.
- Rebuild from trusted lanes only. Let a week of normal work from write-access lanes repopulate memory while untrusted-input lanes stay read-only or off.
- Compare. Facts that come back had a trusted source. Facts that don’t are worth tracing to wherever they came from.
- Time it. Record how long the purge took and who had to be involved. That number is your response time for a poisoned memory.
Use the same test repository to answer what no vendor documents. Every canary is inert: a unique marker string, no instructions.
- Can an automatic review of a fork PR create a repository-level fact? GitHub, Cursor and Anthropic don’t document it. Open a canary fork PR from an account without write access, let any configured review run, then check the Memory list.
- Does a Seer-started Copilot cloud-agent task touch Copilot Memory? Seer can hand issues to a Copilot cloud agent, and that agent uses Memory, but no doc connects the two, so the chain is our inference. Send the canary event from the telemetry register’s drill and diff memory afterwards.
- Does a PR closed without merging leave a fact? GitHub documents that it can, so this drill only proves your weekly diff catches it.
How shared agent memory gets poisoned quietly, and the signal for each
| Failure | Signal | First move |
|---|---|---|
| An untrusted-input lane writes a fact | A new entry in the weekly diff with no provenance record | Delete it; switch that lane’s memory off or read-only |
| A misleading fact keeps getting used | The same entry survives diff after diff, well past 28 days | Purge it on purpose, because idle expiry won’t |
| A closed, unmerged PR leaves a fact | An entry whose citation isn’t on the default branch | Delete it and log the PR |
| An admin enables Memory for an org | Repository facts appear in repos with untrusted-input lanes | Scope the policy; the most restrictive licensing org wins |
| A repository picks Claude Code’s memory directory | Sessions recall notes that aren’t in the project’s own memory folder | Update to 2.1.273 or later and set the block in managed settings |
| A new Cursor automation keeps the default | A memory entry grows on an automation with an untrusted trigger | Disable memories for that automation |
| An agent deletes memory mid-run | Entries vanish between diffs with no human removal logged | Log removals as writes; restore from the snapshot if needed |
| Memory reaches GA under an unconfigured policy (our inference) | The Memory policy reads Unconfigured after GA | Set it explicitly |
Every signal in that table comes from your own snapshots and logs, which is why the diff has a named owner and a fixed day.
Memory belongs in the fleet’s evidence trail
An operating layer keeps a record of what each agent did, so an incident can be replayed. Memory breaks that record in one specific way: a run’s behavior depends on notes another run wrote, sometimes weeks earlier and under someone else’s name. Replaying a session without its memory state replays half of it. Provenance records get you the other half, because each fact’s record points back to the run and the source that wrote it.
So file the memory inventory with the lane record, next to the owner, the credentials and the kill switch. A lane that can write shared memory can steer lanes it never touches directly, and its write rules are part of its blast radius.
FAQ
Can someone poison GitHub Copilot Memory?
GitHub limits it without ruling it out. Repository-level facts are created only in response to actions by users with write access who have Memory enabled, and a fact is used only after its citations validate against the current branch. No review step is documented, and closed, unmerged PRs can leave facts.
How long does Copilot Memory keep a fact?
Unused facts and preferences are deleted after 28 days, but the timer may reset whenever Copilot validates and uses an entry. That makes 28 days an idle timeout, not a maximum age: a fact agents keep using can persist indefinitely. Repository owners can delete repository-level facts in the repository’s Copilot Memory settings.
Is Copilot Memory on by default?
For individual plans, yes. For Business and Enterprise it’s off until an admin enables it in the enterprise or organization settings, and then it’s on for users by default, with an opt-out. It’s enabled per user, not per repository, and with several licensing organizations the most restrictive setting applies.
Sources
- GitHub changelog: Agentic autofix now uses Copilot Memory — autofix reads and writes memory; public preview (Sep 25, 2026)
- GitHub docs: About GitHub Copilot Memory — four features, citations, the write gate, 28-day idle expiry, enablement
- GitHub docs: Manage Copilot Memory as an administrator — off by default for managed plans; review and delete facts
- Claude Code CHANGELOG — v2.1.273, the repository-chosen memory directory fix (Sep 15, 2026)
- Claude Code docs: memory —
autoMemoryDirectory,blockReadsOutsideWorkingDirectories, machine-local auto memory - Cursor docs: Automations — memories per automation, on by default, the untrusted-input warning (undated)
- Sentry docs: Autofix — Seer’s handoff to a Copilot cloud agent
