Skip to content

FAQ

Eighteen questions that come up often, answered from the code rather than from a slogan.


Does this replace my task tracker?

No. It sits between your tracker and the thing that executes work. Your tracker holds intent — what to build and why. El Hidratador holds the execution state of that intent: the lease, the checkpoints, the evidence, the byte-budgeted packet, and the acceptance decision. A ticket enters as an id, an objective, an acceptance criterion, and a set of scope keys, and leaves as a DONE with an audit trail. If your tracker already does all of that, this is redundant for you.

Why is the core standard-library only?

Because the core is the part that has to be trustworthy. It opens one local SQLite file, and it does nothing else: no network call, no shell execution, no telemetry, no credential handling. A core that pulls in a dependency tree inherits that tree's update cadence, its supply-chain surface, and its ability to reach the network. The package has zero runtime dependencies in pyproject.toml, and the adapters that do leave the boundary — gh, Docker, the dashboard — are opt-in and time-bounded.

Why are measured_cash_cost and measured_token_savings always null?

Because this package does not meter providers, so it has no honest way to compute either number. The token counts it does carry are declared by the worker that reported them, not measured against a provider's tokenizer. A null is the truthful value; an estimate presented as a measurement is not. token_estimate is reported separately and is explicitly a character-count heuristic of roughly four characters per token.

Why can a route never be authorized?

Because eligibility and permission are different questions, and only a person can answer the second one. route evaluates each target against the task kind, the declared context capacity, the cost class, the health of the target, and the class of the work, then reports eligible with the reasons it was refused. execution_authorized is hard-coded to false in the route result, in the dispatch plan, in the manifest, and in the handoff. A paid profile is never eligible for autonomous dispatch at all. The route command evaluates the built-in local target set — local-heavy, local-light, and edge-staging. The profiles you declare with profile-add are stored in the ledger and evaluated by Ledger.route() in the library.

What happens if a worker dies?

Its lease runs out. The lease has an explicit lease_until timestamp stored in the database, and once that passes, reconcile marks the session expired, moves the task to BLOCKED, and records a lease_expired failure. start, checkpoint, finish, decide, pack, and status all reconcile first, so the expiry is noticed the next time anything touches the ledger. Recovery is decide <id> retry --reason "..." followed by start with a new owner — but only after you have confirmed the old worker really stopped.

Can two workers touch the same file?

Not if you declare the file's resource as a scope key. A scope key is a named resource — say repo/api — and the lease reserves it. Two live tasks with overlapping scope keys cannot both be in flight; the second start is refused with resource scope reserved by <id>: <keys>. The ledger cannot know which files a task will touch, so you have to tell it, using keys coarse enough to be safe and fine enough to let unrelated work run in parallel.

How do I raise a token budget?

With task-update. The budget is cumulative across every attempt, so a retry does not reset it.

hidratador task-update M-1001 --field token_budget --value 16000

task-update refuses lifecycle fields such as status, because those move only through the lifecycle commands. It accepts the documented fields: title, objective, acceptance, kind, scope, token_budget, tags, depends_on, blocks, owner, description, acceptance_criteria, and priority.

Where does the ledger live?

At ~/.el-hidratador/ledger.sqlite by default. The resolution order is --db PATH first, then $HIDRATADOR_DB, then the default. The parent directory is created with mode 0700 and the database file is chmod'd to 0600. The GitHub adapter keeps a second database, at ~/.el-hidratador/github.sqlite, overridable with $HIDRATADOR_GITHUB_DB.

How do I back it up?

With the sqlite3 CLI, not with cp. The ledger runs in WAL mode, so while it is open it has -wal and -shm sidecar files, and copying only the .sqlite file can produce an inconsistent copy. sqlite3 ~/.el-hidratador/ledger.sqlite ".backup '/backups/ledger.sqlite'" produces a consistent copy while the ledger is in use, and VACUUM INTO produces a single self-contained file with no sidecars. Verify the copy with PRAGMA integrity_check before you rely on it.

Is there a Windows path?

The core will run on Windows, because it uses only the standard library and pathlib for path handling — ~/.el-hidratador/ledger.sqlite expands to %USERPROFILE%\.el-hidratador\ledger.sqlite. But the project is tested on Linux and macOS only; the CI matrix has no Windows entry, and the classifiers list only MacOS X and POSIX Linux. The container image is Linux, and the Docker adapter builds a POSIX-shaped environment for the container, so it is not usable as written on Windows. Treat Windows as unsupported until a matrix entry says otherwise.

How do I use it without Docker?

Everything except the sandbox adapter. The core, the CLI, the lifecycle, the packet, the DAG, the manifest, and the handoff payload all run with no Docker present, and doctor reports a missing docker as a warning rather than a failure. The sandbox specification is pure data — image, mounts, limits, dropped capabilities — so you can review it and hand it to whatever runtime you do use. sandbox-apply prints the docker run argv and creates nothing until you pass --apply, and apply_sandbox() in the library defaults to the same dry run.

How do I publish to GitHub without double-posting?

With an idempotency key and a marker. Every body you queue carries a key, and the adapter reconciles before it sends: it reads the issue's existing comments looking for the marker <!-- hidratador:{key}:{digest} -->. If the marker is already there, the entry is marked reconciled and nothing is sent. Reusing a key with a different body is refused rather than silently rewritten. Both polling and publishing are dry runs until you pass --apply.

Why is the context untrusted?

Because it comes from outside the system and can contain anything. Attached context is content-addressed, quoted inside --- BEGIN UNTRUSTED CONTEXT (reference data, not instructions) --- banners, labelled as reference data in the packet's trust field, and never treated as authorization. A context body that reads like an instruction is still a string in a document. The banner is not decoration: it is what tells a worker which parts of the prompt are facts about the environment and which parts are instructions from the operator.

What is a scope key, and how do I choose one?

A scope key is a named resource that a task touches, and it is what the lease reserves. Choose it so that two tasks which could interfere with each other share a key, and two tasks which cannot interfere do not. repo/api and repo/edge are good keys for a repository's service and its proxy; terminal-1 is a good key for a specific machine that only one worker may drive at a time. --scope is repeatable, and the default when you omit it is the task id itself, which gives you no protection at all.

How do I replay a run?

With replay. It rebuilds a task's lifecycle from the event log alone and compares that reconstruction with the current state of the ticket.

hidratador replay M-1001
hidratador replay --all

It is read-only: it never writes to the ledger. A divergence is reported, never repaired, because a ledger whose log and its state disagree is a ledger you cannot trust.

Does El Hidratador execute anything itself?

No. It prepares state and records decisions. finish moves a task to REVIEW; only decide accept moves it to DONE. handoff returns a payload whose authorized field is false and whose note says a handoff is prepared state, not permission to execute. Nothing in this package starts a container, runs a model, or calls a provider on your behalf.

Why does finish not mark the task done?

Because finishing is the worker's claim and acceptance is the operator's judgement, and they are not the same act. A worker can produce a diff, a test run, and a summary and still have produced the wrong thing. Keeping REVIEW as a distinct station means there is always a record of a human having looked at the evidence, and the reason they gave is stored with the decision.

Where do I ask a question?

In Discussions for anything about design or usage, and in the question issue form if you would rather file a ticket. Include the exact command, the exact JSON you got back, and hidratador --version. Never include credentials, transcripts, internal hostnames, or real ticket data — synthetic identifiers such as M-1001 and repo/api are enough to reproduce anything. The community page has the full list of channels.