Skip to content

Conversation

@max-sixty
Copy link
Owner

Summary

  • Move [commit-generation][commit.generation] (nested under existing commit section)
  • Replace command + args with single command string (shell execution via sh -c)
  • Update docs to present Claude CLI and llm as two equal setup options
  • Backward compatibility with deprecation warning for old format

Changes

Config format:

# Old (deprecated, still works)
[commit-generation]
command = "llm"
args = ["-m", "claude-haiku-4.5"]

# New
[commit.generation]
command = "llm -m claude-haiku-4.5"

Shell execution: Commands now run via sh -c (Git Bash on Windows), allowing inline environment variables like MAX_THINKING_TOKENS=0 claude -p ...

Environment variables: WORKTRUNK_COMMIT__GENERATION__COMMAND (note double underscore for nested path)

Test plan

  • All 2149 tests pass (wt hook pre-merge --yes)
  • Tests updated to use new env var format
  • Deprecation warning shown when using old [commit-generation] format
  • Validation added for both old and new per-project config paths
  • Windows compatibility via ShellConfig (uses Git Bash)

This was written by Claude Code on behalf of @max-sixty

max-sixty and others added 6 commits January 22, 2026 16:10
- Move `[commit-generation]` to `[commit.generation]` (nested under commit)
- Replace `command` + `args` with single `command` string (shell execution)
- Add deprecation warning for old format with backward compatibility
- Update docs to present Claude CLI and llm as equal options

The new format uses shell execution (`sh -c`) so environment variables
can be set inline in the command string.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix test regressions identified by Codex review where tests using
WORKTRUNK_COMMIT_GENERATION__ARGS were silently failing because
args is now ignored with the new shell-based command format.

Tests now use the correct pattern:
  WORKTRUNK_COMMIT_GENERATION__COMMAND="cat >/dev/null && echo 'message'"

Also update CLI docs to show:
- New config path: commit.generation.command (not commit-generation.command)
- New env var format: WORKTRUNK_COMMIT__GENERATION__COMMAND
- Remove args documentation (no longer applicable)

Co-Authored-By: Claude <noreply@anthropic.com>
Address Codex review feedback:
- Update per-project doc example to show new format
  (commit.generation instead of commit-generation)
- Update env var comment to show new format
  (COMMIT__GENERATION__COMMAND instead of COMMIT_GENERATION__COMMAND)
- Add validation for projects.*.commit.generation path
  (was only validating deprecated commit-generation path)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use ShellConfig for LLM command execution (Windows compatibility)
- Improve error handling when LLM stderr is empty
- Format reproduction commands with conditional sh -c wrapping
- Update tests to use new env var format (WORKTRUNK_COMMIT__GENERATION__)
- Make test assertion more specific ([commit] not just "commit")
- Document cformat safety for shell commands in output guidelines

Co-Authored-By: Claude <noreply@anthropic.com>
Replace manual single-quote escaping with the shell_escape crate
which is already a dependency used elsewhere in the codebase.

Co-Authored-By: Claude <noreply@anthropic.com>
@max-sixty max-sixty added the feedback-requested Looking for user feedback on this feature label Jan 23, 2026
max-sixty and others added 12 commits January 22, 2026 19:03
Consolidate all deprecation handling into deprecation.rs:
- Add [commit-generation] → [commit.generation] section migration
- Merge deprecated `args` field into `command` string
- Generate single .new file with all migrations (template vars + sections)
- Skip migration if new section already exists (don't overwrite)
- Skip warning for empty deprecated sections
- Use shell_escape for proper quoting when merging args
- Rename hint from deprecated-project-config to deprecated-config

Remove separate warn_deprecated_commit_generation() from user.rs.

Co-Authored-By: Claude <noreply@anthropic.com>
Fixes found during adversarial testing:

1. Args data loss prevention: merge_args_into_command() now validates
   preconditions (args is array, command is string) BEFORE removing
   the args field. Previously, table.remove("args") happened
   unconditionally, losing data if command was missing or non-string.

2. Malformed generation detection: When checking if new [commit.generation]
   exists, now verify it's actually a table (is_table || is_inline_table).
   A malformed value like `generation = "string"` no longer suppresses
   deprecation warnings for the old section.

3. Inline table support: Both detection and migration now handle inline
   table format (e.g., `commit-generation = { command = "..." }`).
   Detection uses as_inline_table() in addition to as_table().
   Migration converts InlineTable to Table via into_table().

Added comprehensive tests for all edge cases.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for:
- Project-level inline table migration
- Preserving existing [commit] fields during migration
- Empty inline table detection (should not flag as deprecated)

Co-Authored-By: Claude <noreply@anthropic.com>
Add test that exercises the full deprecation path through check_and_migrate()
for commit-generation section rename and args merge. This improves coverage
on the warning output and migration file generation paths.

Co-Authored-By: Claude <noreply@anthropic.com>
…ation

Add test that exercises the project_keys iteration path in check_and_migrate()
for project-level commit-generation deprecation warnings. This covers the
warning message format that includes project identifiers.

Co-Authored-By: Claude <noreply@anthropic.com>
Cover the early-return paths in merge_args_into_command():
- Args without command: args preserved (no command to merge into)
- Non-string command: args preserved (can't merge into non-string)
- Command-only: clean migration without args field

These tests exercise the !can_merge validation path.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for the `_ => None` fallback branches that handle unusual
cases where commit-generation is neither a table nor inline table
(e.g., a string value like `commit-generation = "not a table"`).

These tests exercise lines 264 and 305 in deprecation.rs.

Co-Authored-By: Claude <noreply@anthropic.com>
Add unit tests for:
- Empty command with args: args become the command when command=""
- Invalid TOML parsing: returns content unchanged
- Deduplication path: second call with same path hits early return

These tests cover previously uncovered code paths in deprecation.rs.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for format_reproduction_command and is_lock_file to improve
code coverage.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for:
- CommitConfig merge when only base/override has generation
- CommitConfig merge when both have generation
- CommitGenerationConfig merge for squash_template fields

These cover previously uncovered branches in the Merge implementations.

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for validation error paths in the new [commit.generation]
config format (both top-level and per-project).

Co-Authored-By: Claude <noreply@anthropic.com>
///
/// Simple commands like `llm -m haiku` are shown as-is.
/// Complex commands with shell syntax are wrapped: `sh -c 'complex && command'`
fn format_reproduction_command(base_cmd: &str, llm_command: &str) -> String {
Copy link
Owner Author

Choose a reason for hiding this comment

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

@claude wouldn't it be easier to just do a single format? why have both?

Copy link
Collaborator

@worktrunk-bot worktrunk-bot Jan 23, 2026

Choose a reason for hiding this comment

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

Claude finished @max-sixty's task in 43s —— View job


Answer

Good question! Supporting both formats is required because worktrunk has a growing user base who are already using the old [commit-generation] format in their config files.

From CLAUDE.md:

This project has a growing user base. Balance clean design with reasonable compatibility.

External interfaces to protect:

  • Config file format (wt.toml, user config) — avoid breaking changes; provide migration guidance when necessary

Why backward compatibility matters here:

  1. Breaking user workflows: If we just switched to [commit.generation] without supporting the old format, existing users' config would stop working immediately when they upgrade
  2. Graceful migration path: The deprecation system (src/config/deprecation.rs) does three things:
    • Detects the old format in config files
    • Continues to work with the old format (no breakage)
    • Generates a .new migration file with the updated format that users can apply when ready

The implementation:

Line 25 isn't actually where both formats are handled - that's just a helper function for reproduction commands. The actual dual-format handling is in:

  • src/config/deprecation.rs (lines 153-233): Detects both [commit-generation] and [commit.generation] formats
  • src/config/user.rs: Deserializes both old and new structures, preferring new when both exist
  • Migration: Automatically generates a migrated config file for users to review and apply

Why not force migration immediately?

Per project guidelines, "Prefer deprecation warnings over silent breaks" - users get warnings and migration files, but their existing config keeps working until they're ready to update. This is better UX than forcing everyone to manually fix their config the moment they upgrade.


Copy link
Owner Author

Choose a reason for hiding this comment

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

do you see the line that I'm commenting on? my comment is very specific to tuos area of code

max-sixty and others added 3 commits January 23, 2026 10:43
Cover the "create from scratch" branch in save_to() which serializes
commit.generation and deprecated commit-generation sections to new files.
Previously only the "update existing file" branch was tested.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Remove Claude Code/llm setup examples from config docs since they
duplicate llm-commits.md. Config page now links to llm-commits.md
for setup and keeps only the custom prompt templates section.

Co-Authored-By: Claude <noreply@anthropic.com>
max-sixty and others added 4 commits January 23, 2026 12:02
Previously `config show` read files directly without calling the config
loaders, so deprecation warnings weren't shown. Now we call
UserConfig::load() and repo.load_project_config() to trigger warnings
while still displaying invalid configs (the deprecation check runs on
raw content before parsing).

Co-Authored-By: Claude <noreply@anthropic.com>
Move aichat example from "Alternative tools" into the Setup section as
"Option 3: aichat" alongside Claude CLI and llm. Add "Custom scripts"
subsection for the generic example. Remove the now-redundant
"Alternative tools" section.

Co-Authored-By: Claude <noreply@anthropic.com>
Change from "Option 1/2/3" to just showing examples of tools that work.
Lead with the concept: any command that reads stdin and outputs a commit
message. Simplify Claude Code example and condense install instructions.

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

Labels

feedback-requested Looking for user feedback on this feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants