Skip to content

fix: a stopped run says so, and says what survived - #75

Merged
donislawdev merged 1 commit into
mainfrom
stability/stopped-sentence
Sep 7, 2026
Merged

fix: a stopped run says so, and says what survived#75
donislawdev merged 1 commit into
mainfrom
stability/stopped-sentence

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Stability report finding T6, which was already ours as O175 (2026-09-02, deliberately deferred pending your call). You said do it.

What was wrong

Ctrl+C and a CI timeout both printed:

tfg: context canceled

Six characters of Go runtime vocabulary, handed to somebody who had just told the tool to stop and now wanted to know whether their directory was safe to reuse. The window has said the useful thing since it had a progress bar - Stopped after N files. The manifest describes exactly those. The command line was the surface staying quiet about facts it already had.

tfg: stopped before it finished. 897 files written, and the manifest describes exactly those.
manifest: /out/manifest.json

Reproduced with a real signal

O175 was written from the code, not from a run, because a signal cannot be delivered to this process from the shell on this machine - kill -INT from MSYS does not deliver a CTRL_C_EVENT. Done in a Linux container instead:

signal exit said on disk
SIGINT into 3000 files 130 897 files written 897
SIGTERM into 3000 files 143 755 files written 755

The count matches to the file.

Two sentences rather than three, and that is a decision

The report asked for Ctrl+C and SIGTERM to read differently, since that is why their exit codes differ. Both arrive as context.Canceled - the signal is known only in cmd/tfg/main.go, deliberately, because signal.NotifyContext does not say which one arrived. Separating them in the words means plumbing the signal into cli.Run, whose signature the whole guard suite is written against.

The exit code already carries it (130 against 143), and that is the channel a script reads. A person who pressed Ctrl+C does not need to be told they pressed Ctrl+C. A deadline gets its own sentence, because the error itself distinguishes it and that costs nothing.

What the guard proves, and what it does not

The guard reaches the sentence. It does not reach the count, and the code says so rather than leaving it to be discovered: the command line plans before it runs and planning honours the context, so a run started with a finished context returns from PlanContext and never arrives at the count. Landing between the two needs a cancel timed after planning and before the last file - a clock, and a guard on a clock goes red on a busy machine rather than on a defect.

The count is proven by running it, in the table above.

One thing worth recording

The first version had a third guard, asserting that a run which never began claims no count. It was green and could not be reddened - that path does not call whatSurvived at all. The mutation found it (NOT CAUGHT), not the reading. The guard was removed rather than kept for comfort, and the condition it was aimed at is documented as unreachable from this surface with the reason it stays.

Two mutations, both caught. preflight --quick green on all 12.

🤖 Generated with Claude Code

Ctrl+C and a CI timeout both printed "tfg: context canceled" - six characters
of Go runtime vocabulary handed to somebody who had just told the tool to stop
and now wanted to know whether their directory was safe to reuse. The window
has said the useful thing since it had a progress bar. The command line was
the surface staying quiet.

    tfg: stopped before it finished. 897 files written, and the manifest
    describes exactly those.

Reproduced against a real signal in a Linux container, because a signal cannot
be delivered to this process from the shell on this machine - which is why
O175 was written from the code on 2026-09-02 and left open. SIGINT into a run
of 3000: exit 130, "897 files written", 897 on disk. SIGTERM: exit 143, "755
files written", 755 on disk. The count matches to the file.

Two sentences rather than three, and that is a decision. The report asked for
Ctrl+C and SIGTERM to read differently, since that is why their exit codes
differ. Both arrive here as context.Canceled - the signal is known only in
cmd/tfg/main.go, deliberately, because signal.NotifyContext does not say which
one arrived. Separating them in the words would mean plumbing the signal into
cli.Run, whose signature the whole guard suite is written against. The exit
code already carries it, and that is the channel a script reads. A deadline
gets its own sentence, because the error itself distinguishes it and that
costs nothing.

The guard reaches the sentence and not the count, and the code says so. The
command line plans before it runs and planning honours the context, so a run
started with a finished context returns from PlanContext and never reaches the
count. Landing between the two needs a clock, and a guard on a clock goes red
on a busy machine rather than on a defect.

Worth recording: the first version had a third guard, for a run that never
began claiming no count. It was green and could not be reddened, because that
path does not call whatSurvived at all. The mutation found it - NOT CAUGHT -
and the guard was removed rather than kept for comfort.

Two mutations, both caught.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 7b958df into main Sep 7, 2026
18 checks passed
@donislawdev
donislawdev deleted the stability/stopped-sentence branch September 7, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant