feat(config): experimental worktrunk.config.* git-config project-config source - #3609
feat(config): experimental worktrunk.config.* git-config project-config source#3609indexzero wants to merge 4 commits into
Conversation
…ig source Any key under the worktrunk.config. prefix in git config now supplies project configuration (max-sixty#3454). Strip the prefix; the remainder is the exact .config/wt.toml key path. Selection is all-or-nothing: when any key exists, the merged effective git config is the complete project config, the file is ignored, and a warning names the superseded file. Values are strings only; schema violations fail loudly with no fallback to the file. Commands from this source pass through the same approval gate as file-based project config. Git owns precedence: keys come from the cached `git config --list -z` map, so system/global/local scopes and includes resolve before worktrunk sees them, at zero additional subprocess cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
21b2343 to
b2be351
Compare
Findings from adversarial reviews of the two competing implementations, applied per the reviewed merge-fix plan: - Alias discovery routes through ProjectConfig::load, so git-config aliases appear in listings exactly when dispatch would run them. - A WORKTRUNK_PROJECT_CONFIG_PATH override — any value, including the empty kill switch — disables the git source, enforced in the sole accessor so every consumer inherits the deferral by construction. - wt config show propagates a failed git-config read instead of rendering the file as active while execution errors. - wt --diagnose names the git-config source, its key names, and the superseded file, but never the values: diagnose output is routinely pasted into public bug reports and this source exists for private configuration. - The supersession-warning latch is peeked before label resolution and set only on emit, so a no-file call cannot suppress a later born-superseded warning from wt config create --project. - Provenance docstring softened: include/includeIf can carry remotely-sourced content, which is why the approval gate applies unchanged. - Docs now state what the git source skips (file migration and its deprecation messaging — deprecated spellings may still deserialize), that per-worktree git config is unsupported, and describe the diagnostic command as listing matching keys rather than consumed values. - wt config create --project warns when the new file is born superseded; wt step prune suppresses the branch-hooks annotation under the git source (git config is branch-independent); project config resolves from a bare root via the git source, with wt config approvals framing its answers from it. - New tests: behavioral approval decline (hook must not run), includeIf resolution, both override-precedence forms, alias-listing consistency, born-superseded create, bare-root approvals, and diagnose value redaction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b2be351 to
c2a50d0
Compare
worktrunk-bot
left a comment
There was a problem hiding this comment.
Reviewing as a draft — flagging anything that looks worth a quick fix. Mark ready for a full review.
Nicely structured — the mechanical key mapping, the all-or-nothing selection, and the consistent "name the source, keep the approval gate" treatment across every consumer (config show, hook show, diagnostic, approvals, create, alias) all hang together. The diagnostic path deliberately omitting values (only naming keys + the superseded file) is a good call given diagnose output gets pasted into public bug reports.
One functional gap in the wt step prune skip-hint, noted inline: the git_source_active → invoking_project_bytes = None change doesn't actually suppress the (different hooks on branch) annotation the way its comment claims — it inverts it for the supersession case. Details on the line.
- wt config show snapshots for the two error renderings: a git-source value that violates the schema (Invalid config + gutter) and colliding key paths (conflict error), matching the file branch's treatment. - wt step prune --dry-run under the git source exercises the suppressed branch-hooks baseline. - A bare root without keys exercises the no-project-config guard and its path-free error. - A repeat-call unit test exercises the supersession-warning latch peek. - The diagnose Err arm collapses into the file fallback (best-effort surface; the file section already reports read failures) and the infallible-in-practice TOML render error map shrinks to a passthrough, removing two unreachable specialized handlers instead of pretending to test them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
worktrunk-bot
left a comment
There was a problem hiding this comment.
Just the new commit since the last review — mostly test coverage, plus one behavior change in the diagnose path worth a second look (inline).
…r arm Two review findings on the follow-up commits: - wt step prune suppressed the "(different hooks on branch)" annotation by nulling the comparison baseline, which inverts it: a candidate with a committed .config/wt.toml compares Some(_) != None and flags the exact case where git-config hooks are identical across branches. The suppression now lives on the differs computation itself, pinned by a test with a committed file and an unapproved git-config pre-remove. - The diagnose Err arm returns: a failed bulk-config read is reported as such instead of falling through and printing .config/wt.toml as the active source — the file section's "(read failed)" covers a different read, so the fall-through misattributed, in the report meant to diagnose exactly that failure. Its uncovered line is an honest, effectively untestable gap, and is documented as one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What this adds
An experimental second source for project configuration:
worktrunk.config.*keys in git config.One mechanical rule governs the mapping. Strip
worktrunk.config.; the remainder is the exact key path from.config/wt.toml.worktrunk.config.post-startis the top-levelpost-starthook.worktrunk.config.list.urlis[list] url. No renamed keys, no parallel schema.Why
.git/configis never committed, never pushed, never cloned. Configuration written there is private to one machine by construction — and shared across every linked worktree, because the local scope lives in the common git dir. That combination is what issue 3454 asks for, and what several earlier issues (650, 1077, 2818) kept circling: private, repo-local config without a new file format and without repo identities leaking into public dotfiles.How selection works
All-or-nothing. When any
worktrunk.config.*key exists, those keys are the complete project config;.config/wt.tomlis ignored. There is no key-level merging between sources.Two consequences follow, both deliberate:
git config --show-scope --show-origin --get-regexp '^worktrunk\.config\.'.wt config showandwt hook showname the active source.What stays the same
include/includeIf(a cloned dotfiles repo, for instance), so source alone is not a trust signal.git config --list -zmap — zero new subprocesses. Local overrides global; conditional includes work; worktrunk never sees scopes..config/wt.toml,[projects."…"]overrides, andWORKTRUNK_PROJECT_CONFIG_PATHare untouched.Limits in this first version
Values are strings, one per key. Fields needing other TOML types (
step.copy-ignored.exclude, an array) are not expressible and error clearly when attempted. Keys must be lowercase — git's key model makes middle segments case-sensitive, and the schema's own spelling is lowercase. No write commands:git configalready is the management interface.Testing
Unit tests cover the key mapping: nesting, value/table conflicts, empty segments, loud failure on non-string fields. Integration tests cover source selection, the supersession warning firing exactly when a file is superseded, local-over-global precedence,
include.pathresolution, approval gating, andwt config showin human and JSON forms. The config docs page gained a "Private project config in git config" section, and all generated doc mirrors and help snapshots are refreshed.