Skip to content

feat: upgrade-command#68

Merged
satyaborg merged 1 commit into
mainfrom
feat/upgrade-command
Jun 17, 2026
Merged

feat: upgrade-command#68
satyaborg merged 1 commit into
mainfrom
feat/upgrade-command

Conversation

@satyaborg

Copy link
Copy Markdown
Owner

Devloop Upgrade Command

Generated by devloop --create-pr.

Problem

Updating an already installed Devloop currently requires remembering the uninstall script and rerunning the curl installer manually. This hurts maintainers and users whenever a new release is available, because the update path is outside the CLI they already use.

Outcome

Users can run devloop upgrade to install the latest released Devloop. When a user opens the interactive Devloop entry point and a newer release exists, Devloop prompts once in that session to upgrade or skip before continuing.

Acceptance Criteria

AC1: devloop upgrade appears in plain help, TUI help, README command table, and command dispatch.
AC2: devloop upgrade installs a newer release through the existing remote installer behavior when fixture metadata reports a greater version.
AC3: devloop upgrade exits 0 without reinstalling when the latest fixture version equals the current VERSION.
AC4: devloop upgrade exits nonzero with a clear error when the latest version cannot be resolved.
AC5: devloop with no args prompts to upgrade before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs.
AC6: devloop menu prompts to upgrade before the menu when fixture metadata reports a greater version and stdin/stdout are TTYs.
AC7: Declining the automatic prompt continues to the normal interactive menu without changing the installed symlink.
AC8: Non-interactive commands and commands with explicit work do not run the automatic upgrade check.
AC9: The implementation has Bash test coverage in scripts/devloop_test.sh for upgrade command success, already-current behavior, resolver failure, prompt accept, prompt decline, and prompt skip paths.
AC10: The implementation preserves the existing remote installer checksum verification and skill installation behavior.

Review Trail

Review rounds and the final report are posted as PR comments below.


Latest commit: 0cf71f9

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
devloop 0cf71f9 Commit Preview URL

Branch Preview URL
Jun 16 2026, 11:39 AM

@satyaborg

Copy link
Copy Markdown
Owner Author

Devloop Review Round 1

  • Verdict: ACCEPT

Review 1

Verdict: ACCEPT

Acceptance matrix

Criterion Status Implementation evidence Test evidence
AC1 PASS Dispatch devloop:91-95; plain help devloop:184; TUI help devloop:223; README table README.md:42 contains "$help" "devloop upgrade", tui_help=$(welcome_tui); contains "$tui_help" "devloop upgrade", contains "$readme_text" "\devloop upgrade`", and main upgrade` exercised by the install test — all green
AC2 PASS upgrade_command (devloop:401) resolves latest, gates on devloop_version_gt, then devloop_upgrade_install (devloop:389) runs bash "$installer" --yes --version "$latest" ok - devloop upgrade installs newer release: asserts upgrading $version -> $remote_version, symlink target $upgrade_root/$remote_version/devloop, --version returns $remote_version, and Codex+Claude skill checksums present
AC3 PASS if ! devloop_version_gt "$latest" "$current"; then ... already latest; return 0 (devloop:415-418) ok - devloop upgrade already current: if ! wrapper proves exit 0, asserts is already latest, and [[ ! -e $current_upgrade_bin/devloop ]] (no reinstall)
AC4 PASS Resolver failure → error: could not resolve latest Devloop release to stderr, return 1 (devloop:411-414) ok - devloop upgrade resolver failure: if resolver_failure_output=...; then fail proves nonzero, asserts error text, no symlink created
AC5 PASS No-arg TUI path calls maybe_prompt_upgrade before interactive_menu (devloop:144-149) ok - no-arg dispatch prompts before menu asserts ordered prompt\nmenu; maybe_prompt_upgrade behavior (TTY + greater version → prompt) covered by ok - automatic upgrade prompt accept
AC6 PASS menu with no extra args calls maybe_prompt_upgrade before interactive_menu (devloop:103-108) ok - menu dispatch prompts before menu asserts prompt\nmenu; full path ok - automatic upgrade prompt accept runs main menu with fixture+TTY override, installs and relinks
AC7 PASS Decline branch sets UI_NOTICE="upgrade skipped" with no install (devloop:447-448) ok - automatic upgrade prompt decline: pre-creates symlink, ui_confirm returns 1, asserts menu still shown, symlink target unchanged, [[ ! -e $prompt_decline_root/$remote_version ]]
AC8 PASS maybe_prompt_upgrade only reachable from the two interactive entry points; run_command and spec/status/clean/continue/doctor/reports/upgrade/--version never call it; non-TTY self-guards via devloop_prompt_tty_ready (devloop:437) ok - explicit work skips upgrade prompt asserts main "$criteria_file" emits run only (no prompt); non-TTY skip in ok - automatic upgrade prompt skip paths
AC9 PASS All six functions present and wired Green: devloop upgrade installs newer release, already current, resolver failure, automatic upgrade prompt accept, automatic upgrade prompt decline, automatic upgrade prompt skip paths
AC10 PASS Install delegated unchanged to scripts/install.remote.sh (verify_archive + install_skills); installer not modified in diff Success test asserts verified checksum and presence of ~/.agents/skills/devloop-review/.devloop-checksum and ~/.claude/skills/devloop-review/.devloop-checksum

Full suite: bash scripts/devloop_test.sh exits 0, all ok - lines pass, including ok - 100% project function coverage. Verified locally, not trusted from the track.

Engineering quality matrix

Area Status Evidence
Correctness PASS devloop_version_gt (devloop:301) implements SemVer precedence correctly: numeric main parts via 10# to avoid octal traps, release > prerelease, numeric-vs-alpha identifier rules, longer-identifier-set wins. Array indexing is bounds-checked before access, so set -u is safe with no set -e masking the intentional return 1 guards. Errors propagate: devloop_upgrade_install is the tail call of upgrade_command, so installer failure becomes the command's exit code
Test quality PASS Tests assert real outcomes (symlink targets via readlink, installed --version, skill checksum files, exit codes via if !, not_contains "unexpected prompt") rather than implementation details. Decline test pre-creates and re-checks the symlink — a genuine regression guard. Unit tests for normalize/compare cover leading-zero rejection and prerelease ordering
Maintainability PASS New helpers are small, single-purpose, and named; dispatch additions follow the existing if [ "${1:-}" = ... ] style. Minor bounded duplication of resolver/regex with install.remote.sh is explicitly sanctioned by the spec note and unavoidable without a new sourced module (the two executables do not source each other)
Architecture boundaries PASS Download/checksum/symlink/skill logic stays in install.remote.sh, reused via bash "$installer" --yes --version. devloop only resolves, compares, and delegates — honoring the spec constraint against duplicating install logic
Simplicity PASS Full prerelease comparison is heavier than X.Y.Z releases strictly need, but the spec mandated a "deterministic Bash-compatible helper" and it is fully tested; the rest of the change is direct dispatch + delegation with no wrappers or flags
Security PASS $latest passed to the installer is re-validated through devloop_normalize_version's strict SemVer regex in the resolver before use, so a hostile tag_name cannot inject shell args. file:// handling is a test-only override; curl uses -fsSL; checksum verification preserved
Operational safety PASS Install reuses the atomic target.tmpmvln -sfn path. Prompt-path upgrade failure is non-fatal: sets UI_NOTICE and continues to the menu (devloop:444-445), surfaced via ui_headerui_print_notice. One-shot DEVLOOP_UPGRADE_PROMPTED guard prevents repeat prompts

Review flags

  • Silent decision: absent - Tradeoffs (installer reuse, SemVer helper, TTY-override test strategy, pseudo-TTY residual risk) are recorded in the track's Design tradeoffs and Residual risk sections.
  • Scope drift: absent - Changes confined to devloop, README.md, scripts/devloop_test.sh, and the track. The make_remote_release fixture now copies install.remote.sh into the archive for parity with real releases — a test-only change within scope.
  • Missing test: absent - Every acceptance-criterion behavior has targeted, outcome-asserting test evidence.

Findings

None.

Missing tests

None.

Fix instructions

None.

Notes

  • Scope: reviewed git diff main...HEAD (README.md, devloop, scripts/devloop_test.sh) plus the unchanged scripts/install.remote.sh to confirm AC10. This is Pass 1; the listed upgrade-command-r1.md is the output target, so there were no prior findings to reconcile.
  • Optional (non-blocking) hardening: the spec Behavior section lists "check finds an invalid version" as distinct from "version check fails." It is handled — devloop_resolve_latest_version ends in devloop_normalize_version, which rejects a malformed tag_name and surfaces invalid version: X via the same 2>&1 path as resolver failure — and devloop_normalize_version's rejection is unit-tested. A fixture with a present-but-invalid tag_name would exercise that exact branch end-to-end, but its absence does not leave any acceptance criterion underverified.
  • Minor cosmetic: when the local checkout's VERSION is newer than the latest release (e.g. a dev build), upgrade_command prints "is already latest" rather than "is newer." Behavior (no downgrade) is correct and within the spec's already-current edge.

@satyaborg

Copy link
Copy Markdown
Owner Author

Devloop Final Report

Field Value
Final status accepted
Pass count 1 / 5
Final verdict ACCEPT
PR URL #68
Branch feat/upgrade-command

Acceptance Matrix Summary

Acceptance matrix

Criterion Status Implementation evidence Test evidence
AC1 PASS Dispatch devloop:91-95; plain help devloop:184; TUI help devloop:223; README table README.md:42 contains "$help" "devloop upgrade", tui_help=$(welcome_tui); contains "$tui_help" "devloop upgrade", contains "$readme_text" "\devloop upgrade`", and main upgrade` exercised by the install test — all green
AC2 PASS upgrade_command (devloop:401) resolves latest, gates on devloop_version_gt, then devloop_upgrade_install (devloop:389) runs bash "$installer" --yes --version "$latest" ok - devloop upgrade installs newer release: asserts upgrading $version -> $remote_version, symlink target $upgrade_root/$remote_version/devloop, --version returns $remote_version, and Codex+Claude skill checksums present
AC3 PASS if ! devloop_version_gt "$latest" "$current"; then ... already latest; return 0 (devloop:415-418) ok - devloop upgrade already current: if ! wrapper proves exit 0, asserts is already latest, and [[ ! -e $current_upgrade_bin/devloop ]] (no reinstall)
AC4 PASS Resolver failure → error: could not resolve latest Devloop release to stderr, return 1 (devloop:411-414) ok - devloop upgrade resolver failure: if resolver_failure_output=...; then fail proves nonzero, asserts error text, no symlink created
AC5 PASS No-arg TUI path calls maybe_prompt_upgrade before interactive_menu (devloop:144-149) ok - no-arg dispatch prompts before menu asserts ordered prompt\nmenu; maybe_prompt_upgrade behavior (TTY + greater version → prompt) covered by ok - automatic upgrade prompt accept
AC6 PASS menu with no extra args calls maybe_prompt_upgrade before interactive_menu (devloop:103-108) ok - menu dispatch prompts before menu asserts prompt\nmenu; full path ok - automatic upgrade prompt accept runs main menu with fixture+TTY override, installs and relinks
AC7 PASS Decline branch sets UI_NOTICE="upgrade skipped" with no install (devloop:447-448) ok - automatic upgrade prompt decline: pre-creates symlink, ui_confirm returns 1, asserts menu still shown, symlink target unchanged, [[ ! -e $prompt_decline_root/$remote_version ]]
AC8 PASS maybe_prompt_upgrade only reachable from the two interactive entry points; run_command and spec/status/clean/continue/doctor/reports/upgrade/--version never call it; non-TTY self-guards via devloop_prompt_tty_ready (devloop:437) ok - explicit work skips upgrade prompt asserts main "$criteria_file" emits run only (no prompt); non-TTY skip in ok - automatic upgrade prompt skip paths
AC9 PASS All six functions present and wired Green: devloop upgrade installs newer release, already current, resolver failure, automatic upgrade prompt accept, automatic upgrade prompt decline, automatic upgrade prompt skip paths
AC10 PASS Install delegated unchanged to scripts/install.remote.sh (verify_archive + install_skills); installer not modified in diff Success test asserts verified checksum and presence of ~/.agents/skills/devloop-review/.devloop-checksum and ~/.claude/skills/devloop-review/.devloop-checksum

Full suite: bash scripts/devloop_test.sh exits 0, all ok - lines pass, including ok - 100% project function coverage. Verified locally, not trusted from the track.

Engineering Quality Summary

Engineering quality matrix

Area Status Evidence
Correctness PASS devloop_version_gt (devloop:301) implements SemVer precedence correctly: numeric main parts via 10# to avoid octal traps, release > prerelease, numeric-vs-alpha identifier rules, longer-identifier-set wins. Array indexing is bounds-checked before access, so set -u is safe with no set -e masking the intentional return 1 guards. Errors propagate: devloop_upgrade_install is the tail call of upgrade_command, so installer failure becomes the command's exit code
Test quality PASS Tests assert real outcomes (symlink targets via readlink, installed --version, skill checksum files, exit codes via if !, not_contains "unexpected prompt") rather than implementation details. Decline test pre-creates and re-checks the symlink — a genuine regression guard. Unit tests for normalize/compare cover leading-zero rejection and prerelease ordering
Maintainability PASS New helpers are small, single-purpose, and named; dispatch additions follow the existing if [ "${1:-}" = ... ] style. Minor bounded duplication of resolver/regex with install.remote.sh is explicitly sanctioned by the spec note and unavoidable without a new sourced module (the two executables do not source each other)
Architecture boundaries PASS Download/checksum/symlink/skill logic stays in install.remote.sh, reused via bash "$installer" --yes --version. devloop only resolves, compares, and delegates — honoring the spec constraint against duplicating install logic
Simplicity PASS Full prerelease comparison is heavier than X.Y.Z releases strictly need, but the spec mandated a "deterministic Bash-compatible helper" and it is fully tested; the rest of the change is direct dispatch + delegation with no wrappers or flags
Security PASS $latest passed to the installer is re-validated through devloop_normalize_version's strict SemVer regex in the resolver before use, so a hostile tag_name cannot inject shell args. file:// handling is a test-only override; curl uses -fsSL; checksum verification preserved
Operational safety PASS Install reuses the atomic target.tmpmvln -sfn path. Prompt-path upgrade failure is non-fatal: sets UI_NOTICE and continues to the menu (devloop:444-445), surfaced via ui_headerui_print_notice. One-shot DEVLOOP_UPGRADE_PROMPTED guard prevents repeat prompts

Implementation Summary

  • Final branch: feat/upgrade-command
  • Final commit: 0cf71f9
  • Commit message: feat: upgrade-command

Commit References

  • pass 1 0cf71f9 feat: upgrade-command (README.md, devloop, scripts/devloop_test.sh)

Tests Run

  • Verification hook log: not configured
  • Review test evidence: see the acceptance matrix summary above.

Residual Risk

  • No blocking residual risk was recorded by the final review.

@satyaborg satyaborg marked this pull request as ready for review June 17, 2026 00:48
@satyaborg satyaborg merged commit 60b2c04 into main Jun 17, 2026
4 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