fix(session): report the frame a pause began at

Frames go on counting while paused, since Pump still runs, but status
gave no pause frame and `wait --paused` replied with the current one.
A wait that came minutes after `pause --on-check` caught a failure at
frame 237 said "paused at frame 5475", which an agent takes for when
the check failed.

The session records the frame in PauseNow, and status and the paused
wait report it as pausedAt. The client prints "PAUSED since frame N",
and "paused at frame N (now M)" when the two differ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-11-01 03:21:41 +01:00
commit 2c77cdcfab
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
3 changed files with 29 additions and 5 deletions

View file

@ -269,6 +269,9 @@ namespace Crafter::Debug {
bool armedOnCheck = false;
bool armedTrap = false;
std::string pauseReason;
// The frame the pause began at. Frames go on counting while paused,
// so the current one says nothing about when it stopped.
std::uint64_t pausedAt = 0;
bool shutDown = false;
// The Pump thread's own.
@ -333,11 +336,12 @@ namespace Crafter::Debug {
}
void PauseNow(std::string reason, bool trap) {
paused.store(true);
{
std::lock_guard lock(mutex);
pauseReason = reason;
pausedAt = frame.load();
}
paused.store(true);
Detail::Timeline::Instance().Append(Detail::MakeEntry(EntryKind::Pause, "debug", std::format("paused: {}", reason), std::source_location::current()));
if (trap && TracerAttached()) ::raise(SIGTRAP);
}
@ -429,6 +433,7 @@ namespace Crafter::Debug {
{
std::lock_guard lock(mutex);
status.Set("pauseReason", Value(pauseReason));
if (paused.load()) status.Set("pausedAt", Value(pausedAt));
status.Set("queued", Value(jobs.size()));
status.Set("waiting", Value(waiters.size()));
status.Set("following", Value(followers.size()));
@ -615,7 +620,7 @@ namespace Crafter::Debug {
waiter.kind = WaitKind::Paused;
if (paused.load()) {
std::lock_guard lock(mutex);
return exchange->Reply(MakeReply(Object({ { "frame", Value(frame.load()) }, { "reason", Value(pauseReason) } })));
return exchange->Reply(MakeReply(Object({ { "frame", Value(frame.load()) }, { "pausedAt", Value(pausedAt) }, { "reason", Value(pauseReason) } })));
}
}
std::lock_guard lock(mutex);
@ -914,11 +919,13 @@ namespace Crafter::Debug {
case WaitKind::Paused:
if (paused.load()) {
std::string reason;
std::uint64_t since = 0;
{
std::lock_guard lock(mutex);
reason = pauseReason;
since = pausedAt;
}
waiter.exchange->Reply(MakeReply(Object({ { "frame", Value(current) }, { "reason", Value(reason) } })));
waiter.exchange->Reply(MakeReply(Object({ { "frame", Value(current) }, { "pausedAt", Value(since) }, { "reason", Value(reason) } })));
done = true;
}
break;