Visitar URL original
Repo Memory | GitHub Agentic Workflows

Repo Memory

Repo memory provides persistent file storage via Git branches with unlimited retention. The compiler auto-configures branch cloning/creation, file access at /tmp/gh-aw/repo-memory-{id}/, commits/pushes, and merge conflict resolution (your changes win).

---
tools:
repo-memory: true
---

Creates branch memory/default at /tmp/gh-aw/repo-memory-default/. Files are stored within the branch at the branch name path (memory/default/). Files auto-commit/push after workflow completion.

Workers using tools.work-queue.worker: true cannot use standalone tools.repo-memory persistence: its ordinary push job is not attributed to a Claim. Removing this restriction or adding a declaration alongside that push job does not make the writes Claim-scoped.

For persistent snapshots, use a matching safe-outputs.scripts or custom preparation job and safe-outputs.claim-adapters with effect-type: git_tree. Pin an existing native base commit, a fixed target repository and a separate branch prefix. Credential-free preparation emits declared file content; the trusted handler creates an immutable Claim branch and independently verifies its commit, complete tree and blobs. The installed Work must freeze a positive numeric repository target and permit the output, intersecting the approved profile, Subject and every worker-created ancestor. Preparation and restored history are not authority or delivery proof.

The repository’s ESLint Refiner workflow preserves its legacy memory/eslint-refiner JSON/JSONL history and publishes new eslint-refiner.json snapshots under memory/eslint-refiner-runs/claims/. A read-only setup step restores both histories from exact native commit and blob identities. It rejects truncated trees, foreign repositories, malformed refs, altered bytes and exceeded bounds rather than silently dropping history. Its limits are 128 snapshots, 1 MiB per file and 16 MiB total; archive older history through an approved operator before exceeding them.

This migration preserves memory continuity but does not update the old canonical branch. The Claim adapter cannot overwrite an existing Claim branch, and memory/eslint-refiner-runs avoids conflicting with the existing memory/eslint-refiner ref. Do not retain the ordinary unscoped memory push job or use a moving branch name as the configured base revision.

---
tools:
repo-memory:
branch-name: memory/custom-agent-for-aw
branch-prefix: tracking # Custom prefix instead of "memory"
description: "Long-term insights"
file-glob: ["*.md", "*.json"]
max-file-size: 1048576 # 1MB (default 100KB)
max-file-count: 50 # default 100
max-patch-size: 1048576 # 1MB max (default 10KB)
target-repo: "owner/repository"
create-orphan: true # default
allowed-extensions: [".json", ".txt", ".md"] # Restrict file types (default: empty/all files allowed)
format-json: true # Pretty-print .json files (default: false)
validation:
timeout-minutes: 1
json-schemas:
- file: state.json
schema:
type: object
required: [version, items]
additionalProperties: false
properties:
version: { enum: [1] }
items:
type: array
items: { type: string }
script: |
const data = JSON.parse(fs.readFileSync(path.join(memoryRoot, "state.json"), "utf8"));
if (!Array.isArray(data.items)) throw new Error("state.json must contain an items array");
---

branch-prefix changes the default memory prefix and must be 4-32 alphanumeric, hyphen, or underscore characters; it cannot be copilot. allowed-extensions limits which file types can be stored, format-json: true pretty-prints .json files before commit, validation.json-schemas declares mandatory per-file JSON/JSONL validation, validation.script runs an optional custom JavaScript domain validator, and max-patch-size caps the total diff size for one push (default 10KB, max 1MB) to prevent oversized updates.

Ledgers use tools.ledger, independently of repo-memory file storage. The legacy tools.repo-memory.ledger declaration is no longer supported. For built-in state models, declare log, set, map, table, counter, or notes:

---
tools:
ledger:
findings:
type: table
key: id
schema:
type: object
required: [id, status]
properties:
id: { type: string }
status: { type: string }
---

Each ledger persists immutable records on its own ledgers/<name> Git branch. Trusted preparation reconstructs a disposable, read-only SQLite database at /tmp/gh-aw/ledgers/<name>/ledger.db. Query state for the built-in current state and records for immutable history. Schemas validate operation values, not operation envelopes; table requires a string primary-key field.

Configure bounded storage limits and maintenance cadence per ledger:

tools:
ledger:
findings:
type: log
schema: .github/schemas/ledger.schema.json
max-segment-kb: 100 # default 100 KiB
max-record-kb: 32 # default 32 KiB
max-patch-kb: 10 # default 10 KiB
compaction:
schedule: weekly
min-segments: 32
max-segments: 128

Agents submit mutations only through the configured ledger safe-output tools. Map ledgers expose ledger_map_put and ledger_map_delete; notes expose ledger_note_add and ledger_note_vote. Other built-in types use typed ledger_append operations. Writes are deferred: the immediate response confirms queuing, not durability. The trusted push_ledger_changes job validates and reconciles accepted requests against the latest branch state before pushing. Queued writes are not immediately visible in the run’s projection.

Ledger compaction never runs in the agent or persistence job. Standalone tools.ledger ledgers are compacted by Agentic Maintenance through an untrusted plan job and a trusted apply job; see Ledger compaction.

Custom replay.script, replay.config, and compaction.script settings are rejected. Use schemas for domain-specific values and the built-in compaction policy for segment selection. Ordinary repo-memory validation.script remains supported; it is a separate file-storage validator.

Existing raw-record ledgers can retain their generic projection by omitting type and querying records.payload with SQLite JSON functions. Adding a built-in type does not convert historical raw payloads into operations. See Ledger replay projections for type semantics and migration guidance.

File Glob Matching Rules:

  • Patterns are matched against the relative path within the artifact directory — do not include the branch name.
  • Slashless patterns (no / in the pattern, e.g. *.json, *.md) are matched against the full relative path, so a single * only matches files at the artifact root (depth 0). They do not match files inside subfolders (depth 1+) — use a pattern containing / (e.g. **/*.json) for that.
  • Patterns containing / (e.g. metrics/**, data/*.csv) are matched against the full relative path from the artifact root and work as standard glob expressions.
  • Absolute paths (patterns starting with /) are not supported and are rejected at compile time and runtime.

Example: with the default filter ["*.json", "*.md"], the root-level file processed-discussions.json is persisted (depth 0 ✓), but discussion-task-miner/processed-discussions.json (depth 1) is not — use ["**/*.json", "**/*.md"] to also match files nested in subfolders.

---
tools:
repo-memory:
- id: insights
branch-prefix: daily # Creates daily/insights branch
file-glob: ["*.md"]
- id: state
file-glob: ["*.json"]
max-file-size: 524288 # 512KB
---

Mounts at /tmp/gh-aw/repo-memory-{id}/ during workflow execution. The required id determines the folder name, and branch-name defaults to {branch-prefix}/{id} with memory as the default prefix. Files are stored inside the branch under that branch-name path. File globs match paths relative to the memory directory, so never include the branch name. Slashless patterns such as *.json match files at the memory root only. Use **/*.json to match nested JSON files; combine it with *.json to match both root and nested files. Patterns containing /, such as metrics/*.json, match the full relative path; * does not cross directories, while ** does. Files excluded by the glob or extension filters produce warnings listing their paths.

Branches auto-create as orphans by default, or clone with --depth 1. After validating file-glob, max-file-size, and max-file-count, gh-aw auto-commits and pushes when changes are present and threat detection passes.

On a shared memory branch, file-glob and allowed-extensions scope normalization, validation, and persistence to eligible files. Existing files outside that scope are preserved, including files managed by other workflows.

Use validation.json-schemas for required structural checks. Each declaration has an inline schema and may have a relative file path or glob (* matches within a path segment; ** spans segments). If file is omitted, the schema applies to every eligible .json and .jsonl file in the memory directory, excluding .git; omit format too so the format is inferred for each file. Otherwise, paths must match the configured file-glob and allowed-extensions policies and exist in the candidate; globs must match at least one file, and every match is validated. A json file must contain one non-empty valid JSON document. A jsonl file is checked record by record: LF and CRLF are accepted, a final newline is optional, an existing empty file has zero records, and blank physical lines or malformed records fail with a line number. Schema checks do not coerce, default, strip, or rewrite values. They run after configured filtering and format-json normalization, before an optional validation.script, and are repeated before upload/commit and in the repo-memory push job.

The file format is inferred from each matched file’s .json or .jsonl extension (case-insensitive), so a glob may match both formats. Optional format: json or format: jsonl overrides inference and is required for other extensions.

The supported schema vocabulary is deliberately limited to type (including type lists and null), primitive enum, required, nested properties, additionalProperties: false, object-schema items, and standalone oneOf or anyOf. Schemas are limited to 32 levels; alternatives cannot have sibling constraints. Numeric enum integers must be exactly representable by JavaScript, and values checked against numeric enums must not lose precision during JSON parsing. This is not full JSON Schema support: keywords such as $ref, const, format, pattern, numeric/string/array bounds, and uniqueItems are rejected at compile time. Use a script for cross-file rules, uniqueness, positive-ID checks, timestamps, or other unsupported constraints.

Use validation.script when generic storage and schema checks are not enough. The script is a JavaScript body executed with Node.js over the complete configured memory directory after format-json normalization and before artifact upload or branch commit. It runs in the agent job and is re-run in the repo-memory push job as defense in depth.

The script also checks the cloned baseline before the agent starts, without formatting or filtering the checkout. A baseline failure is shown to the agent with the validator’s output so it can repair memory and call push_repo_memory to verify the change. If an invalid baseline remains unchanged, the agent job warns and skips its memory artifact instead of failing after the agent exits. Any changed memory still requires successful validation before upload and again before the push.

Available globals are Node.js fs and path, plus memoryRoot/memoryDir, memoryId, and memoryKind ("repo"). These paths and the working directory point to a temporary copy of the eligible memory files, excluding the checkout’s .git directory. Changes to this copy are not persisted and do not modify the original memory directory. This copy is not a sandbox: validators remain trusted code with the runner’s filesystem permissions.

Environment variables available to the validator are intentionally limited to basic runner paths plus GH_AW_MEMORY_ROOT, GH_AW_MEMORY_DIR, GH_AW_MEMORY_ID, and GH_AW_MEMORY_KIND; GitHub tokens and write credentials are not passed to the validator subprocess. Network access follows the workflow runner’s normal network policy. The default timeout is 1 minute and may be set with validation.timeout-minutes (1-5 minutes).

Throw an exception, return false, time out, exit nonzero, or modify a memory file to reject persistence. Validator stdout and stderr are reported separately from built-in storage validation output so agents can distinguish domain-schema validation from size/count checks.

In the repo-memory push job, validation failures set validation_failed to true and include both non-empty output streams in validation_error and the Repo-memory push failed job summary. Push failures are also logged with their underlying error. Configured GH_TOKEN, GITHUB_TOKEN, and GITHUB_APP_TOKEN values and HTTP authorization credentials are redacted from these diagnostics; validators should never print secrets.

Commits use the GitHub GraphQL createCommitOnBranch mutation, so they are automatically Verified with GitHub’s GPG key and satisfy rulesets that require signed commits.

FeatureCache MemoryRepo Memory
StorageGitHub Actions CacheGit Branches
Retention7 daysUnlimited
Size Limit10GB/repoRepository limits
Version ControlNoYes
PerformanceFastSlower
Best ForTemporary/sessionsLong-term/history

For fast 7-day caching without version control, see Cache Memory.

  • Branch not created: Ensure create-orphan: true is enabled, or create the branch manually.
  • Validation or patch-size failures: Keep changes within file-glob, max-file-size (100KB default), max-file-count (100 default), and max-patch-size (10KB default).
  • Changes not persisting: Confirm the directory path, let the workflow finish, and check the push job’s Repo-memory push failed summary and logs for the underlying error. Custom validation failures include both stdout and stderr; updates rejected by validation are not committed or pushed.
  • Merge conflicts: Concurrent pushes are replayed onto the latest remote state, so your file changes win. .jsonl files are merged with merge=union, so rows written by concurrent runs are all kept; other file types keep the local version.
  • Many runs finishing at once: Pushes are not serialized. Each run commits with a compare-and-swap on the remote branch head and, if another run wins the race, re-reads the head, rebases its change with JSONL union merging, and retries with exponential backoff. To avoid conflicts entirely, have each run write its own file instead of all runs editing the same file, for example runs/${{ matrix.worker }}.jsonl or targets/${{ matrix.target }}.jsonl.
  • GH013 — Commits must have verified signatures: This usually means the artifact included a symlink, executable file, or submodule entry, which forced a fallback to plain git push. Remove the unsupported file type and re-run.

Do not store sensitive data in repo memory. It follows repository permissions, so use private repositories when appropriate, avoid secrets, set constraints such as file-glob, max-file-size, max-file-count, and max-patch-size, consider branch protection, and use target-repo when you want isolation.

See Deep Report and Daily Firewall Report for long-term insights and historical data tracking.

See Cache Memory for 7-day cache storage, Frontmatter for full configuration details, and Safe Outputs for output automation.