Skip to content

fix(cli): harden swarm commands found by max-quality review#318

Merged
jongio merged 1 commit into
mainfrom
fix/mq-swarm-findings
Jul 18, 2026
Merged

fix(cli): harden swarm commands found by max-quality review#318
jongio merged 1 commit into
mainfrom
fix/mq-swarm-findings

Conversation

@jongio

@jongio jongio commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

devx-max-quality deep review of the recently merged swarm CLI commands (#317) surfaced correctness, safety, and injection issues that got lighter review than the git-status feature. This fixes all of them with regression tests. Every deterministic gate is green (build, vet, full test suite, -race, gofmt, gofumpt, mage deadcode, golangci-lint).

Findings fixed

Sev Command Issue Fix
HIGH open Scoped resume reused stats' created-ascending ordering, so it resumed the oldest matching session, not the most recent. List scoped sessions ordered by updated DESC (same as --last) and pick the newest match.
MED open open --current with no detectable repo/branch/folder left an empty scope filter that matched every session and resumed an unrelated one. Guard: error out when nothing can be detected.
MED watch AttentionInterrupted sessions were tallied as idle while simultaneously listed as interrupted. Add an Interrupted bucket to the snapshot and count/print it.
MED stats stats --csv wrote user-controlled repository/branch labels raw. A label starting with = + - @ executes as a formula when opened in Excel/Sheets/LibreOffice (CWE-1236). csvSafe prefixes a ' to formula-triggering values.
MED prune prune used the filtered session list, which drops empty sessions, so config metadata for real-but-empty sessions was treated as stale. An empty store would also wipe every config entry. Enumerate all session IDs via new Store.AllSessionIDs; refuse to --apply against an empty store.
LOW tag tag required an exact ID match (no prefix) and missed empty sessions. Resolve via Store.ResolveIDPrefix, matching the other <id> commands.
LOW stats writeStatsCSV used defer cw.Flush(), swallowing any flush error. Explicit Flush() + cw.Error() check on the success path.

Tests

Added regression tests: TestRunOpen_ScopedResumePicksMostRecent, TestRunOpen_CurrentNoDetection, TestRunWatch_InterruptedNotIdle, TestRunStatsCSV_FormulaInjection, TestCsvSafe, TestRunPrune_EmptyStoreGuard, TestRunTag_ResolvesPrefix.

Verification

  • go build ./..., go vet ./...: pass
  • go test ./... -count=1: all packages pass
  • go test -race on changed packages: pass
  • gofmt -l . / gofumpt -l .: clean
  • mage deadcode: OK (10 known exclusions)
  • golangci-lint run: 0 issues

Deep review of the swarm CLI commands (open, watch, stats, prune, tag)
surfaced correctness, safety, and injection issues. Fix them all with
regression tests.

- open: scoped resume now picks the most-recently-active match instead
  of the oldest (it reused stats' created-ascending ordering); add a
  guard so `open --current` errors out instead of resuming an unrelated
  session when no repo/branch/folder can be detected.
- watch: count AttentionInterrupted sessions in their own bucket instead
  of silently tallying them as idle while listing them as interrupted.
- stats: neutralize CSV/spreadsheet formula injection (CWE-1236) in
  repository/branch/host labels via csvSafe, and surface csv writer
  flush errors instead of swallowing them in a deferred Flush.
- prune: enumerate all session IDs (including empty sessions) so config
  metadata for real-but-empty sessions is not treated as stale; refuse
  to --apply against an empty store, which would otherwise wipe every
  config entry.
- tag: resolve an ID or unique prefix via the store (matches the other
  <id> commands and finds empty sessions the filtered list omits).
- data: add Store.AllSessionIDs for existence checks that must include
  empty sessions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 524cf702-8922-4cfd-b23f-4069b734e09b
@jongio
jongio merged commit 33afadd into main Jul 18, 2026
2 checks passed
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