Skip to content

Port: port/pr-29-feature/25-git-worktree-support#7

Merged
briansumma merged 56 commits into
mainfrom
port/pr-29-feature/25-git-worktree-support
May 14, 2026
Merged

Port: port/pr-29-feature/25-git-worktree-support#7
briansumma merged 56 commits into
mainfrom
port/pr-29-feature/25-git-worktree-support

Conversation

@briansumma

Copy link
Copy Markdown
Collaborator

Ported from upstream cytostack/openwolf. Needs review before merge.

briansumma and others added 30 commits April 23, 2026 18:49
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Documents the Shared Knowledge / Namespaced Session architecture for
first-class git worktree support in OpenWolf (Approach 2 scope).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11-task TDD plan covering detection utility, hook updates, CLI guards,
and smoke test for first-class git worktree support in OpenWolf.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… worktree README section

vi.mock("node:child_process") cannot patch live bindings on CJS built-ins
when imported via ESM. Switching to vi.spyOn on the git-wrapper ESM module.

Also adds git worktree section to README per CONTRIBUTING guidelines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The init refactor inadvertently dropped critical Claude Code integration:
HOOK_SETTINGS used a non-standard format, settings were written to
.wolf/settings.json instead of .claude/settings.json, and the CLAUDE.md
snippet and .claude/rules/openwolf.md creation were removed.

- Restore HOOK_SETTINGS to Claude Code format (matcher/hooks/command)
- Write hooks to .claude/settings.json with merge via replaceOpenWolfHooks()
- Add writeClaudeRules() for .claude/rules/openwolf.md and CLAUDE.md snippet
- Remove writeReadme() (should not modify user README.md)
- Gate writeIdentity() behind !isUpgrade to respect CREATE_IF_MISSING
- Use atomic writeJSON() for .claude/settings.json writes
- Fix || to ?? for CLAUDE_PROJECT_DIR in shared.ts
- Remove TOCTOU existsSync before idempotent mkdirSync in ensureSessionDir()
- Fix Next steps output to suggest correct files for git add

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix critical regressions and edge cases found during pre-merge review:

- Use --git-dir vs --git-common-dir comparison for worktree detection,
  fixing false positives on git submodules and subdirectories
- Add shared.js and package.json (type:module) to writeHooks() so hooks
  actually work at runtime
- Fix openwolf status in worktree mode by resolving wolfDir before the
  existence check
- Fix writeHooks() source path resolution to find compiled hooks
  relative to __dirname instead of templatesDir
- Add error discrimination in detectWorktreeContext() catch blocks to
  warn on unexpected git failures instead of silently falling back
- Fix writeSettings() to backup and warn on corrupted settings.json
  instead of silently overwriting
- Wrap scanProject in try/catch to prevent mid-init crashes
- Revert Rust doc comment regex from // back to /// (only match doc comments)
- Remove unused imports (execSync, isWindows, readText, writeText)
- Add tests for submodule false-positive guard and error warning behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix critical bugs: ensureSessionDir crash on mkdirSync failure, .wolf/
skip checks broken in worktree mode (check both projectRoot and
mainRepoRoot), memory.md split-brain between hooks and Claude (write to
shared wolfDir), and writeJSON silently swallowing fallback failures.

Add error logging to all 6 hook catch handlers, fix stale docs paths in
hooks.md, remove premature v1.1.0 version claim, scan sessionDir for
orphaned .tmp files, guard writeGitIgnore against EACCES, align init.ts
worktree detection with findProjectRoot(), and document bare-repo
worktree limitation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rehensive error logging

## Critical fixes

- **Worktree hook resolution**: Hook commands now use `git rev-parse --git-common-dir` at shell invocation time to resolve to the main checkout where `.wolf/hooks/` lives, instead of relying on `$CLAUDE_PROJECT_DIR` which points to the worktree directory in worktree mode.

- **Restore cerebrum seeding on fresh init**: Re-added `seedCerebrum()`, `detectProjectName()`, and `detectProjectDescription()` helpers so new projects get project name/description in `cerebrum.md`.

- **Anatomy scan upgrade-safe**: Wrapped `scanProject()` in `if (!isUpgrade)` guard so upgrades preserve user-curated entries. Added error reason to catch output.

- **Token-ledger created_at initialization**: Stamps `created_at` on first init when the field is empty.

- **Fixed docs: memory.md is shared, not session-scoped**: Corrected README.md and docs/troubleshooting.md to list `token-ledger.json` and `_session.json` as session-scoped, with `memory.md` explicitly noted as shared across worktrees.

## Important improvements

- **10 empty catch blocks in hooks now log to stderr** (post-write.ts, session-start.ts, stop.ts, shared.ts): All hooks still exit 0 but report errors for debugging. ENOENT filtered where appropriate.

- **Fixed registerProject call**: Removed erroneous `await`, restored openwolf self-guard, restored multi-manifest project name detection.

- **writeIdentity() differentiates ENOENT**: Now warns on malformed package.json instead of silently ignoring.

- **Restored fuller CLAUDE.md snippet**: New projects get the full header with description; existing CLAUDE.md files use smarter detection.

- **Added findTemplatesDir fallback candidates**: 4 candidates instead of 2 for better resilience.

- **Added daemon status hint**: Init summary now mentions daemon can be started manually.

- **18 new tests for settings merge**: Comprehensive coverage for `replaceOpenWolfHooks()` and `isOpenWolfHook()` with upgrade, preservation, idempotency, and edge case scenarios. All 27 tests pass.

## Files changed

- src/cli/init.ts: Restored lost functions, fixed worktree hook commands, exported test helpers
- src/cli/init.test.ts: New test file with 18 tests for settings merge logic
- src/hooks/{post-write,session-start,shared,stop}.ts: Added stderr logging to error cases
- README.md, docs/troubleshooting.md: Corrected memory.md scope documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ree errors, tests)

- Fix SQL regex in shared.ts:569 to match unquoted table names
- Move memory.md from sessionFiles to sharedFiles in status.ts
- Extract detectWorktreeContext to src/hooks/worktree-helper.ts (zero deps)
- Remove empty catch in worktree.ts; unexpected errors now propagate
- Preserve targeted fallback for non-git/ENOENT cases in utils/worktree.ts
- Remove stderr silencers (2>/dev/null) from init.ts WOLF_ROOT
- Add worktree guard tests for initCommand()
- Update worktree tests to mock node:child_process directly (ESM compat)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…te drift

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single source of truth for the hook script basename list — eliminates
the duplication across init/update/status that allowed worktree-helper.js
to be missed in any one call site. Replaces the source-text regex test
with a direct contains-check on the imported constant, covering all three
call sites and removing the process.cwd() fragility flagged in PR review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the awkward triple-tuple stdio assertion with a single-string
'pipe' + object-level `as const`. Cache not-a-repo, missing-git, and
timeout fallbacks in detectWorktreeContext so a broken-git project does
not re-pay the 2s timeout on every hook call within the same process.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
briansumma and others added 26 commits April 28, 2026 11:26
… path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eserve cause

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…etadata

Replace all bare catch {} blocks in init.ts with ENOENT-aware handlers.
ENOENT (file not found) is silently ignored; all other errors (EACCES,
corrupted JSON, EPERM, etc.) emit a console.warn so the user gets a
signal during openwolf init.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck sites

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…solution

Templates were never copied into the build output, so findTemplatesDir always
failed at runtime. Add a build:templates step and a correct candidate path
(../../templates from dist/src/cli → dist/templates).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getWolfDir() was returning <worktree>/.wolf which does not exist — .wolf/
only lives in the main checkout. This caused ensureWolfDir() to exit(0)
silently, making every hook a no-op in worktree mode. Session isolation
is already handled by getSessionDir(), so getWolfDir() should always
return mainRepoRoot/.wolf.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… modules

Consolidate isOpenWolfHook, replaceOpenWolfHooks, and findTemplatesDir which
were duplicated across init.ts and update.ts — each now lives in one canonical
module. Fixes path.basename("") bug in post-write.ts that produced empty
filenames in bug-log summaries, adds worktree guard to update command, and
removes redundant process.exit mock from session-start tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unnecessary path.basename() on function name in post-write.ts
- Standardize ?? operator in shared.ts for consistency with CLAUDE_PROJECT_DIR checks
- Add package-lock.json to .gitignore (pnpm-lock.yaml is authoritative)
- Add test for worktree session ledger path isolation in stop.ts

All changes verified: pnpm build passes, smoke test passes, all tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- post-write.ts: use ?? instead of || for CLAUDE_PROJECT_DIR to match
  shared.ts fix and correctly treat empty-string env var as unset
- stop.test.ts: replace inline require('node:fs') with named ESM
  imports (mkdirSync, existsSync, readFileSync) for consistency

Build passes, all 54 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@briansumma briansumma marked this pull request as ready for review May 14, 2026 19:01
@briansumma briansumma merged commit 5ede549 into main May 14, 2026
@briansumma briansumma deleted the port/pr-29-feature/25-git-worktree-support branch May 14, 2026 19:01
briansumma added a commit that referenced this pull request Jun 23, 2026
All 8 review points verified against the code and incorporated:

- #1 nested lock: updateJSON now extracts _writeJSONUnsafe instead of calling
  writeJSON inside its own lock (avoids EEXIST retry + spurious unlocked warning).
- #2 hooks boundary: NDJSON helper needs a self-contained copy in src/hooks/
  (can't import src/buglog under tsconfig.hooks.json) plus the canonical CLI copy.
- #3 stop.ts: added to the B4 table; nudge string match buglog.json -> buglog.
- #4 _session.json RMW: post-write.ts and stop.ts session writes added to A2,
  with a documented semantic caveat for shared-checkout session conflation.
- #5 logBug: noted as a 4th RMW site (CLI, unlocked); added B6 (lift withFileLock
  to a shared module if B3b locking is chosen).
- #6 atomicity wording: single-write-syscall framing, not "POSIX atomic".
- #7 testing: added concurrent append + read (torn final line) test.
- #8 TOCTOU: documented the staleness-storm bound.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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