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.
Configuration
Section titled “Configuration”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 ledgerMaintenance 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.
Trust boundary
Section titled “Trust boundary”All compaction-enabled ledgers share two maintenance jobs:
| Job | Permissions | Responsibility |
|---|---|---|
ledger_compaction_plan | contents: read | Checks each selected ledger branch, selects segments with the built-in policy, and uploads the created plans in one artifact |
ledger_compaction_apply | contents: write | Downloads 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_idthat 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.
Plan format
Section titled “Plan format”Plans use version gh-aw/ledger-compaction-plan/v1 and contain exactly these keys:
| Key | Content |
|---|---|
version, ledger, branch | Plan version and target ledger |
trigger | scheduled or requested |
created_at, base_commit | When and from which branch commit the plan was made |
sources | Sorted segments to retire: segment, sha256, bytes, records (sorted record SHAs) |
replacement | The new segment, described with the same fields |
plan_id | SHA-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.
Results and retries
Section titled “Results and retries”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.
| Result | Meaning |
|---|---|
applied | The plan was committed |
already_applied | The plan’s effect is already present; nothing changed |
stale | Some sources were already retired; the next run plans again |
conflict | A referenced segment changed; the next run plans again |
rejected | The 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.
Requesting compaction
Section titled “Requesting compaction”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.