Skip to content

fix: v2 QA fixes — 12 bugs, 7 groups, 708 tests pass - #560

Merged
namastex888 merged 4 commits into
devfrom
fix/v2-qa-fixes
Mar 14, 2026
Merged

namastex888 merged 4 commits into
devfrom
fix/v2-qa-fixes

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Summary

Fixes 12 bugs (#546-#555, #558, #559) found during comprehensive QA of the v2 redesign. Zero plan changes — all fixes match the original v2 spec.

Changes by Group

Group 1: Shared File Lock Extraction (#550)

  • New src/lib/file-lock.ts — deduplicated from 3 identical copies
  • Updated agent-directory, wish-state, agent-registry to use shared module

Group 2: Concurrency Fixes (#547, #555)

  • Added file lock to mailbox.send() and markDelivered() — was losing 9/10 messages under concurrent writes
  • Added file lock to team-chat.postMessage()

Group 3: Wish State Hardening (#548, #549, #552, #553)

  • Cycle detection in createState() via Kahn's algorithm
  • Dangling dep validation (throw on refs to non-existent groups)
  • Strict transitions: completeGroup() requires in_progress state
  • resetGroup() + genie reset slug#group CLI for leader recovery
  • JSDoc on getState() documenting lockless reads

Group 4: Spawn & Team Fixes (#546, #551, #554)

  • Fixed spawn CWD to use team worktree for built-in agents
  • validateBranchName() rejects git-unsafe team names
  • parseWishGroups() now case-insensitive

Group 5: Global Team Configs (#558)

  • Moved team configs from <repo>/.genie/teams/ to ~/.genie/teams/
  • All team commands work from ANY CWD — zero process.cwd() dependency
  • getTeam(), listTeams(), listMembers() no longer take repoPath param

Group 6: Fix genie update (#559)

  • Version read from package.json at runtime instead of hardcoded in dist

Issues Fixed

# Title Severity
#546 Spawn CWD ignores team worktree High
#547 Mailbox loses messages (no lock) Critical
#548 No cycle detection in deps Medium
#549 ready->done skips in_progress Design
#550 File lock duplicated 3x Refactor
#551 Team name not validated Medium
#552 No reset mechanism for stuck groups Enhancement
#553 getState() lockless reads Low
#554 parseWishGroups case-sensitive Medium
#555 team-chat JSONL no lock Medium
#558 Team commands fail from non-repo CWD Critical
#559 genie update doesn't update version Medium

Test plan

  • bun run typecheck — passes
  • bun run lint — passes (5 pre-existing warnings)
  • bun test — 708 pass, 0 fail
  • bun run build — succeeds

Test User and others added 2 commits March 14, 2026 14:12
… version

Group 1: Extract shared file-lock utility (src/lib/file-lock.ts)
- Deduplicate lock pattern from agent-directory, wish-state, agent-registry
- All 3 modules now import from file-lock.ts

Group 4: Spawn & team fixes
- Fix spawn CWD to use team worktree path for built-in agents (#546)
- Add validateBranchName() in team-manager (#551)
- Make parseWishGroups() case-insensitive (#554)

Group 5: Global team configs (#558)
- Move team configs from <repo>/.genie/teams/ to ~/.genie/teams/
- Drop repoPath param from getTeam/listTeams/listMembers
- All team commands resolve repo from stored config, not CWD
- Update all callers across codebase

Group 6: Fix genie update (#559)
- Read version from package.json at runtime instead of hardcoded

3 test failures remain (team-manager signature changes in test mocks)
Groups 2 (concurrency) and 3 (wish state hardening) still pending.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Fix 3 failing tests: isolate GENIE_HOME in team.test.ts and
  msg.test.ts so team manager reads from test-local directories
- Add file-lock concurrency protection to mailbox.send(),
  mailbox.markDelivered(), and team-chat.postMessage()
- Add wish-state validation: self-dep, dangling dep, and cycle
  detection (Kahn's algorithm) in createState()
- Enforce strict completeGroup() transitions (must be in_progress)
- Add resetGroup() to revert in_progress groups back to ready
- Add JSDoc to getState() documenting lockless-by-design reads
- Add reset CLI command in state.ts
- Add comprehensive tests for validation and resetGroup
@coderabbitai

coderabbitai Bot commented Mar 14, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 77d8b195-d2d3-4241-b2ec-f48266589d44

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/v2-qa-fixes
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses 12 identified bugs and design issues from the v2 redesign, significantly enhancing the stability, robustness, and usability of the system. The changes focus on improving concurrent data access through shared file locking, strengthening wish state management with advanced validation and recovery mechanisms, and refining team and agent interactions by centralizing configurations and improving command-line flexibility. These fixes ensure a more reliable and consistent user experience across various core functionalities.

Highlights

  • Shared File Locking: Introduced a new shared file-locking utility (src/lib/file-lock.ts) to centralize and deduplicate file locking logic previously scattered across agent-directory, wish-state, and agent-registry modules. This improves consistency and maintainability.
  • Concurrency Fixes: Implemented file locks in mailbox.send() and mailbox.markDelivered() to prevent message loss under concurrent writes, and added a file lock to team-chat.postMessage() to ensure data integrity.
  • Wish State Hardening: Enhanced the wish state management with cycle detection (Kahn's algorithm), validation for dangling dependencies, and strict enforcement of in_progress status for group completion. A new resetGroup() function was added for leader recovery, and getState() was documented for lockless reads.
  • Spawn & Team Fixes: Corrected the Current Working Directory (CWD) for spawned built-in agents to use the team's worktree path. Added validation for team names to reject git-unsafe characters and made parseWishGroups() case-insensitive for improved flexibility.
  • Global Team Configurations: Migrated team configuration files from repository-specific .genie/teams/ to a global ~/.genie/teams/ directory. This change allows team commands to function correctly from any CWD, removing process.cwd() dependencies from getTeam(), listTeams(), and listMembers().
  • Dynamic Versioning for genie update: Updated the version retrieval mechanism to read from package.json at runtime, ensuring that genie update accurately reflects the current version without requiring hardcoded values in the distribution.
Changelog
  • src/lib/agent-directory.ts
    • Removed duplicate file locking logic.
    • Imported shared acquireLock function.
  • src/lib/agent-registry.ts
    • Removed duplicate file locking logic.
    • Imported shared acquireLock function.
  • src/lib/file-lock.ts
    • Added new shared file locking utility with acquireLock and withLock functions.
  • src/lib/mailbox.ts
    • Imported acquireLock function.
    • Added file locks to send and markDelivered functions to prevent race conditions.
  • src/lib/protocol-router-spawn.ts
    • Updated resolveParentSession to use the global getTeam function.
    • Added logic to override the spawned agent's CWD with the team's worktree path.
  • src/lib/team-chat.ts
    • Imported acquireLock function.
    • Added a file lock to the postMessage function for concurrent write protection.
  • src/lib/team-manager.test.ts
    • Added tests for validateBranchName function.
    • Updated test setup to use GENIE_HOME environment variable for global team storage.
    • Modified createTeam tests to verify global config storage and invalid branch name rejection.
    • Updated calls to getTeam, listTeams, hireAgent, fireAgent, disbandTeam, and listMembers to remove the repoPath parameter.
  • src/lib/team-manager.ts
    • Moved team configuration storage to a global ~/.genie/teams/ directory.
    • Removed repoPath parameter from teamsDir, teamFilePath, getTeam, listTeams, hireAgent, fireAgent, disbandTeam, and listMembers functions.
    • Added validateBranchName function to enforce valid git branch naming conventions for team names.
    • Integrated validateBranchName into createTeam to reject invalid team names.
  • src/lib/version.ts
    • Modified version retrieval to dynamically read from package.json at runtime instead of using a hardcoded value.
  • src/lib/wish-state.test.ts
    • Added tests for resetGroup functionality.
    • Included tests for cycle detection and dangling dependency validation in createState.
    • Updated completeGroup tests to enforce strict in_progress status before completion.
  • src/lib/wish-state.ts
    • Removed duplicate file locking logic.
    • Imported shared acquireLock function.
    • Implemented validateGroupRefs and detectCycles functions for dependency validation.
    • Integrated validateGroups into createState to prevent self-dependencies, dangling dependencies, and cycles.
    • Enforced strict in_progress status check for completeGroup.
    • Added resetGroup function to reset an in-progress group to ready status.
  • src/term-commands/agents.ts
    • Updated resolveNativeTeam to use the global getTeam function.
    • Modified handleWorkerSpawn to set the agent's repoPath to the team's worktree path if available.
  • src/term-commands/dispatch.test.ts
    • Added tests to verify case-insensitive parsing of wish groups by parseWishGroups.
  • src/term-commands/dispatch.ts
    • Modified the groupPattern regex in parseWishGroups to be case-insensitive.
  • src/term-commands/msg.test.ts
    • Updated test setup to configure the GENIE_HOME environment variable for global team storage.
  • src/term-commands/msg.ts
    • Updated checkSendScope and findAgentTeam to use the global listTeams function and removed the repoPath parameter.
  • src/term-commands/state.ts
    • Added a new reset command for wish groups, allowing an in-progress group to be reset to ready.
  • src/term-commands/team.test.ts
    • Updated test setup to configure the GENIE_HOME environment variable for global team storage.
    • Modified genie command execution to pass the GENIE_HOME environment variable.
  • src/term-commands/team.ts
    • Removed repoPath parameter from autoDetectTeam, printMembers, printTeams, hireAgent, fireAgent, and disbandTeam calls, aligning with global team config storage.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This is an impressive pull request that addresses a wide range of bugs and makes significant improvements to the system's robustness and maintainability. The extraction of the file locking mechanism into a shared module is a great refactoring. The addition of concurrency controls, state machine hardening with cycle detection, and moving team configurations to be global are all excellent changes. The code is well-structured and includes thorough tests for the new functionality. I have one critical concern regarding the new file lock implementation, which could lead to race conditions.

Comment thread src/lib/file-lock.ts
Comment on lines +83 to +90
if (Date.now() > deadline) {
try {
await unlink(lockPath);
} catch {
throw new Error(`Lock timeout: could not remove stale lock at ${lockPath}`);
}
continue;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

This timeout logic introduces a critical race condition. Forcefully unlinking a lock file simply because this process has timed out waiting for it can lead to a state where two processes believe they hold the lock, potentially causing data corruption. If another process holds the lock for a legitimate, long-running operation, this code will break that lock and proceed, violating the mutual exclusion guarantee.

The safer approach is to simply throw an error when the timeout is exceeded, and rely on the existing stale lock cleanup (tryCleanStaleLock) to handle locks left behind by dead processes.

    if (Date.now() > deadline) {
      throw new Error(`Lock timeout: could not acquire lock for ${lockPath} within ${LOCK_TIMEOUT_MS}ms.`);
    }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fd5fc220a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/team-manager.ts
Comment on lines +192 to 195
const filePath = teamFilePath(name);
if (existsSync(filePath)) {
const content = await readFile(filePath, 'utf-8');
return JSON.parse(content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include repo identity in team config lookup

createTeam now treats a team name as globally unique by checking only teamFilePath(name) under ~/.genie/teams, so creating feat/foo in repo B after repo A will return repo A's stored config instead of creating a new worktree for repo B. This makes subsequent hire/fire/disband operations act on the wrong repository whenever branch-style team names are reused across repos, which is a common workflow.

Useful? React with 👍 / 👎.

Comment thread src/term-commands/msg.ts

const teamManager = await getTeamManager();
const teams = await teamManager.listTeams(repoPath);
const teams = await teamManager.listTeams();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope message checks to the active repository

checkSendScope still receives repoPath from genie send, but it now ignores it and evaluates membership against listTeams() globally. If an agent name exists in a team from another repo, sends from the current repo can be incorrectly rejected with a scope violation even though no local team relationship exists.

Useful? React with 👍 / 👎.

Comment thread src/lib/version.ts
// Try paths relative to this module
const candidates = [
// From src/lib/version.ts → ../../package.json
resolve(dirname(import.meta.dir ?? __dirname), '..', '..', 'package.json'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve runtime version from the project package first

The first candidate path uses dirname(import.meta.dir) before walking ../.., which overshoots by one directory in source mode and can select a parent workspace package.json instead of this project's file. In that setup, VERSION reports the outer package version, so the CLI can display an unrelated version after update.

Useful? React with 👍 / 👎.

Test User and others added 2 commits March 14, 2026 14:58
Add build + publish steps to version.yml so dev merges
publish to npm under the `next` dist-tag.

Install with: bun add -g @automagik/genie@next

Co-Authored-By: Paperclip <noreply@paperclip.ing>
genie update --next    Switch to dev builds (@next npm tag)
genie update --stable  Switch to stable releases (@latest npm tag)
genie update           Uses last selected channel (default: latest)

Channel preference persisted in ~/.genie/config.json (updateChannel field).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
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