Skip to content

Commit 75c4eda

Browse files
authored
Merge pull request #24 from KadenMc/feat/cross-machine-ledger
Cross-machine run ledger
2 parents b6bf6b9 + a8bdbe7 commit 75c4eda

16 files changed

Lines changed: 3171 additions & 27 deletions

CHANGELOG.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.6.0] - 2026-05-25
11+
12+
### Added
13+
14+
- **Cross-machine run ledger.** New `.aexp/ledger/<job_id>.json` files
15+
are sanitized projections of terminal-state runs, committed to git.
16+
Every machine sees the same view after `git pull` — the validator
17+
no longer needs to read filesystem-local signac workspace state to
18+
resolve finding citations. Auto-populated by a hook in
19+
`aexp.runs.mark_status` that fires on every terminal-status
20+
transition (complete/failed/abandoned/stopped). The projection is an
21+
explicit allowlist (statepoint, run_link, status, ended_at,
22+
wallclock_s, tracker pointers, code commit, registered_machine,
23+
promoted_at). Per-machine debris (absolute paths in
24+
`tracker_log/events.jsonl`, wandb offline-run dirs, user artifacts)
25+
stays in the gitignored `.runs/workspace/<id>/`. New `aexp.ledger`
26+
module with `promote_to_ledger`, `backfill_ledger`, and lookup
27+
helpers.
28+
- **`aexp ledger promote <id>` / `aexp ledger backfill` CLI verbs.**
29+
Manual one-shot promotion and a bulk migration tool. Each machine
30+
with terminal-state runs runs `aexp ledger backfill` once after
31+
upgrading; the resulting ledger files commit and push.
32+
- **`aexp validate --strict-runs={error|warn|off}` flag.** Manual
33+
severity knob for finding-citation existence checks. Default
34+
`error` preserves 0.5 behavior. `warn` downgrades existence
35+
failures to warnings (exit 0); `off` skips existence checks
36+
entirely. Structural-shape checks always emit at error severity
37+
regardless. Useful while the ledger is being backfilled across
38+
machines.
39+
- **`finding.absent_run_citation` warning code** for citations that
40+
resolve in a per-machine index file but not in the local store or
41+
ledger — distinguishes "lives on another machine" from "broken."
42+
Same treatment for batches: `finding.absent_batch_runs`.
43+
- **`finding.no_run_store` warning code** emitted once per validate
44+
run when no source-of-truth is available (no ledger, no local
45+
store, no index). Replaces a previously-silent tolerance branch
46+
that made it easy to overlook a missing local store.
47+
- **`.aexp/installed.json::machine_label` field.** Short identifier
48+
for each install, tagged on ledger entries via `registered_machine`.
49+
Default: short hostname. Override at install time with
50+
`aexp install --machine-label <name>`, or by editing
51+
`installed.json` directly. Sticky across re-installs. New
52+
`read_machine_label` helper in `aexp.utils.paths`.
53+
- **`aexp install` manages a `.gitignore` block.** Block-merged with
54+
`# agentic-experiments:begin`/`:end` markers. Body sets `.aexp/*` +
55+
`!.aexp/runs-index/` + `!.aexp/ledger/` so per-machine state stays
56+
ignored while cross-machine shared subdirs are committable. Emits
57+
a `gitignore_migration_warning` action when a legacy `.aexp/`
58+
pattern is detected outside the managed block (the legacy form
59+
parent-excludes `.aexp/`, blocking the `!` exceptions).
60+
- **`aexp runs-export-index` verb (transitional).** Per-machine JSON
61+
dump of terminal runs at `.aexp/runs-index/<machine_label>.json`,
62+
unioned by the validator into a three-state vocabulary. Deprecated
63+
immediately in favor of `aexp ledger backfill`; emits a
64+
deprecation warning when invoked. Kept one release window for
65+
back-compat reads.
66+
67+
### Changed
68+
69+
- **`aexp.runs.mark_status` is the unified terminal-status hook
70+
point.** `run_lifecycle`'s `complete`/`failed` status writes route
71+
through `mark_status` (with `set_ended_at=False` to avoid an extra
72+
doc-load inside the Windows file-lock race window during
73+
`stop_queued` shutdown). The function gains a `set_ended_at: bool
74+
= True` keyword; default preserves the pre-0.6 behavior for
75+
existing callers. New `TERMINAL_STATUSES` module constant.
76+
- **`aexp.linking.link_to_experiment` re-promotes on terminal jobs.**
77+
Re-stamping a job's run-link via `aexp link` now also updates the
78+
ledger entry's `run_link` field, so the ledger projection doesn't
79+
lag the on-disk state.
80+
81+
### Fixed
82+
83+
- Validator no longer silently passes finding citations when neither
84+
a local run store nor any cross-machine source exists — surfaces a
85+
`finding.no_run_store` warning so the silence is visible.
86+
87+
### Migration
88+
89+
Existing consumers upgrading from 0.5:
90+
91+
1. `pip install -e <agentic-experiments>` in each env.
92+
2. `aexp install --force` once per consumer (writes the new
93+
gitignore block, materializes `machine_label`). If a
94+
`gitignore_migration_warning` is emitted, delete the legacy
95+
`.aexp/` line from `.gitignore`.
96+
3. `aexp ledger backfill` on each machine that has terminal-state
97+
runs.
98+
4. Commit `.gitignore` and `.aexp/ledger/`. Push, then pull on every
99+
other consumer.
100+
101+
See `docs/queue.md` § cross-machine for the full workflow.
102+
10103
## [0.5.0] - 2026-05-21
11104

12105
### Changed

docs/queue.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,107 @@ recommended pattern is:
494494
- Cluster writes **during/after** execution (run-queued → status transitions).
495495
- Laptop doesn't touch `.runs/` while the cluster is running.
496496

497+
### Validator and citations across machines
498+
499+
If a `kb/research/findings/F###.md` cites a job that lives on the
500+
cluster but not your laptop, the validator on the laptop can't see
501+
the run directly — the signac project listing is filesystem-local.
502+
Before 0.6 this was a hard error. As of 0.6 the validator has three
503+
layered sources of truth, in order of preference:
504+
505+
1. **`.aexp/ledger/<job_id>.json`** — the universal cross-machine
506+
ledger. Sanitized projection of a terminal-state job, committed
507+
to git. Every machine sees the same ledger after `git pull`. This
508+
is the canonical "this run exists" source.
509+
2. **Local `.runs/workspace/<id>/`** — the signac project. Counts as
510+
"here" too, so freshly-promoted runs (after `mark_status` fires
511+
the auto-promote hook but before the next git push) are still
512+
recognized.
513+
3. **`.aexp/runs-index/<machine>.json`** — a transitional per-machine
514+
index. Deprecated; kept one release for back-compat. A citation
515+
matching only an index file (not in the ledger or local store)
516+
emits `finding.absent_run_citation` (warning) so you know a peer
517+
machine has the run but it hasn't been promoted yet.
518+
519+
The steady-state workflow:
520+
521+
```
522+
# cluster (after each batch finishes):
523+
aexp ledger backfill # promotes every terminal-state local run
524+
git add .aexp/ledger/
525+
git commit -m "ledger: backfill <date>"
526+
git push
527+
528+
# laptop:
529+
git pull
530+
aexp validate # exits 0 — every cited job resolves via ledger
531+
```
532+
533+
The auto-promote hook in `aexp.runs.mark_status` makes the backfill
534+
step optional in steady state — every `mark_status(job, "complete")`
535+
already writes the ledger entry. `aexp ledger backfill` is the
536+
migration path for runs registered before 0.6 (or in environments
537+
where the hook didn't fire for some reason).
538+
539+
#### --strict-runs escape hatch
540+
541+
If the ledger hasn't been backfilled everywhere yet (or you're
542+
running against an older install), `aexp validate
543+
--strict-runs={error|warn|off}` lets you downgrade or skip the
544+
existence check:
545+
546+
```
547+
aexp validate # exits 1: "ERROR finding.broken_run_citation ..."
548+
aexp validate --strict-runs=warn # exits 0: "WARNING finding.broken_run_citation ..."
549+
aexp validate --strict-runs=off # skips existence checks entirely
550+
```
551+
552+
`warn` is the right escape hatch during a partial rollout.
553+
Structural-shape checks (malformed citation, non-32-hex ids) always
554+
emit at error severity regardless — those are real authoring
555+
mistakes, not cross-machine ledger gaps.
556+
557+
### Migrating to the cross-machine ledger (0.5 → 0.6)
558+
559+
After upgrading aexp:
560+
561+
1. On every machine that has terminal-state runs:
562+
```
563+
pip install -e <agentic-experiments> # (you do this)
564+
aexp install --force # picks up new gitignore block + scaffold
565+
aexp ledger backfill # promotes every terminal-state job
566+
git add .gitignore .aexp/ledger/
567+
git commit -m "migrate to aexp 0.6 ledger"
568+
git push
569+
```
570+
2. On the laptop (or any other consumer of these runs):
571+
```
572+
git pull
573+
aexp validate # exits 0 — every cited job resolves via ledger
574+
```
575+
3. The transitional `aexp runs-export-index` verb still works for
576+
one release window, with a deprecation warning. Remove
577+
`.aexp/runs-index/` after every machine has backfilled.
578+
579+
If `aexp install --force` warns about a legacy `.aexp/` pattern in
580+
your gitignore (`gitignore_migration_warning`), edit `.gitignore` and
581+
remove the plain `.aexp/` line — git can't re-include
582+
`.aexp/runs-index/` or `.aexp/ledger/` while the whole `.aexp/`
583+
directory is excluded. The aexp-managed block (with `.aexp/*` +
584+
explicit `!` exceptions) replaces it.
585+
586+
### Machine identity
587+
588+
The ledger entries record which machine registered each run via the
589+
`registered_machine` field, sourced from
590+
`.aexp/installed.json::machine_label`. Default is short hostname
591+
(`socket.gethostname().split(".")[0]`); override at install time with
592+
`aexp install --machine-label <name>`, or by editing
593+
`.aexp/installed.json` directly (the file is per-machine and
594+
gitignored). On HPC clusters where per-node hostnames are noisy,
595+
set `--machine-label cluster` so the ledger is consistent across
596+
login nodes and compute nodes.
597+
497598
## FAQ
498599

499600
**What happens if the runner script is killed mid-job?**

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agentic-experiments"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "Git-first, hypothesis-forcing experiment tracking for agent-driven ML research. Bundles a research harness for the H->E->F artifact model, uses signac for local execution/run state, and bridges to W&B for remote observability."
55
authors = [
66
{name = "Kaden McKeen", email = "mckeenkaden@gmail.com"}

0 commit comments

Comments
 (0)