- Boot the scenario once — bring up your cluster (or adopt a running one that k7d already hosts) and wait until it is in the state you want every rollout to start from.
- Root a tree at that checkpoint.
- For each GRPO group (or tree-search step):
tree_fork_batch(N)→ run your N policies against the N copies → score →tree_protectthe winners,tree_prunethe losers → let the daemontree_auto_evictunder your RAM/disk budget. - Roll forward from a protected winner when you want the next generation to start from a better state, or
tree_rollbackto an earlier node when you don’t.
Why byte-identical starts matter for GRPO
GRPO (and most group-relative methods) compare rewards within a group. If member A starts from a colder cache, a different etcd revision, or a half-ready Deployment than member B, the reward gap is noise, not signal. A k7d fork is a copy of the live machine — same memory, same disk, same in-cluster TLS sessions, same kube-apiserver state. Every member of the group begins from a byte-identical world, then diverges only because of what your policy did. That is the difference between “we reset the env” and “we cloned the universe.”How your agent talks to the tree
The agent talks JSON-lines over a Unix socket (/run/k7d/k7d.sock). The verbs you actually need:
Full request/response shapes: Daemon API. Budget semantics and LRU eviction: Snapshot tree.
The demo as a template
A thin Python client that covers exactly this loop lives inexamples/cluster-tree-search/. Treat it as the template for wiring your GRPO trainer — not as a finished SDK.
--keep (leave the tree around for tree_nodes inspection), --tree-id NAME (stable tree id), K7D_SOCKET (override the control socket).
Sizing the loop
- A single 3-VM cluster fork is ~105 ms under API churn; a shared-pause batch of 50 clusters is ~4.1 s (~82 ms/cluster). See Benchmarks.
- Forks share memory until they diverge — RAM cost per group member is the dirty pages of its own rollout, not full guest RAM.
- Set the tree budget to your box:
max_live_vms,max_live_ram_bytes,max_disk_bytes. The daemon proactively suspends least-recently-active unprotected nodes at 85% of the RAM budget, so a long training run doesn’t OOM the host.
