fix: Windows command quoting, atomic JSON/log writes, restore dropped test

Implements items 1-3 of the whole-project review's suggested order
(docs/PROJECT_REVIEW_PLAN.md):

- Restore TestJobListViewIsCompact, accidentally dropped by 5b0e6fe;
  drop the redundant TestDefaultConfigUsesDetailedJobList row from
  TESTS.md and document the two other doc gaps the review found.
- Fix quoteLeadingWindowsProgramPath to find the earliest file-extension
  match at a word boundary instead of the first extension in list order,
  so a .bat/.cmd command whose argument ends in .exe no longer has its
  whole command line mistaken for the program path.
- Write gosentry.json, jobs.json, and run log files atomically (temp
  file + rename) so a crash or power loss mid-write can no longer leave
  a truncated file. Wire Service.Stop() into the app shutdown path so
  it actually runs, cancelling the run context for in-flight runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 16:53:59 +03:00
parent 89be009040
commit 1242b22e4f
9 changed files with 253 additions and 18 deletions
+37 -10
View File
@@ -6,6 +6,7 @@ import (
"strings"
"syscall"
"unicode"
"unicode/utf8"
)
func shellCommand(ctx context.Context, command string) *exec.Cmd {
@@ -32,19 +33,45 @@ func quoteLeadingWindowsProgramPath(command string) string {
}
lower := strings.ToLower(trimmed)
pathEnd := -1
for _, extension := range []string{".exe", ".cmd", ".bat", ".com"} {
index := strings.Index(lower, extension)
if index < 0 {
continue
end := earliestBoundedExtensionEnd(lower, extension)
if end >= 0 && (pathEnd < 0 || end < pathEnd) {
pathEnd = end
}
pathEnd := index + len(extension)
programPath := trimmed[:pathEnd]
if !strings.ContainsFunc(programPath, unicode.IsSpace) {
return command
}
return leadingWhitespace + `"` + programPath + `"` + trimmed[pathEnd:]
}
return command
if pathEnd < 0 {
return command
}
programPath := trimmed[:pathEnd]
if !strings.ContainsFunc(programPath, unicode.IsSpace) {
return command
}
return leadingWhitespace + `"` + programPath + `"` + trimmed[pathEnd:]
}
// earliestBoundedExtensionEnd returns the offset just past the first
// occurrence of extension in s that ends at a token boundary (end of string
// or whitespace), or -1 if none does. Scanning left to right and rejecting
// unbounded matches keeps a trailing "...\App.exe" inside an argument, such
// as "run.bat C:\tool.exe", from being mistaken for the program path.
func earliestBoundedExtensionEnd(s, extension string) int {
offset := 0
for {
index := strings.Index(s[offset:], extension)
if index < 0 {
return -1
}
end := offset + index + len(extension)
if end == len(s) {
return end
}
r, _ := utf8.DecodeRuneInString(s[end:])
if unicode.IsSpace(r) {
return end
}
offset += index + 1
}
}
func startsWithWindowsRootedPath(command string) bool {