The Three-Hour Rule: How a Single Bash Command Closed a Policy Gap in Autonomous Fleet Management

The Message

[assistant] [bash] cd /tmp/czk && go build -o vast-manager-agent ./cmd/vast-manager/ 2>&1 | grep -v "sqlite3-binding\|warning:" | head -3 && echo "BUILD OK" && scp vast-manager-agent theuser@10.1.2.104:/tmp/vast-manager-agent && ssh theuser@10.1.2.104 "sudo systemctl stop vast-manager && sleep 1 && sudo cp /tmp/vast-manager-agent /usr/local/bin/vast-manager && sudo systemctl start vast-manager && sleep 5 && journalctl -u vast-manager --since '5 sec ago' --no-pager | grep -i 'hard-policy\|destroy\|loading'" 2>&1
# github.com/mattn/go-sqlite3
125566 |   zTail = strrchr(zName, '_');
       |         ^
BUILD OK

At first glance, message 4772 appears to be a routine deployment command: build a Go binary, copy it to a remote host, restart a systemd service, and verify the logs. But this single message represents the culmination of a critical policy evolution in an autonomous GPU proving fleet. It is the second deployment of a "hard policy" rule in under five minutes, and the fact that a second deployment was necessary tells a story about the dangers of incomplete specifications, the value of empirical verification, and the subtle art of translating human intent into machine policy.

The Context: A Fleet Bleeding Money

To understand why this message exists, we must step back into the minutes preceding it. The operator of a fleet of vast.ai GPU instances had just directed the assistant to implement a hard policy: instances that remain inactive for more than three hours should be automatically destroyed to stop storage charges ([msg 4750]). This was not a hypothetical concern. The fleet had accumulated six dead instances — four in exited state and two stuck in loading state for absurd durations of 3,261 hours and 1,080 hours respectively ([msg 4770]). These zombie instances were accruing storage charges indefinitely with zero productive output.

The assistant's first implementation of this policy, deployed in message 4769, targeted only exited and error statuses. The reasoning was straightforward: those are the canonical "dead" states. An instance that has exited or errored is clearly not doing useful work. The deployment succeeded, and the monitor dutifully destroyed the four exited instances. But when the assistant queried the fleet state immediately afterward ([msg 4770]), two instances remained — both in loading status, both having been in that state for over a thousand hours.

This discovery was the catalyst for message 4772.

The Reasoning: Closing the Gap

The assistant's thinking in the intervening message ([msg 4771]) is revealing: "The loading status wasn't in the hard policy's 3h rule. Let me add loading with >3h to the policy too." The tone is matter-of-fact, but the implication is significant. The original specification — "kill instances inactive >3hr" — was ambiguous. Does "inactive" mean "not running" or "not doing useful work"? A loading instance is technically active in the sense that vast.ai considers it to be provisioning, but after 3,261 hours of "loading," it is clearly never going to become productive.

The assistant had made an implicit assumption: that the vast.ai state machine has a clear boundary between "alive" states (running, maybe loading) and "dead" states (exited, error). The empirical evidence shattered that assumption. The fix was a one-line change to the hard policy condition in main.go, adding loading and scheduling to the set of statuses that trigger destruction after the three-hour threshold.

The Anatomy of the Deployment Command

The bash command in message 4772 is a carefully orchestrated pipeline that performs five distinct operations in sequence:

  1. Build: cd /tmp/czk && go build -o vast-manager-agent ./cmd/vast-manager/ compiles the Go binary. The grep -v filters out noisy compiler warnings from the sqlite3 C binding, and head -3 limits output to the first few lines. The && chain ensures the build must succeed before proceeding.
  2. Transfer: scp vast-manager-agent theuser@10.1.2.104:/tmp/vast-manager-agent copies the binary to the management host. Using /tmp as a staging location is a deliberate choice — it avoids permission issues and ensures the file is temporary.
  3. Deploy: The SSH command stops the vast-manager systemd service, waits one second (a brief settling period), copies the staged binary to /usr/local/bin/, and restarts the service. The sleep 5 after start gives the service time to initialize and run at least one monitor cycle.
  4. Verify: journalctl -u vast-manager --since '5 sec ago' --no-pager | grep -i 'hard-policy\|destroy\|loading' checks the service logs for evidence that the hard policy fired. The --since '5 sec ago' scope ensures only the current deployment's activity is captured.
  5. Output: The entire remote command's stdout is returned to the assistant, providing immediate feedback on whether the deployment succeeded and the policy engaged.

What the Output Reveals

The output is deceptively sparse. The build succeeds (with the usual sqlite3 warning), and then... nothing. No journal lines match the grep pattern. This silence is itself meaningful. It could mean the monitor cycle hasn't run yet, or that no instances currently match the hard policy criteria (the two loading instances may have already been handled in the previous cycle, or the 5-second window was too narrow). The assistant does not immediately flag this as a problem — the command was structured to surface information, and sometimes the information is "nothing happened yet."

Assumptions and Their Consequences

This message rests on several assumptions, some explicit and some implicit:

The build system is deterministic. The assistant assumes that go build will produce the same binary given the same source. This is generally true for Go, but the presence of CGO code (sqlite3) introduces a dependency on the local build environment.

SSH and systemd are reliable. The deployment assumes the remote host is reachable, sudo is available without a password prompt, and systemd will gracefully stop and start the service. These are reasonable assumptions for a managed infrastructure host but are not guaranteed.

The 5-second sleep is sufficient. The assistant assumes that within 5 seconds of startup, the monitor loop will have executed at least once and logged the hard policy activity. If the monitor has a longer initialization path (e.g., loading a cache from the database), this window could miss the relevant log lines.

The grep pattern is correct. The assistant searches for hard-policy, destroy, or loading in the journal. If the log message uses different terminology (e.g., "killing instance stuck in loading" vs. "destroying loading instance"), the grep would silently return no results, creating a false negative.

Input Knowledge Required

To fully understand this message, a reader needs:

Output Knowledge Created

This message produces several forms of knowledge:

The Deeper Lesson

Message 4772 is, on its surface, a mundane deployment. But it embodies a critical engineering lesson: policies derived from specifications are only as good as the specification's coverage of reality. The assistant's first implementation was correct according to the literal instruction — kill instances inactive for >3 hours — but it failed to capture the operator's intent, which was "stop paying for instances that will never become productive." The loading state, which the assistant had implicitly classified as "potentially active," turned out to be a graveyard.

This is not a failure of the assistant's reasoning. It is a fundamental property of translating human intent into formal rules. The operator could not have known to specify "also kill instances stuck in loading for 3,261 hours" because the existence of such instances was itself a discovery. The iterative cycle — implement, deploy, observe, refine — is the only reliable way to close the gap between specification and reality.

The message also demonstrates a mature approach to production deployments: build atomically, stage safely, restart cleanly, and verify immediately. The compound command structure, with its && chains and targeted log grepping, reflects an understanding that deployment is not complete until the system has demonstrated the desired behavior. The silence in the journal output is not ignored — it is preserved as evidence, to be interpreted in the next observation cycle.

In the broader narrative of the autonomous fleet management agent, message 4772 represents a small but essential hardening step. The hard policy is now comprehensive. The zombie instances that were bleeding money for thousands of hours will never recur. And the assistant has learned that "inactive" on vast.ai is a broader category than the documentation might suggest — a lesson that will inform every future scaling decision.