Skip to content

Commit ccf7f85

Browse files
test: gate st2 against bounded pty fleet snapshots
agent-session-id: dev3.dotfiles-cos-misc-agent-runtime-simplification agent-tool: Codex agent-tool-version: 0.145.0 agent-model: gpt-5.6-sol agent-runtime-profile: /home/schickling/.config/coding-agents/profile.json agent-skills-manifest: /nix/store/nk9iml2841l1yjjg0f6f0d3y60zkg1nn-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@4a0515f
1 parent 58c4851 commit ccf7f85

3 files changed

Lines changed: 95 additions & 1 deletion

File tree

flake.lock

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
inputs = {
55
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
66
flake-utils.url = "github:numtide/flake-utils";
7+
# Runtime compatibility gate: this is the smallest pty revision containing both the
8+
# ambiguity-safe PID read and the single fleet-wide socket fallback budget.
9+
pty.url = "github:compoundingtech/pty/6de78841e05ac5b9f4fbe844a71b6dfcc7c7f9a5";
10+
pty.inputs.nixpkgs.follows = "nixpkgs";
711
};
812

913
outputs =
1014
{
1115
self,
1216
nixpkgs,
1317
flake-utils,
18+
pty,
1419
}:
1520
flake-utils.lib.eachDefaultSystem (
1621
system:
@@ -151,6 +156,43 @@
151156
# wants them.
152157
checks.st2 = st2;
153158

159+
# Real producer-consumer contract: st2 consumes `pty list --json` from the exact pty
160+
# revision that owns fleet observation. Fake CLI fixtures below still cover malformed
161+
# output and a wedged child; this check proves the healthy 0/75/100/500-seat path crosses
162+
# both packaged binaries within st2's short outer deadline.
163+
checks.pty-fleet-contract = pkgs.runCommand "st2-pty-fleet-contract-${version}" {
164+
nativeBuildInputs = [
165+
pkgs.coreutils
166+
pty.packages.${system}.default
167+
st2
168+
];
169+
} ''
170+
export HOME=$(mktemp -d)
171+
catalog=$(mktemp -d)
172+
mkdir -p "$catalog/agents/contract/gone"
173+
printf '%s\n' \
174+
'agent "gone" { host "contract"; retired #true; command "true" }' \
175+
> "$catalog/agents/contract/gone/agent.kdl"
176+
177+
for fleet_size in 0 75 100 500; do
178+
root=$(mktemp -d)
179+
i=0
180+
while test "$i" -lt "$fleet_size"; do
181+
seat=$(printf 'seat-%03d' "$i")
182+
: > "$root/$seat.sock"
183+
printf '%s\n' "$$" > "$root/$seat.pid"
184+
i=$((i + 1))
185+
done
186+
187+
PTY_ROOT="$root" timeout 2s \
188+
st2 doctor --catalog "$catalog" --host contract \
189+
> "doctor-$fleet_size.out"
190+
grep -F 'contract.gone retirement complete' "doctor-$fleet_size.out" >/dev/null
191+
done
192+
193+
touch $out
194+
'';
195+
154196
# Smoke test that the built binary actually runs and its command tree is
155197
# wired, independent of the in-tree `cargo test`.
156198
checks.help = pkgs.runCommand "st2-help-${version}" { } ''

tests/doctor.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,36 @@ fn doctor_bounds_a_hung_pty_probe_and_reports_the_runtime_error() {
188188
assert!(stdout.contains("timed out after 2.0s"), "{stdout}");
189189
}
190190

191+
#[test]
192+
fn doctor_rejects_a_partial_pty_snapshot_atomically() {
193+
let tmp = tempfile::tempdir().unwrap();
194+
let catalog = tmp.path().join("catalog");
195+
let declaration = catalog.join("agents/h/gone/agent.kdl");
196+
let bin = tmp.path().join("bin");
197+
fs::create_dir_all(declaration.parent().unwrap()).unwrap();
198+
fs::create_dir_all(&bin).unwrap();
199+
fs::write(
200+
declaration,
201+
"agent \"gone\" { host \"h\"; retired #true; command \"true\" }\n",
202+
)
203+
.unwrap();
204+
executable(
205+
&bin.join("pty"),
206+
"#!/bin/sh\nprintf '[{\"name\":\"h.gone.agent\",\"status\":\"running\"},'\n",
207+
);
208+
209+
let output = doctor(&catalog, &bin, &tmp.path().join("state"));
210+
211+
assert!(!output.status.success());
212+
let stdout = String::from_utf8_lossy(&output.stdout);
213+
assert!(stdout.contains("✗ task runtime readable"), "{stdout}");
214+
assert!(stdout.contains("parsing `pty list --json`"), "{stdout}");
215+
assert!(
216+
!stdout.contains("retirement complete"),
217+
"a valid prefix must never be consumed as a partial snapshot:\n{stdout}"
218+
);
219+
}
220+
191221
#[test]
192222
fn retired_declaration_is_healthy_when_tasks_and_presence_are_absent() {
193223
let tmp = tempfile::tempdir().unwrap();

0 commit comments

Comments
 (0)