Skip to content

docs: Investigate user experience — try/catch anti-pattern with errexit=false default (issue #156)#157

Open
konard wants to merge 5 commits intomainfrom
issue-156-eb63a328307e
Open

docs: Investigate user experience — try/catch anti-pattern with errexit=false default (issue #156)#157
konard wants to merge 5 commits intomainfrom
issue-156-eb63a328307e

Conversation

@konard
Copy link
Member

@konard konard commented Feb 26, 2026

Summary

Investigates issue #156 — a user experience problem triggered by a silent bug in link-assistant/calculator#79.

Root Cause

In the calculator's `scripts/version-and-commit.mjs`, `try/catch` was used to detect non-zero exit codes from `git diff --cached --quiet`. However, `command-stream` defaults to `errexit: false` (equivalent to bash without `set -e`), which means commands never throw on non-zero exit codes by default. The `catch` block was silently never reached, causing the auto-release pipeline to skip version commits on every CI run.

This caused 37 changelog fragments to accumulate without being released — every CI run printed "No changes to commit" and exited successfully, while Cargo.toml and CHANGELOG.md updates were staged but never committed.

Changes

  • `js/docs/case-studies/issue-156/README.md` — Comprehensive case study with:

    • Reconstructed timeline and sequence of events
    • Root cause analysis with code evidence from command-stream source
    • Bash vs. command-stream behavior comparison table
    • Full configuration API documentation (`shell.errexit()`, `set()`, `unset()`)
    • Recommended patterns for mixed strict/optional error handling
    • Comparison with similar libraries (execa, zx, bash, child_process)
    • Proposed solutions ranked by impact
  • `js/BEST-PRACTICES.md` — Added Pitfall Built-in 'which' command returns non-zero exit code for existing commands #7: try/catch anti-pattern with `errexit=false`, with examples of the bug and correct fix patterns

  • `experiments/issue-156/` — 4 runnable experiment scripts:

    • `01-default-behavior.mjs` — reproduces default errexit=false behavior
    • `02-errexit-enabled.mjs` — demonstrates shell.errexit(true) configuration
    • `03-bash-comparison.sh` — bash set -e reference comparison
    • `04-calculator-bug-repro.mjs` — exact reproduction of calculator#78 bug
  • `.changeset/issue-156-errexit-false-documentation.md` — Changeset for this documentation update (patch)

Key Finding

// ❌ ANTI-PATTERN: catch is NEVER reached with errexit=false (the default)
try {
  await $`git diff --cached --quiet`;
  console.log('No changes to commit'); // ← ALWAYS runs — silent bug!
} catch {
  // Never reached
}

// ✅ CORRECT: explicit exit code check
const result = await $`git diff --cached --quiet`;
if (result.code === 0) {
  console.log('No changes to commit');
} else {
  await $`git commit -m "Release"`;
}

Answer to Issue #156 Questions

  1. Does command-stream stop executing on first failure by default? No — it defaults to `errexit: false` (like bash without `set -e`). Commands continue after failures.

  2. Can this be configured? Yes — use `shell.errexit(true)` or `set('e')` to enable throw-on-failure behavior.

Behavior Comparison Table

Scenario bash default bash with `set -e` command-stream default command-stream with `shell.errexit(true)`
`false` exits with code 1 Continues Aborts Continues Throws
`try/catch` around `false` N/A N/A catch NOT reached catch IS reached
Result object available N/A N/A ✅ `result.code` ✅ `error.code`

Test Plan

Fixes #156


🤖 Generated with Claude Code

Adding CLAUDE.md with task information for AI processing.
This file will be removed when the task is complete.

Issue: #156
@konard konard self-assigned this Feb 26, 2026
…exit=false default

Investigates the user experience issue reported in #156, triggered by a
silent bug in link-assistant/calculator#79 where try/catch was used to
detect non-zero exit codes, but command-stream's errexit=false default
meant the catch block was never reached.

Changes:
- Add comprehensive case study at js/docs/case-studies/issue-156/README.md
  including timeline, root cause analysis, bash vs command-stream comparison
  table, configuration API documentation, and proposed solutions
- Add Pitfall #7 to js/BEST-PRACTICES.md: try/catch anti-pattern with
  errexit=false, with correct patterns and link to case study
- Add reproducible experiments in experiments/issue-156/:
  01-default-behavior.mjs — demonstrates default errexit=false behavior
  02-errexit-enabled.mjs — demonstrates shell.errexit(true) configuration
  03-bash-comparison.sh — bash set -e comparison reference
  04-calculator-bug-repro.mjs — reproduces the exact calculator#78 bug

Fixes #156

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@konard konard changed the title [WIP] Investigate user's experience docs: Investigate user experience — try/catch anti-pattern with errexit=false default (issue #156) Feb 26, 2026
@konard konard marked this pull request as ready for review February 26, 2026 10:43
@konard
Copy link
Member Author

konard commented Feb 26, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $0.955514
  • Calculated by Anthropic: $2.593670 USD
  • Difference: $1.638156 (+171.44%)
    📎 Log file uploaded as Gist (2923KB)
    🔗 View complete solution draft log

Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Member Author

konard commented Feb 26, 2026

🔄 Auto-restart 1/3

Detected uncommitted changes from previous run. Starting new session to review and commit them.

Uncommitted files:

?? bash-research.txt
?? calculator-pr-79-body.txt
?? calculator-pr-79-comments.json
?? calculator-pr-79-diff.txt
?? calculator-pr-79-review-comments.json
?? calculator-pr-79.txt
?? calculator-version-and-commit.mjs
?? exp-01-output.txt
?? exp-02-output.txt
?? exp-03-output.txt
?? exp-04-output.txt
?? files.txt
?? git-log.txt
?? issue-156.txt
?? issue-comments.json
?? js-files.txt
?? pr-157.txt

Auto-restart will stop after changes are committed or after 2 more iterations. Please wait until working session will end and give your feedback.

konard and others added 2 commits February 26, 2026 10:47
Add changeset for the try/catch anti-pattern with errexit=false case study.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-fix ESLint errors (string concatenation → template literals,
formatting) and Prettier formatting in experiment scripts and
case study README.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@konard
Copy link
Member Author

konard commented Feb 26, 2026

🔄 Auto-restart 1/3 Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $2.438783
  • Calculated by Anthropic: $1.747207 USD
  • Difference: $-0.691576 (-28.36%)
    📎 Log file uploaded as Gist (5129KB)
    🔗 View complete solution draft log

Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Member Author

konard commented Feb 26, 2026

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

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.

Investigate user's experience

1 participant