Skip to content

Repository files navigation

ldgr

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.

Core ideas

  1. Bound the work. One work item per loop cycle, nothing more.
  2. Externalize memory. Observations, artifacts, and decisions live in the ledger, not in a context window.
  3. Decide what happens next. Each run ends with a decision: continue with a next work item, stop, or record why the work cannot proceed.
  4. Separate discovery from execution. New problems become queued work items, not detours that derail the current run.
  5. 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.

Install

curl -fsSL https://raw.githubusercontent.com/hydra-dynamix/ldgr-core/main/scripts/install.sh | sh

On native Windows, run this from PowerShell:

irm https://raw.githubusercontent.com/hydra-dynamix/ldgr-core/main/scripts/install.ps1 | iex

The installer detects the current OS/CPU, downloads the matching release archive, verifies its SHA-256 checksum when checksum tooling is available, and installs ldgr 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:

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 schema, generated adapter contract set, pending migrations, and last verified backup without changing the database. Upgrade Core before adapters; Core applies the complete migration plan atomically and refuses unknown shapes or incompatible sidecars. See docs/database-upgrade-and-recovery.md in the LDGR repository for the backup and restore procedure.

Numerical sequence telemetry

LDGR can optionally share numerical state-transition sequences for research. Collection is disabled until an explicit telemetry choice is recorded. The first interactive ldgr install asks for Yes or No with no default; non-interactive installs must pass --telemetry enable or --telemetry disable because --yes is not telemetry consent. Later installs remember the stored decision.

When enabled, Core buffers only committed terminal sequences as bare JSON integer arrays under ~/.ldgr/telemetry-pending/<protocol>/. The released v1 protocols are core-work/v1 and research-workflow/v1; adapters inherit Core consent and must use Core-owned buffering and transmission.

ldgr telemetry status
ldgr telemetry preview
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

preview prints the exact raw arrays and destination endpoints without sending them. transmit is best-effort, HTTPS-only, and can also read the collector origin from LDGR_TELEMETRY_COLLECTOR; failed sends are retained locally and do not affect ordinary LDGR commands. disable requires no network request, deletes unsent local payloads, and sends no final event. LDGR_TELEMETRY=off disables collection and transmission for the current process without changing the stored choice.

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.

Quick start

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 --json

That 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.

Structured queues and portable schedules

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 --upsert

The 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.

The autonomous loop

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 anything

Detached 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 --json

Completion-grade CLI E2E gate

On 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 --nocapture

Every 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 cleanroom

Loop 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."

Daily use

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?

Web cockpit

ldgr web            # serves http://127.0.0.1:8686

A 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.

Rust library

The ldgr-core crate also exposes Rust modules for applications that want to build on the same ledger:

  • adapter_manifest for public adapter manifest parsing and validation, including optional command namespace declarations.
  • store for the SQLite-backed work, run, observation, artifact, decision, prompt, notice, event, and context records.
  • loop_runtime for bounded autonomous loop execution.
  • cli for the command runner used by the ldgr binary.
  • web for the local cockpit server.
  • tool_runner for command rendering and argv parsing helpers.

Adapter command manifests

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.

Where data lives

State lives in .ldgr/ inside the project where you run ldgr:

  • .ldgr/ldgr.db is 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.

License

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.

About

LDGR core durable investigation loop

Resources

Contributing

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages