Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .beastmode/artifacts/design/2026-04-12-lockfile-path-fix-dcd0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
phase: design
epic-id: bm-dcd0
epic-slug: lockfile-path-fix-dcd0
epic-name: Lockfile Path Fix
---

## Problem Statement

The watch loop lockfile path is hardcoded to `cli/.beastmode-watch.lock`. When the CLI runs from a different working directory (e.g., inside a worktree), the resolved path points to a non-existent directory, causing an ENOENT error that prevents the dashboard from starting.

## Solution

Move the lockfile from `cli/.beastmode-watch.lock` to `.beastmode/.beastmode-watch.lock`. The `.beastmode/` directory is project-rooted and always exists, making the lockfile resilient to working directory changes.

## User Stories

1. As a developer running `beastmode dashboard`, I want the lockfile to resolve correctly regardless of working directory, so that the dashboard starts without ENOENT errors.
2. As a developer with a stale lockfile from a previous session, I want the stale-PID detection to work at the new path, so that I don't get locked out.
3. As a developer, I want the lockfile gitignored at its new path, so that it never gets committed.

## Implementation Decisions

- Change `lockfilePath()` in `cli/src/lockfile.ts` to resolve to `.beastmode/` instead of `cli/`.
- Update the test in `cli/src/__tests__/watch.test.ts` that hardcodes the `cli/` path for the stale lockfile scenario.
- Update `.gitignore` entry from `.beastmode-watch.lock` to `.beastmode/.beastmode-watch.lock`.
- Update context doc references in `context/design/orchestration.md` and `context/design/cli.md` to reflect the new path.

## Testing Decisions

- Existing lockfile tests (acquire, release, stale detection) cover the behavior — only the path constant and the stale-lockfile test setup need updating.
- No new tests required; the fix is a path change, not a behavior change.

## Out of Scope

- Lockfile format changes.
- Additional lockfile features (timeout, retry, etc.).

## Further Notes

None.

## Deferred Ideas

None.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
phase: implement
epic-id: bm-dcd0
epic-slug: lockfile-path-fix-dcd0
feature-id: lockfile-path-fix-dcd0.1
feature-name: Lockfile Path Fix
feature-slug: lockfile-path-fix-dcd0.1
status: completed
---

# Implementation Report: Lockfile Path Fix

**Date:** 2026-04-12
**Feature Plan:** .beastmode/artifacts/plan/2026-04-12-lockfile-path-fix-dcd0--lockfile-path-fix.1.md
**Tasks completed:** 3/3
**Review cycles:** 6 (spec: 3, quality: 3)
**Concerns:** 0
**BDD verification:** skipped

## Completed Tasks
- Task 1: Update lockfile path and test (haiku) — clean
- Task 2: Update .gitignore entry (haiku) — clean
- Task 3: Update context documentation (haiku) — clean

## Concerns
None.

## Blocked Tasks
None.

## BDD Verification
- Result: skipped
- Reason: No Integration Test Scenarios in feature plan — skip gate classified this feature as non-behavioral.

All tasks completed cleanly — no concerns or blockers.
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# Lockfile Path Fix -- Write Tasks

## Goal

Move the watch loop lockfile from `cli/.beastmode-watch.lock` to `.beastmode/.beastmode-watch.lock` so that the lockfile resolves correctly regardless of working directory. The `.beastmode/` directory is project-rooted and always exists, eliminating ENOENT errors when the dashboard runs from worktree directories.

## Architecture

Single-constant path change in `cli/src/lockfile.ts`. All lockfile consumers (acquire, release, read, stale detection) call the same `lockfilePath()` function, so changing the constant propagates everywhere. No behavioral changes -- only the resolved filesystem path differs.

## Tech Stack

- Runtime: Bun
- Language: TypeScript
- Test runner: vitest (run via `bun --bun vitest run`)
- Test location: `cli/src/__tests__/watch.test.ts`

## File Structure

| File | Action | Responsibility |
|------|--------|---------------|
| `cli/src/lockfile.ts` | Modify | Change path resolution from `cli/` to `.beastmode/`; update module doc comment |
| `cli/src/__tests__/watch.test.ts` | Modify | Update hardcoded stale-lockfile path in test from `cli/` to `.beastmode/` |
| `.gitignore` | Modify | Replace bare `.beastmode-watch.lock` with specific `.beastmode/.beastmode-watch.lock` |
| `.beastmode/context/design/orchestration.md` | Modify | Update path reference in Recovery section |
| `.beastmode/context/design/cli.md` | Modify | Update path reference in Recovery Model section |

## Wave Isolation

| Wave | Tasks | Files | Parallel-safe | Reason |
|------|-------|-------|---------------|--------|
| 1 | T1, T2, T3 | T1: `cli/src/lockfile.ts`, `cli/src/__tests__/watch.test.ts` / T2: `.gitignore` / T3: `.beastmode/context/design/orchestration.md`, `.beastmode/context/design/cli.md` | no | T2 and T3 are independent but T1 must precede test verification; sequential is safer for 3 trivial tasks |

## Tasks

### Task 1: Update lockfile path and test

**Wave:** 1
**Depends on:** -

**Files:**
- Modify: `cli/src/lockfile.ts:4,17`
- Modify: `cli/src/__tests__/watch.test.ts:57`
- Test: `cli/src/__tests__/watch.test.ts`

- [x] **Step 1: Update the module doc comment in lockfile.ts**

In `cli/src/lockfile.ts`, replace the doc comment on line 4 that says `cli/.beastmode-watch.lock` with `.beastmode/.beastmode-watch.lock`:

```typescript
/**
* Lockfile manager — prevents duplicate watch instances.
*
* Creates .beastmode/.beastmode-watch.lock on start, removes on clean shutdown.
* Detects stale lockfiles by checking if the PID is still running.
*/
```

The old comment reads:

```typescript
/**
* Lockfile manager — prevents duplicate watch instances.
*
* Creates cli/.beastmode-watch.lock on start, removes on clean shutdown.
* Detects stale lockfiles by checking if the PID is still running.
*/
```

- [x] **Step 2: Change the path constant in lockfilePath()**

In `cli/src/lockfile.ts`, line 17, change `"cli"` to `".beastmode"` in the `lockfilePath` function:

```typescript
function lockfilePath(projectRoot: string): string {
return resolve(projectRoot, ".beastmode", LOCKFILE_NAME);
}
```

The old code reads:

```typescript
function lockfilePath(projectRoot: string): string {
return resolve(projectRoot, "cli", LOCKFILE_NAME);
}
```

- [x] **Step 3: Update the stale-lockfile test path**

In `cli/src/__tests__/watch.test.ts`, line 57, change the hardcoded lockfile path in the "detects stale lockfile (dead PID)" test from `"cli"` to `".beastmode"`:

```typescript
const lockPath = resolve(TEST_ROOT, ".beastmode", ".beastmode-watch.lock");
```

The old code reads:

```typescript
const lockPath = resolve(TEST_ROOT, "cli", ".beastmode-watch.lock");
```

- [x] **Step 4: Run all lockfile tests to verify they pass**

Run: `cd cli && bun --bun vitest run src/__tests__/watch.test.ts -t "lockfile" --reporter=verbose`

Expected: All 4 lockfile tests PASS:
- acquires lock when no lockfile exists
- prevents duplicate lock acquisition
- releases lock cleanly
- detects stale lockfile (dead PID)

Note: The test setup already creates both `cli/` and `.beastmode/` directories under `TEST_ROOT` (see `setupTestRoot()` at line 13-17 of watch.test.ts), so no test fixture changes are needed beyond the path string.

- [x] **Step 5: Run the full watch test suite to verify nothing else broke**

Run: `cd cli && bun --bun vitest run src/__tests__/watch.test.ts --reporter=verbose`

Expected: All tests in the file PASS (lockfile, DispatchTracker, and WatchLoop tests).

- [x] **Step 6: Commit**

```bash
git add cli/src/lockfile.ts cli/src/__tests__/watch.test.ts
git commit -m "fix(lockfile): resolve lockfile path to .beastmode/ instead of cli/"
```

### Task 2: Update .gitignore entry

**Wave:** 1
**Depends on:** Task 1

**Files:**
- Modify: `.gitignore:10`

- [x] **Step 1: Replace the gitignore entry**

In `.gitignore`, line 10, replace the bare pattern `.beastmode-watch.lock` with the specific path `.beastmode/.beastmode-watch.lock`:

```
.beastmode/.beastmode-watch.lock
```

The old entry reads:

```
.beastmode-watch.lock
```

The full `.gitignore` after this change should read:

```
# Beastmode session state and worktrees
.beastmode/state/
.beastmode/sessions/
.beastmode/worktrees/
.beastmode/pipeline/
.beastmode/config.yaml
.claude/worktrees/
.claude/settings.local.json
.beastmode/artifacts/**/*.output.json
.beastmode/.beastmode-watch.lock
.DS_Store
```

- [x] **Step 2: Verify the gitignore entry works**

Run: `cd /Users/D038720/Code/github.com/bugroger/beastmode/.claude/worktrees/lockfile-path-fix-dcd0 && git check-ignore .beastmode/.beastmode-watch.lock`

Expected: Output `.beastmode/.beastmode-watch.lock` (the path is ignored).

- [x] **Step 3: Commit**

```bash
git add .gitignore
git commit -m "fix(gitignore): update lockfile entry to match new .beastmode/ path"
```

### Task 3: Update context documentation

**Wave:** 1
**Depends on:** Task 1

**Files:**
- Modify: `.beastmode/context/design/orchestration.md:31`
- Modify: `.beastmode/context/design/cli.md:34`

- [x] **Step 1: Update orchestration.md**

In `.beastmode/context/design/orchestration.md`, line 31, replace the old path with the new one:

```markdown
- Lockfile (`.beastmode/.beastmode-watch.lock`) prevents duplicate watch instances — single orchestrator guarantee
```

The old line reads:

```markdown
- Lockfile (`cli/.beastmode-watch.lock`) prevents duplicate watch instances — single orchestrator guarantee
```

- [x] **Step 2: Update cli.md**

In `.beastmode/context/design/cli.md`, line 34, replace the old path with the new one:

```markdown
- Lockfile (`.beastmode/.beastmode-watch.lock`) prevents duplicate watch instances
```

The old line reads:

```markdown
- Lockfile (`cli/.beastmode-watch.lock`) prevents duplicate watch instances
```

- [x] **Step 3: Commit**

```bash
git add .beastmode/context/design/orchestration.md .beastmode/context/design/cli.md
git commit -m "docs: update lockfile path references in context docs"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
phase: plan
epic-id: bm-dcd0
epic-slug: lockfile-path-fix-dcd0
feature-name: Lockfile Path Fix
wave: 1
---

# Lockfile Path Fix

**Design:** `.beastmode/artifacts/design/2026-04-12-lockfile-path-fix-dcd0.md`

## User Stories

1. As a developer running `beastmode dashboard`, I want the lockfile to resolve correctly regardless of working directory, so that the dashboard starts without ENOENT errors.
2. As a developer with a stale lockfile from a previous session, I want the stale-PID detection to work at the new path, so that I don't get locked out.
3. As a developer, I want the lockfile gitignored at its new path, so that it never gets committed.

## What to Build

Change the lockfile resolution from the `cli/` subdirectory to the `.beastmode/` directory, which is project-rooted and always exists regardless of working directory.

The lockfile module's path function currently resolves to `<projectRoot>/cli/<lockfile-name>`. Change it to resolve to `<projectRoot>/.beastmode/<lockfile-name>`. This is a single-constant change — all consumers (acquire, release, read, stale detection) go through the same path function, so behavior is preserved.

Update the test that manually constructs the lockfile path for the stale-PID scenario to use the new `.beastmode/` directory.

Update the `.gitignore` entry to match the new specific path `.beastmode/.beastmode-watch.lock` instead of the bare pattern `.beastmode-watch.lock`.

Update context documentation references in orchestration and CLI design docs that cite the old `cli/.beastmode-watch.lock` path.

## Integration Test Scenarios

<!-- No behavioral scenarios — skip gate classified this feature as non-behavioral -->

## Acceptance Criteria

- [ ] `lockfilePath()` resolves to `<projectRoot>/.beastmode/.beastmode-watch.lock`
- [ ] Existing lockfile tests pass (acquire, release, stale detection) with no behavior changes
- [ ] `.gitignore` contains the new path entry
- [ ] Context docs reference the new path
- [ ] Dashboard starts without ENOENT when run from a worktree directory
34 changes: 34 additions & 0 deletions .beastmode/artifacts/release/2026-04-12-lockfile-path-fix-dcd0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
phase: release
epic-id: lockfile-path-fix-dcd0
epic-slug: lockfile-path-fix-dcd0
bump: patch
---

# Release: lockfile-path-fix-dcd0

**Version:** v0.127.2
**Date:** 2026-04-12

## Highlights

Fixes ENOENT error when starting the dashboard from a worktree by moving the watch loop lockfile from `cli/` to the project-rooted `.beastmode/` directory.

## Fixes

- Resolve lockfile path to `.beastmode/.beastmode-watch.lock` instead of `cli/.beastmode-watch.lock`
- Update `.gitignore` entry to match new lockfile path

## Docs

- Update lockfile path references in `context/design/orchestration.md` and `context/design/cli.md`

## Full Changelog

- `8ba83c61` fix(lockfile): resolve lockfile path to .beastmode/ instead of cli/
- `e4876797` fix(gitignore): update lockfile entry to match new .beastmode/ path
- `6a4bcb9a` docs: update lockfile path references in context docs
- `cacdd19c` implement(lockfile-path-fix-dcd0): checkpoint
- `5f49351b` validate(lockfile-path-fix-dcd0): checkpoint
- `64dcf079` plan(lockfile-path-fix-dcd0): checkpoint
- `c076f7ed` design(lockfile-path-fix-dcd0): checkpoint
Loading