Skip to content

fix(dry-run): run validation before short-circuit and print structured preview#177

Merged
scottlovegrove merged 2 commits intomainfrom
fix/135-dry-run-validation
Apr 16, 2026
Merged

fix(dry-run): run validation before short-circuit and print structured preview#177
scottlovegrove merged 2 commits intomainfrom
fix/135-dry-run-validation

Conversation

@scottlovegrove
Copy link
Copy Markdown
Collaborator

@scottlovegrove scottlovegrove commented Apr 16, 2026

Part of #135.

Summary

  • Bug fix: thread done, thread mute, thread unmute, thread rename now run getThread + assertChannelIsPublic before the --dry-run short-circuit. Previously, a dry-run against a missing or inaccessible thread falsely reported success — it now fails the same way the real command would.
  • Actionable output: new printDryRun(action, details) helper in src/lib/output.ts (modelled on the todoist-cli pattern) replaces the old Dry run: would ... one-liners with a structured preview showing resolved state.
  • Adopted everywhere: all 18 --dry-run call sites (thread, conversation, msg, comment, away, react/unreact) now use the helper. Resource references show resolved data (e.g. thread title + id) instead of raw IDs.
  • SKILL_CONTENT picks up a brief Dry Run section; SKILL.md regenerated.

New output format

[dry-run] Would mute thread:
  Thread: Fix the login bug (500)
  Duration: 60 minutes
Run without --dry-run to execute.

Breaking change (output only)

The dry-run message format changes from Dry run: would mute thread 500 for 60 minutes to the structured form above. Any external scraper of dry-run text needs to update its parser. CLI syntax is unchanged.

Out of scope

These will be done in later PRs

  • No new pre-fetch validation added to commands that currently have none (conversation done/mute/unmute, msg delete, comment delete). The issue asks that existing validation not be skipped; introducing new validation is a separate concern.
  • JSON dry-run remains text-only except for react/unreact, which already emitted JSON dry-run and keeps its existing shape.

Test plan

  • npm run lint:check — clean
  • npm run type-check — clean
  • npm run check:skill-sync — in sync
  • npm test — 458 tests pass (new tests prove validation runs in dry-run for the four fixed commands; new unit tests for printDryRun)
  • Smoke-tested tw away clear --dry-run, tw away set vacation 2026-05-01 --dry-run, tw react thread 123 +1 --dry-run (text + JSON) against the built binary
  • Manual spot-check on a real workspace: tw thread mute <valid-id> --minutes 30 --dry-run renders resolved title; tw thread mute 999999 --dry-run fails with "thread not found"

🤖 Generated with Claude Code

…d preview

Closes #135

- Move getThread + assertChannelIsPublic above the dry-run check in
  thread done/mute/unmute/rename so dry-run catches missing or
  inaccessible threads the same way the real command would.
- Add printDryRun(action, details) helper in src/lib/output.ts modelled
  on todoist-cli. Output is now a structured preview with a yellow
  header, resolved resource details, and a dim footer pointing to the
  real execution.
- Adopt the helper across all 18 commands that support --dry-run.
  Dry-run messages now show the resolved target (e.g. thread title +
  id, channel name) instead of just the raw id.
- Remove dead printNotifyLines helper after folding notify output into
  the details dict.
- Update SKILL_CONTENT with a brief Dry Run section; regenerate
  SKILL.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@doistbot doistbot requested a review from pedroalves0 April 16, 2026 20:40
Copy link
Copy Markdown
Member

@doistbot doistbot left a comment

Choose a reason for hiding this comment

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

This PR introduces a helpful structured preview format for --dry-run commands and ensures validation runs before the short-circuit for several thread actions. These updates greatly improve the actionable feedback users receive while keeping the codebase aligned with expected CLI behavior. There are just a couple of areas to refine, specifically regarding the indentation of multiline values in the new output helper and ensuring the agent documentation accurately reflects which commands currently perform pre-fetch validation.

Share FeedbackReview Logs

Comment thread src/lib/output.ts Outdated
Comment thread src/lib/skills/content.ts Outdated
Address PR #177 review feedback:

- printDryRun now splits detail values on newlines and indents each
  continuation line so multi-paragraph previews stay structured instead
  of rendering flush-left and blending into the footer.
- Narrow the SKILL_CONTENT Dry Run section to say pre-flight validation
  runs "where a command performs it" rather than implying every command
  validates first — several commands (conversation done/mute/unmute,
  comment/msg delete) still parse the ref and print the preview without
  hitting the API.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scottlovegrove scottlovegrove self-assigned this Apr 16, 2026
@scottlovegrove scottlovegrove added the 👀 Show PR PR must be reviewed before or after merging label Apr 16, 2026
@scottlovegrove scottlovegrove merged commit d552e45 into main Apr 16, 2026
5 checks passed
@scottlovegrove scottlovegrove deleted the fix/135-dry-run-validation branch April 16, 2026 20:52
doist-release-bot bot added a commit that referenced this pull request Apr 16, 2026
## [2.31.1](v2.31.0...v2.31.1) (2026-04-16)

### Bug Fixes

* **dry-run:** run validation before short-circuit and print structured preview ([#177](#177)) ([d552e45](d552e45))
@doist-release-bot
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.31.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

scottlovegrove added a commit that referenced this pull request Apr 16, 2026
Follow-up to #177. The earlier PR fixed validation ordering for the
four thread commands that already had pre-flight validation. Five more
commands short-circuited on --dry-run without touching the API at all,
so previews against nonexistent, inaccessible, or non-owned resources
falsely reported success.

This adds a fetch (and, where appropriate, a creator/channel check)
before the dry-run short-circuit:

- conversation done/mute/unmute: getConversation, enrich preview with
  resolved title + current mute state. No creator check — archive and
  mute are per-user inbox/notification state, not shared-data
  mutations.
- msg delete: client.batch(getMessage, getSessionUser), throw
  NOT_CREATOR if message.creator != sessionUser.id. Mirrors the
  existing thread delete pattern.
- comment delete: client.batch(getComment, getSessionUser),
  assertChannelIsPublic(channelId, workspaceId), NOT_CREATOR check.
  Comments carry channelId on the entity so no extra thread fetch is
  needed.

Also adds a `conversationLabel` helper in conversation/helpers.ts for
consistent "title (id)" rendering in dry-run previews.

Tests: expanded msg.test.ts from a 42-line stub to a full fixture
covering delete, dry-run, creator-rejection and --json paths. Updated
comment and conversation tests to cover the new fetch + gate, and
added a new conversation done describe block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released 👀 Show PR PR must be reviewed before or after merging

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants