Ledger compaction

Standalone tools.ledger ledgers add one immutable shard on each run, so a ledger used often builds up many small segments. Compaction merges them into fewer, larger segments without losing history. Compaction belongs to the generated Agentic Maintenance workflow (agentics-maintenance.yml). Agent jobs never compact, rewrite, or delete ledger history.

Compaction is on by default and runs daily. Set it for each ledger:

tools:
ledger:
findings:
compaction:
schedule: weekly # daily (default), weekly, or manual
min-segments: 32 # compact only after this many small segments exist (2-256, default 32)
max-segments: 128 # maximum segments merged per plan (2-256, default 128)
metrics:
compaction: false # never compact this ledger

Maintenance may run more often than a ledger’s schedule. Each run checks whether the ledger is due and does nothing if it is not. A manual ledger is compacted only when compaction is requested.

Segment selection uses the built-in deterministic policy: eligible segments are considered in segment-ID order, up to max-segments, while keeping their combined records within max-segment-kb. Custom compaction.script settings are not supported.

When upgrading an existing workflow, remove compaction.script and retain only schedule, min-segments, and max-segments (or compaction: false). There is no replacement script hook. The built-in policy applies to typed and generic ledgers without changing record payloads or folding historical operations.

All compaction-enabled ledgers share two maintenance jobs:

JobPermissionsResponsibility
ledger_compaction_plancontents: readChecks each selected ledger branch, selects segments with the built-in policy, and uploads the created plans in one artifact
ledger_compaction_applycontents: writeDownloads the plans, validates each one against the latest ledger branch, and applies each in one commit

Each ledger runs in its own step. Plan steps have a 15-minute timeout, and a failed step does not prevent sibling ledgers from being processed. The plan job reports step failures after uploading any successful plans; the apply job still handles those plans and reports its own step failures after processing all of them. The jobs do not share a workflow concurrency group: apply revalidates the latest branch and commits only when its expected head still matches.

Each plan is only a proposal. The apply job runs no user JavaScript and treats each plan as hostile input. Before it writes to a ledger, it:

  • rejects unknown keys, oversized plans, wrong ledger or branch names, and a plan_id that does not match the plan contents.
  • reloads the latest ledger and checks every source segment against its SHA-256 hash, size, and sorted record hashes.
  • rebuilds the replacement segment from those verified records and confirms that no record is lost.
  • publishes the change in one commit that applies only if the branch head is the one it validated against. Segments added after planning are kept.

Plans use version gh-aw/ledger-compaction-plan/v1 and contain exactly these keys:

KeyContent
version, ledger, branchPlan version and target ledger
triggerscheduled or requested
created_at, base_commitWhen and from which branch commit the plan was made
sourcesSorted segments to retire: segment, sha256, bytes, records (sorted record SHAs)
replacementThe new segment, described with the same fields
plan_idSHA-256 of the canonical ledger, branch, version, and source and replacement identities

A given state transition always gets the same plan_id. After a successful apply, ledger/compaction/state.json on the ledger branch records the plan ID and the time it was applied.

The apply job reports one of these results in its step summary, together with the ledger name, trigger, plan ID, segment and record counts, and bytes before and after. Ledger payloads are never logged.

ResultMeaning
appliedThe plan was committed
already_appliedThe plan’s effect is already present; nothing changed
staleSome sources were already retired; the next run plans again
conflictA referenced segment changed; the next run plans again
rejectedThe plan failed validation; the job fails and nothing changed

If a commit attempt fails, for example because the branch moved or GitHub is unavailable, the apply job fetches the branch again and revalidates before retrying. It never overwrites the branch. A planning, artifact, or validation failure leaves the ledger unchanged.

When at least one ledger has compaction enabled, agents get the ledger_request_compaction safe output with an optional ledger and reason. The ledger value is required only when the workflow has more than one compaction-enabled ledger. This safe output never compacts anything itself. It dispatches Agentic Maintenance with operation: compact_ledger and the ledger name, and the maintenance jobs above handle the request the same way as a scheduled run. To allow the dispatch, the safe-outputs job gets actions: write.

You can also start compaction manually: run Agentic Maintenance with the compact_ledger operation and an optional ledger input. Leave ledger empty to consider every ledger.