CodeNomad/CONTRIBUTING.md
Pascal André 83a4124b37
feat(v2): manage session families across worktrees (#649)
## Summary

Manage complete session families across the opened local repository's
native OpenCode worktrees. This supersedes #642 with the V2
implementation and addresses #319.

- OpenCode owns `worktree.refresh/list/create/remove`; CodeNomad
supplies the default directory, named-branch policy, local repository
scope and verified family transactions.
- A project is the opened local checkout and its registered worktrees,
identified through the physical Git common directory. Independent clones
sharing an OpenCode project ID stay separate.
- Session rows, composer selection, search and the Git panel follow
native `SessionInfo.location.directory`. Durable metadata is preserved,
but old `worktreeSlug` metadata does not override native placement.
- Creation uses the selected checkout's revision and the main local
checkout's `.codenomad/worktrees` parent. Uncommitted edits stay in the
source checkout. External registered worktrees remain discoverable.
- Keep copy/open/delete actions in the accessible selector, remove the
redundant action bar, hide root-workspace badges, and explain “Create
and use” in all ten locales.

## Current base and native contract

This branch includes merged `dev@e47e01c6`, including #697's separately
reviewed discovery/status compatibility fix. UI/server pin
`@opencode/client@2.0.4`; the independently managed daemon has no
exact-version startup gate.

- Complete bounded inventories use opaque native cursors; continuations
send only the cursor.
- Moves are serialized across complete families, block active sessions,
verify authoritative native locations and roll back partial failures.
- Native move events refresh placement and the catalogue without
speculative cross-project transfers.
- Opaque worktree identifiers are separate from mutable branch labels.
The main checkout cannot be removed.
- Native detached creation is followed by ordinary Git branch selection,
preserving Git's refusal to reuse a checked-out branch;
branch-attachment failure cleans up through the native API.
- Agent placement instructions distinguish creating a checkout, one-off
commands and moving the conversation's execution context. Explicit
attachment choices are preserved.

## Deletion safety

- Physical checkout reservations cover published and in-flight logical
workspaces.
- Project serialization and deletion fences cover all family
source/destination checkouts.
- Complete inventories and cross-project checks run inside the deletion
fence, including nested workspaces and owned Shell/PTY resources.
- Canonical path, filesystem identity, registration, branch, HEAD and
active sessions are rechecked before removal. Replacement checkouts fail
closed; rollback requires the original identity.

## Recovery and Git-panel corrections

Validation on the real repository exposed serial enumeration of 162
worktrees and concurrent ownership misses launching overlapping
inventories. Git annotations now run with bounded concurrency, pending
catalogue reads are shared, and negative ownership results are
snapshot-cached. A regression exercises 40 concurrent misses.

The Git panel now treats its complete local staged/unstaged inventory as
authoritative instead of resurrecting native-only stale rows. It also
rechecks the selected worktree/request after waiting for native VCS
status.

## Validation

Latest integration validation:

- Isolated official **OpenCode 2.0.0 and 2.0.7**: production
discovery/status, native worktree discovery/create/remove, selected
HEAD/default parent/named branches, independent-clone scope, stable IDs,
nested paths, checked-out/dirty guards, complete family moves and
rollback.
- Real Solid selector browser tests: mouse, keyboard, touch, actions
without session movement, creation source and returned stable ID.
- Targeted server ownership/cache/manager/proxy/route/location tests; UI
session projection/request-authority/lifecycle tests; family
evacuation/rollback and Git model regressions.
- UI/server typechecks and production server build including the UI.
- Installed Windows Tauri application on the actual repository:
dedicated session creation/use, native location and row/composer badges,
Git marker visibility, move back, and agent `session_move` reflected in
both badges.
- Rebuilt UI/server resources installed; full Developer Mode restart
restored the original conversation. Disposable sessions, marker,
worktrees and branches cleaned up. The original conversation remained
attached to `pr649-final`.
- `git diff --check`.

An immediate post-restart desktop run exceeded a 30-second
refresh-button wait; the subsequent completed run passed. These checks
validate the workflows, not a startup latency bound or exhaustive
compatibility of every OpenCode payload.

Earlier branch validation also covered Electron/Tauri native actions,
packaging and desktop parity; those results remain in the PR's
historical validation comments. Detailed contract and latest evidence:
`dev-docs/WORKTREE_SESSION_PLACEMENT.md`.

## Residual limit

OpenCode and Git expose no shared snapshot/CAS transaction. CodeNomad
narrows races with reservations, locks, fences and repeated
authoritative checks; unrelated processes can still mutate state outside
those controls.
2026-09-17 22:57:37 +02:00

8.3 KiB

Contributing to CodeNomad

Thank you for your interest in contributing! This guide will help you get started.

Prerequisites

  • Node.js 18+ and npm
  • OpenCode CLI in your PATH (CodeNomad uses one shared native V2 service for all workspace locations)

Quick Start

git clone https://github.com/NeuralNomadsAI/CodeNomad.git
cd CodeNomad
npm install
npm run dev

Finding Issues to Work On

Browse open issues and look for these labels:

Label Meaning
ready-to-work Clear scope, ready for anyone to pick up
good-first-issue Good for first-time contributors
enhancement New feature requests
bug Bug reports

Before starting: comment on the issue so we can discuss approach and avoid duplicate work.

Development Workflow

1. Fork and Branch

# Fork the repo on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/CodeNomad.git
cd CodeNomad

# Add the upstream remote
git remote add upstream https://github.com/NeuralNomadsAI/CodeNomad.git

# Create a branch from upstream/dev
git fetch upstream
git checkout -b fix/your-branch-name upstream/dev

2. Branch Naming

Prefix Use for
fix/ Bug fixes
feat/ New features
docs/ Documentation changes
refactor/ Code refactoring
chore/ Build, config, maintenance

Examples: fix/question-queue-ordering, feat/retry-tool-call, docs/contributing-guide

3. Make Your Changes

# Install dependencies
npm install

# Run the dev server
npm run dev

# Run type checking
npm run typecheck --workspace @codenomad/ui

4. Commit

Write clear, descriptive commit messages. Explain what changed and why.

git add .
git commit -m "fix(ui): preserve question queue order when upserting duplicate requests

When a question arrives as a global entry and later resolves to a tool
part with a newer timestamp, the original enqueue time was lost, causing
the question to move behind newer entries and break interruption order."

5. Push and Create a PR

git push origin your-branch-name

Then open a pull request on GitHub targeting the dev branch.

PR checklist:

  • Branch is based on latest upstream/dev
  • One issue per PR (don't mix unrelated changes)
  • Type checking passes: npm run typecheck (root) or the workspace-specific script matching your change area
  • Tests pass (if applicable)
  • PR description explains the change, includes relevant screenshots for UI changes, and links related issues when applicable

Project Structure

Package Description
packages/server Core logic & CLI — workspaces, OpenCode proxy, API, auth
packages/ui SolidJS frontend — reactive UI components and stores
packages/electron-app Electron desktop shell
packages/tauri-app Tauri desktop shell (experimental)
packages/cloudflare Cloudflare deployment adapters

OpenCode V2 Boundaries

  • Server and UI pin @opencode/client@2.0.4; the pruning plugin pins @opencode/plugin@2.0.4. Upgrade them together with the lockfile and isolated native validation. The runtime CLI is managed independently, and startup must not reject an otherwise compatible service solely for a different version string. Review current OpenCode documentation, installed declarations, and proxy/API parity whenever the client contract changes.
  • Upgrade references: OpenCode releases, OpenCode V2 documentation, and node_modules/@opencode/client/dist/promise/.
  • packages/server/src/workspaces/opencode-service.ts uses the selected host or WSL CLI's official service status, service start, and service get password lifecycle to connect to one externally owned global daemon. CodeNomad owns no private port, database, registration, or daemon PID and never stops the daemon on backend shutdown.
  • WSL requires Windows localhost forwarding and runs the Linux CLI lifecycle inside the distribution; never inspect or signal Linux PIDs from Windows.
  • OpenCode owns the global daemon's standard state and database. Configured allowed environment variables apply only when CodeNomad starts a missing daemon; an existing daemon is unchanged, and legacy OPENCODE_DB/XDG_STATE_HOME ownership settings are ignored.
  • Explicit Stop Workspace evicts the native location/resources. Closing a tab or window only detaches that local UI and must never delete or evict the workspace.
  • OpenCode session calls use /workspaces/:id/instance/api/*; CodeNomad control routes and multiplexed events use /api/* and /api/events.
  • The proxy is method/path allowlisted, so new upstream functionality is not exposed automatically.
  • Shell mode (client.session.shell) and prompt instructions (client.session.instructions.entry) remain separate from background shells and interactive PTYs.
  • Location-scoped background shells use client.shell.* and are listed in the Status panel. The UI refreshes them on Shell events and reconnect, displays native metadata, and supports ownership-checked removal. client.pty.* remains reserved for interactive terminals. packages/opencode-plugin and the server plugin/background-process paths remain deleted and must not be restored.
  • Native events are volatile. Reconnect handlers must refetch authoritative state instead of assuming missed events will replay.
  • Git mutations and Yolo policy remain CodeNomad-owned server boundaries.
  • Native desktop identity is channel plus config profile: one singleton process/backend per profile and multiple UUID windows. A second launch opens another window by default; Advanced settings can restore MRU focus, while --new-window always requests another window. Stable, dev, and non-default profiles isolate native/browser/client state; OpenCode sessions/messages stay shared while tabs, drafts, and views are per-window.
  • Desktop restore uses a V3 per-window envelope over the V2 content-addressed partition graph. Preserve atomic publication/migration, ownership write fencing, and post-commit conservative garbage collection in both Electron and Tauri.
  • Native SideCar/browser previews are sandboxed without same-origin access, so DOM comment inspection is web-only.

Key UI Files

Path Purpose
packages/ui/src/stores/session-events.ts SSE event handlers (idle, status, permissions, questions)
packages/ui/src/stores/session-actions.ts User actions (send message, abort, revert, fork)
packages/ui/src/stores/message-v2/ Message store (v2 architecture)
packages/ui/src/stores/instances.ts Instance management and interruption queues
packages/ui/src/components/tool-call.tsx Tool call rendering
packages/ui/src/components/message-block.tsx Message display blocks
packages/ui/src/components/session/session-view.tsx Main session view
packages/ui/src/lib/i18n/messages/ Translation files (en, es, fr, ja, ru, he, zh-Hans, de, ne, tr)

For the package map, native OpenCode V2 integration, ownership boundaries, and feature traces, load the codenomad-architecture-guide skill: .opencode/skills/codenomad-architecture-guide/SKILL.md

Styling

  • Tokens: packages/ui/src/styles/tokens.css
  • Utilities: packages/ui/src/styles/utilities.css
  • Component styles: packages/ui/src/styles/components/, packages/ui/src/styles/messaging/, packages/ui/src/styles/panels/
  • Keep style files under ~150 lines; split by component

Internationalization (i18n)

  • Use useI18n() in components, tGlobal() in stores
  • Messages live in packages/ui/src/lib/i18n/messages/<locale>/
  • When adding a string: add to en/ first, then add the same key to every other locale
  • Placeholders use {name} syntax (word characters only)

Code Principles

  • KISS: Keep modules narrowly scoped
  • DRY: Share helpers before copy-pasting
  • Single responsibility: Split files when concerns diverge
  • Composable primitives: Prefer signals, hooks, utilities over deep inheritance

Need Help?