docs(plan): implementation plan for the wait-for readiness gate

Six TDD tasks: schema + KDL parsing, the Waiting state and WaitInfo, a
ready module that evaluates one condition, the interruptible gate inside
do_start, CLI surfacing, and the README.

Also corrects the spec: xy-protocol has no etcetera dependency, so ~ is
expanded from $HOME; and in KDL `command`/`args` are sibling nodes inside
the wait-for block, which changes how the parser counts conditions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EGntTHCW3sEPy1VBRopNNp
This commit is contained in:
2026-08-07 15:10:48 +02:00
co-authored by Claude Opus 5
parent ba04cb1ec9
commit 73e17d51f3
2 changed files with 1195 additions and 4 deletions
File diff suppressed because it is too large Load Diff
@@ -66,10 +66,25 @@ Exactly one condition per `wait-for` block. Zero or more than one is a parse
error, so `xy reload` exits 3 and changes nothing — matching how invalid
config already behaves.
`path` values get `~` expanded at parse time via `etcetera::home_dir()`, since
the motivating case is literally `~/.orbstack/run/docker.sock`. The expanded
path is what `xy status` displays, so there is no ambiguity about what was
checked.
`path` values get a leading `~/` expanded at parse time from `$HOME`, since the
motivating case is literally `~/.orbstack/run/docker.sock`. `$HOME` rather than
`etcetera::home_dir()` deliberately: `xy-protocol` is a leaf crate with no
directory dependencies, and adding one for a two-line prefix substitution is
not worth it. If `$HOME` is unset the literal path is kept, which simply never
exists and surfaces as a normal timeout. The expanded path is what `xy status`
displays, so there is no ambiguity about what was checked.
Note the KDL shape of the `command` kind: `command` and `args` are **sibling
nodes** inside the block, mirroring the top level, not one node with trailing
arguments:
wait-for {
command "docker"
args "info"
}
so the parser accumulates them separately and combines them after the loop.
`args` without `command` is a parse error.
The `command` kind is bounded by the poll `interval`: each invocation is run
under a timeout of `interval` and a non-exit is treated as not-ready, so a hung