A minimal durable investigation loop backed by SQLite.
ldgr gives autonomous agents (and the humans steering them) a durable loop for
work items, runs, observations, decisions, and context. Instead of trusting a
model to keep its own task list and memory, ldgr keeps them in one SQLite file
that survives restarts, crashes, and context resets.
- Bound the work. One work item per loop cycle, nothing more.
- Externalize memory. Observations, artifacts, and decisions live in the ledger, not in a context window.
- Decide what happens next. Each run ends with a decision: continue with a next work item, stop, or record why the work cannot proceed.
- Separate discovery from execution. New problems become queued work items, not detours that derail the current run.
- Start small. The core loop is intentionally compact; learn the basic work/run/decision rhythm before adding more process.
See docs/ldgr-loop-philosophy.html for the longer explanation of the loop.
curl -fsSL https://raw.githubusercontent.com/hydra-dynamix/ldgr-core/main/scripts/install.sh | shOn native Windows, run this from PowerShell:
irm https://raw.githubusercontent.com/hydra-dynamix/ldgr-core/main/scripts/install.ps1 | iexThe installer requires Python 3, verifies the signed Core catalog with an
embedded Ed25519 trust root, checks the archive checksum and detached signature,
binds RELEASE-METADATA.json, and installs paired ldgr and agentctl.
It installs to ~/.local/bin by default. Override with:
LDGR_VERSION=0.1.9 LDGR_INSTALL_DIR="$HOME/bin" sh -c "$(curl -fsSL https://raw.githubusercontent.com/hydra-dynamix/ldgr-core/main/scripts/install.sh)"Source install remains available: Key rotation, release ordering, mirrors, and air-gapped installer inputs are documented in docs/release-signing.md.
cargo install --git https://github.com/hydra-dynamix/ldgr-core --locked --force --package ldgr-core
# or, from a source checkout:
git clone https://github.com/hydra-dynamix/ldgr-core
cd ldgr-core
cargo install --path .SQLite is bundled; source fallback requires a recent stable Rust toolchain.
Before an upgrade, ldgr schema doctor inspects the active central schema,
registered components, pending migrations, and last verified backup without
changing the database. Compatibility-v2 discovery separately evaluates adapter
protocol, monotonic minimum Core schema, required capabilities, and optional
central components; it does not require exact global release-set identity. See
docs/database-upgrade-and-recovery.md in the LDGR integration repository for
the backup, rollback, legacy diagnosis, and restore procedure.
The top-level updater can authenticate the configured Core and adapter catalogs and resolve one compatibility-bound plan without changing the installation:
ldgr update --check
ldgr update --check --json
ldgr update --check --core-only
ldgr update --check --adapters-only
ldgr update --check --adapter research --adapter conduct
ldgr update --check --prerelease
ldgr update --check --offline--json writes exactly one schema-versioned result to stdout; diagnostics stay
on stderr. Check mode may update the user-level check cache under ~/.ldgr, but
never downloads release archives, runs installers, or changes installed files
or the current project. Apply with ldgr update (or deliberate non-interactive
ldgr update --yes): Core stages and verifies the whole plan before mutation
and rolls back Core/agentctl, adapter bundles, resources, receipts, and central
database bytes together on failure. --core-only cannot bypass an incompatible
retained adapter.
LDGR can share privacy-minimized numerical constructions for research.
Anonymous construction telemetry is enabled by default and can be opted out at
any time. Installation never prompts for telemetry. ldgr telemetry disable
records an opt-out. Experience donation is separate, non-anonymous, disabled by
default, and requires ldgr telemetry donation enable. After opt-in, Core
captures completed rich work episodes automatically. The detached shutdown and
startup-retry worker drains both queues. Donation disablement removes unsent
rich episodes without changing anonymous consent.
Core maps raw local command/run data immediately into a finite ontology and
stores only consolidated numerical construction keys and bucketed counts. A
command-experience/v1 construction is suppressed below local support five,
released once per seven-day window, and subject to a 20-construction window cap.
Pending anonymous payloads remain bare JSON integer arrays under
~/.ldgr/telemetry-pending/<protocol>/. Opted-in rich episodes use the separate
~/.ldgr/experience-donation-pending/experiences/v1/ queue.
ldgr telemetry status
ldgr telemetry preview
ldgr telemetry transmit
ldgr telemetry transmit --collector https://collector.example
ldgr telemetry transmit --collector https://collector.example --root-ca-pem /path/to/ca.pem --max-delay-ms 30000 --timeout-ms 10000
ldgr telemetry disable
ldgr telemetry donation statuspreview performs the release check and prints exact raw arrays, endpoints, and
decoded command constructions without sending them. transmit is best-effort,
HTTPS-only, and resolves its origin from the flag, environment, then
https://ldgr.run.
Core also schedules a detached transmission worker after each normal CLI exit.
If a process exits unexpectedly, the pending files remain durable. The next
valid CLI startup detects the files and schedules the worker. One local lock
serializes automatic and explicit transmission. Automatic delivery uses
LDGR_TELEMETRY_COLLECTOR, then https://ldgr.run. Private collectors can set
LDGR_AUTOMATIC_TELEMETRY_ROOT_CA_PEM to one additional PEM trust anchor.
Failed sends remain pending and never affect ordinary commands. Set
LDGR_NO_AUTOMATIC_TELEMETRY=1 to suppress detached delivery without changing
collection consent. disable requires no network request, deletes pending
payloads and local construction aggregates, and sends no final event.
Already-ingested sequences cannot be individually located for deletion because the collector intentionally receives and stores no user, installation, request, timestamp, or join identifier. Disabling prevents future collection and removes unsent local data only.
ldgr init # create .ldgr/ldgr.db and print the on-ramp
ldgr work create my-first-task \
--title "Investigate X" \
--description "Figure out why X happens and record evidence."
ldgr work edit my-first-task --description "Figure out why X happens; record evidence."
ldgr work status set my-first-task pending
ldgr run start my-first-task --command "manual investigation"
ldgr observe my-first-task --body "X happens when Y."
ldgr artifact add my-first-task --kind report --path notes/x-report.md --description "Investigation notes."
ldgr artifact show 1
ldgr run close my-first-task --status success --outcome continue \
--rationale "Y confirmed as the trigger." \
--next-slug fix-y --next-title "Fix Y" --next-description "Patch Y handling."
ldgr status # compact agent-first status summary
ldgr context --brief # compact agent on-ramp
ldgr status --json # compact machine-readable handoff
ldgr context # the operational cockpit, also: ldgr context --jsonThat loop is the day-one model: work, run, observation, artifact, decision,
notice, and context. ldgr observe is a shorthand for recording run observations;
ldgr observation add remains available. Commands that attach evidence to a run
accept either numeric run IDs or work-item slugs. ldgr run close is the
recommended closure path for active runs because it records the terminal run
status and work decision together.
Use ldgr --help or ldgr <command> --help to explore the command surface.
CLI inputs are normalized conservatively for agent use; see
docs/cli-input-and-error-policy.md.
When a non-destructive parse error has one plausible correction, LDGR prints
the corrected invocation and saves it for a one-shot ldgr rerun.
Work items can carry priority, program, group, acceptance criteria, and enforced
dependencies. Dependencies form an acyclic graph: an item is not ready and
cannot be started until every prerequisite is done. Priority is a non-empty
label: P0, P1, and similar values retain numeric ordering, while common
labels such as critical, high, medium, and low are also ordered.
ldgr work create registry --title "Registry" --description "Build it." \
--priority P0 --program audit --group accounts
ldgr work create atomicity --title "Atomicity audit" --description "Audit updates." \
--priority P0 --program audit --group accounts \
--acceptance-criteria "Concurrent update test passes." \
--depends-on registry
ldgr work dependency add atomicity schema
ldgr work dependency remove atomicity schema
ldgr work graph --blocked
ldgr work graph --format mermaid
ldgr work audit
ldgr status --program audit --priority P0--depends-on accepts comma-separated slugs, repeated flags, or both. work edit --depends-on ... replaces the complete dependency set; use work dependency add/remove to change one edge. work show and work list --json
include dependency and dependent status, satisfaction, effective readiness, and
blocker reasons.
Use a JSON schedule to create or back up a large queue in one command. Imports
are transactional, and --upsert updates matching slugs.
ldgr work export --output .ldgr/schedule-backup.json
ldgr work export --example
ldgr work import schedule.json --dry-run
ldgr work import schedule.json
ldgr work import schedule.json --upsertThe portable format is ldgr.schedule.v1; exported records include lifecycle
status, structured metadata, hold classification, and dependency slugs. The
SQLite ledger remains the source of truth for run history and evidence, while
the schedule export is suitable for versioned queue backup.
ldgr loop run drives an agent through one or more bounded cycles: each cycle
picks the next pending work item, renders a prompt with the current ledger
context, pipes it to the configured agent, and records the output as a run
artifact. Prompts can come from an editable file path, a durable active prompt
record, or a sealed prompt bundle. Use --max-iterations N to run multiple
cycles; the loop stops early when work is blocked, no pending work remains, or a
subprocess fails.
ldgr loop run --prompt prompts/loop-prompt.md --agent agentctl # use the ldgr-loop agentctl entry from ldgr install
ldgr loop run --prompt-slug surface --agent agentctl # use an active stored prompt
ldgr loop run --bundle cleanroom --prompt-role surface-loop # use a sealed bundle
ldgr loop run --prompt prompts/loop-prompt.md --agent-argv '["my-agent"]' # any command that reads the prompt on stdin
ldgr loop run --prompt prompts/loop-prompt.md --agent agentctl --until-empty --detach # background process with durable logs
ldgr loop run --prompt prompts/loop-prompt.md --dry-run # render artifacts without spawning anythingDetached loops print their PID and write stdout/stderr under .ldgr/logs by
default. On Windows, loop children also receive HOME from USERPROFILE when
HOME is absent. See Detached loops on Windows.
ldgr install writes ~/.agentctl/config.toml entries named ldgr-loop and
ldgr-loop-<harness> so the built-in --agent agentctl runner can call
agentctl run ldgr-loop and stream the rendered prompt through stdin.
Core 0.1.14 is released with agentctl 0.1.2 in the same checksum-covered
archive. Before an LDGR-owned profile starts a worker, agentctl negotiates
ldgr.launcher-compatibility.v1 with the resolved Core binary. Mixed or older
Core installations stop with a durable agentctl.compatibility/core-incompatible
error and upgrade instructions instead of losing the accepted attempt.
ldgr compatibility --agentctl-version 0.1.2 --json
agentctl discover --jsonOn a Windows CI runner, one shell-neutral Cargo entrypoint runs the maintained fresh-project schema/migration/source matrix, the signed offline release-adapter lifecycle, and the live loopback web-safety probes:
cargo test --test cli_e2e_gate -- --ignored --nocaptureEvery invocation retains a unique directory under target/cli-e2e-gate/ with
separate stdout/stderr logs for each safety class, the full matrix result, and
an aggregate ldgr.cli-e2e-gate-result.v1 JSON document. The gate runs every
probe even after an earlier failure and rejects successful process exits when a
test filter selects the wrong count or the matrix reports a semantic failure.
Non-Windows runners fail this ignored completion target explicitly instead of
reporting a misleading zero-test success; keep the completion job on Windows.
Prompt records live in the ledger with slug, role, body, hash, status, and version history. Updating a prompt creates a new version while preserving prior content. Prompt bundles seal active prompt versions into an immutable manifest and bundle hash:
ldgr prompt create surface --role surface-loop --body '... {{ldgr_context}} ...'
ldgr prompt import implementation --role implementation-loop --path prompts/impl.md
ldgr prompt update surface --path prompts/surface-v2.md
ldgr prompt activate surface
ldgr bundle create cleanroom --prompt surface --prompt implementation
ldgr bundle seal cleanroomLoop runs that use stored prompts or bundles write prompt provenance artifacts with the exact prompt slug, version, content hash, and bundle hash used.
Operator steering outside a run is represented as notices:
ldgr notice add --kind notification --body "Prefer the simpler fix in module Z."
ldgr notice edit 1 --body "Course correction handled."
ldgr notice clear 1 --reason "Applied."ldgr is designed to be used continuously while work is happening:
- Start with one concrete work item.
- Start a run when you or an agent begins that work.
- Record observations as facts become clear.
- Attach artifacts when files, reports, logs, or notes matter.
- Close the run with a decision and, when appropriate, queue the next bounded piece of work.
The goal is not to create a large planning database up front. The goal is to keep a durable handoff that always answers: what is active, what was observed, what was decided, and what should happen next?
ldgr web # serves http://127.0.0.1:8686A live dashboard over the ledger: work distribution, execution flow,
decisions, observations, artifacts, and loop controls. Even on loopback, all
mutating routes require X-LDGR-Control-Token. When --control-token is not
provided, ldgr web generates an ephemeral token at startup and prints a local
URL containing ?control_token=...; the bundled UI stores that value in browser
session storage before posting mutations. Exposing the cockpit beyond loopback
requires --unsafe-expose together with an explicit --control-token.
The ldgr-core crate also exposes Rust modules for applications that want to
build on the same ledger:
adapter_manifestfor public adapter manifest parsing and validation, including optional command namespace declarations.storefor the SQLite-backed work, run, observation, artifact, decision, prompt, notice, event, and context records.loop_runtimefor bounded autonomous loop execution.clifor the command runner used by theldgrbinary.webfor the local cockpit server.tool_runnerfor command rendering and argv parsing helpers.
Open adapter manifests may omit command extensions. When an adapter wants core
to expose an adapter-owned command namespace, declare one or more [[commands]]
tables:
[[commands]]
namespace = "community-sample"
argv = ["community-sample"]
aliases = ["sample", "community"]
title = "Community sample commands"
description = "Commands exposed through the core LDGR command surface."
capabilities = ["dispatch", "help"]
[commands.help]
usage = "ldgr community-sample <command> [options]"
summary = "Run community sample adapter commands."
details = "Arguments after the namespace are forwarded to the adapter executable."ldgr::adapter_manifest::parse_adapter_manifest validates namespace syntax,
duplicate command aliases, empty argv, and malformed command declarations with
clear errors while preserving existing manifest digest behavior.
State lives in .ldgr/ inside the project where you run ldgr:
.ldgr/ldgr.dbis the SQLite ledger..ldgr/artifacts/stores managed artifacts created by LDGR.
The ledger is local-first and survives restarts, crashes, and context resets.
You can inspect the current handoff at any time with ldgr status or
ldgr context. Released schema-v1 ledgers migrate transactionally to schema v2
when first opened; existing work, runs, evidence, and decisions are preserved.
Licensed under the Apache License, Version 2.0.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be licensed as Apache-2.0, without any additional terms or conditions.