Skip to content

feat(app,cli,tui): live install progress, detailed failure reporting, and safe cancellation#35

Merged
vladtara merged 1 commit into
mainfrom
014-install-progress-report
Jul 15, 2026
Merged

feat(app,cli,tui): live install progress, detailed failure reporting, and safe cancellation#35
vladtara merged 1 commit into
mainfrom
014-install-progress-report

Conversation

@vladtara

Copy link
Copy Markdown
Contributor

Summary

Replaces the spinner-only install screen ("⏳ Fetching, verifying, and linking…" followed by the contradictory "Installed 0 skill(s), 3 failed") with a structured, event-driven progress and result pipeline shared by all three install entry points (gskill install, gskill add, gskill onboard).

What changed

Domain layer (internal/app)

  • Typed install lifecycle vocabulary: InstallPhase (resolving → complete), InstallStatus (incl. skipped, cancelled, not-attempted), InstallProgressEvent emitted strictly sequentially through an injected nil-safe callback (zero cost unobserved).
  • Per-skill results carry full provenance (source type, skill path, refs, commit, agents, mode) plus a classified InstallFailure — category derived from the typed errs sentinels via errors.Is (never string parsing), remediation hint from the error chain, expected/actual hashes carried as data on integrity mismatches.
  • Aggregate produces summary counters that sum to the total by construction; cancellation dominates outcome classification.

TUI (internal/tui)

  • Shared progress component: terminal-count-driven bar (no fake time-based progress), current skill/source/version/phase, live counters, wide + sub-60-column compact layouts.
  • Result screen: truthful summary, scrollable failure table (failures only; all entries with planned actions on dry runs), enter → full detail view with category, complete message, expected/actual, and suggested action. ctrl+c always exits.
  • Both wizards stream events over the existing channel adapter and support esc/ctrl+c cancellation with visible "cancelling…" feedback.

CLI (internal/cli)

  • Plain renderer: outcome headline, counter line, per-failure blocks with placeholders for unknowns; presentation vocabulary shared with the TUI so the surfaces cannot drift.
  • --json: additively extended stable document (top-level status, complete summary counters, per-skill provenance and failure objects with omission rules); nothing emitted until the single final document.
  • Non-TTY runs print one stable line per skill terminal state only under the global -v; zero cursor-control sequences.
  • The wizard path no longer double-prints a generic summary; exit codes are unchanged (0 / 10 partial / 130 cancelled).

Cancellation, end to end

  • SIGINT/SIGTERM now cancel the run context (cmd/gskill/main.go), so direct CLI runs stop gracefully: the loop halts between skills, in-flight ctx-aware aborts classify as cancelled, remaining entries report not-attempted, completed work persists to a valid lockfile, and every path exits 130.

Safety

All untrusted text (skill names, sources, error messages) is sanitized on every render surface; JSON escapes control bytes. An import-boundary test keeps rendering dependencies out of the domain layer.

Testing

  • TDD throughout: event-stream contract tests, aggregation invariant tests, failure-classification tests, TUI model tests (progress, table, detail, cancellation, panic-restore), 12 golden files pinning plain and JSON output (success/partial/total-failure/cancelled/dry-run/missing-version/hostile-input), and app-layer cancellation tests.
  • A multi-agent code review (8 finder angles + verification) confirmed 20 findings; 18 fixed in-branch, 2 noted as follow-ups.
  • ./scripts/verify.sh exits 0: race tests, coverage 76.5% (up from 75.1% baseline), golangci-lint clean, govulncheck, gitleaks.

Spec: specs/014-install-progress-report/ (internal).

…ing, and safe cancellation

Replace the spinner-only install screen with an event-driven pipeline
shared by all three install entry points (install, add, onboard):

- internal/app emits typed lifecycle events (InstallPhase, InstallStatus,
  InstallProgressEvent) through an injected nil-safe callback; results
  carry provenance plus a classified InstallFailure (category, message,
  hint, expected/actual) derived from errs sentinels, never string
  parsing; Aggregate produces counters that always sum to the total.
- internal/tui gains a shared progress component (terminal-count-driven
  bar, current skill/phase, wide and sub-60-column layouts) and a result
  screen (truthful summary, scrollable failure table, drill-down detail
  view); both wizards stream events over the existing channel adapter.
- internal/cli rewrites the install renderer (headline, counter line,
  per-failure blocks with em-dash placeholders), extends the stable JSON
  document (status, summary, per-skill provenance/failure with omission
  rules), adds verbose-gated non-TTY progress lines, and suppresses the
  duplicate post-wizard summary while keeping exit codes intact.
- Cancellation is safe end-to-end: SIGINT/SIGTERM cancel the run context,
  esc/ctrl+c cancels inside both wizards with visible feedback, the loop
  stops between skills, in-flight aborts classify as cancelled, remaining
  entries report not-attempted, completed work persists to a valid
  lockfile, and every path exits 130.

All untrusted text is sanitized on every surface; behavior is pinned by
unit, TUI, golden (plain + JSON), cancellation, and security tests, plus
an import-boundary test keeping rendering out of the domain layer.
Copilot AI review requested due to automatic review settings July 15, 2026 17:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vladtara
vladtara merged commit 011d4af into main Jul 15, 2026
8 checks passed
@vladtara
vladtara deleted the 014-install-progress-report branch July 15, 2026 17:56
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.

2 participants