Skip to main content

Serialized Codex collaboration for shared Git repositories

Project description

cocodex

中文说明

cocodex coordinates multiple Codex sessions that share one Git repository on one server account. It gives every developer an isolated managed worktree, then serializes the moment when a developer's work becomes the next main.

Core Rule

For day-to-day collaboration, a developer only needs one Cocodex command:

cocodex sync

sync means "move this Cocodex session to the next safe synchronization state." Depending on the session state, it can:

  • fast-forward a clean session to the latest main;
  • publish a dirty session directly when it is already based on latest main;
  • when both the dirty session and main advanced, try a locked Git merge with lightweight structural checks and publish it if clean;
  • start a dirty session's semantic integration when Git cannot merge cleanly or the lightweight checks fail;
  • after Cocodex gives the session a task, publish the committed candidate as the new main.

Developers and Codex sessions must not run git pull main, git merge main, or git push main directly. Cocodex is the only writer to local main. Cocodex installs local Git hooks that block ordinary direct writes and pushes to main; intentional maintenance must be done outside the developer workflow.

If a remote is configured, every successful local cocodex sync also tries to force-sync local main, the current session branch, and Cocodex recovery refs to that remote. It does not push or prune other developers' branches. Remote sync is best-effort: network or authentication failures are reported as warnings and retried on later successful cocodex sync commands.

The daemon does not automatically integrate dirty sessions. Local work stays in the developer's managed worktree until that developer or their Codex explicitly runs cocodex sync from inside that worktree. In Codex, run it as a shell command, for example !cocodex sync.

Roles

Operator/startup commands, run from the project repository:

cocodex init --main main --remote origin
cocodex daemon
cocodex join alice

Developer collaboration command, run from inside that developer's managed worktree, usually through Codex as !cocodex sync:

cocodex sync

Inspection commands:

cocodex status
cocodex log

There are no manual recovery commands. If a sync cannot proceed, run cocodex status or cocodex log for inspection, then let the named owning developer run cocodex join <name> if needed and cocodex sync from their own managed worktree.

Installation

After the first PyPI release:

pip install cocodex

For development from a local checkout:

pip install -e .

This installs the cocodex console command.

Repository Setup

Run these commands in the project repository that the team wants to develop with Cocodex.

The configured main branch must already exist and have an initial commit:

git switch -c main
git add .
git commit -m "initial commit"

If Cocodex should keep a remote copy of the server's local branches, add the remote before initialization:

git remote add origin <url>

Initialize Cocodex once:

cocodex init --main main --remote origin

init refuses to overwrite an existing .cocodex/config.json, because that file contains developer identities and launch commands. Use cocodex init --force only when you intentionally want to replace the existing Cocodex configuration.

Use --remote origin only if that remote exists. With a remote configured, cocodex sync force-pushes local main and the current session branch to that remote. Omit --remote for local-only coordination.

init also installs Cocodex-managed Git hooks and adds /.cocodex/ to the repository-local .git/info/exclude. The hooks block normal direct commits, cherry-picks, rebases, ref updates, and pushes of main unless the operation comes from Cocodex itself.

Before developers join, edit .cocodex/config.json and fill in the top-level developers object. Keep the other keys that cocodex init wrote; do not replace the whole file with only the developer fragment. A typical config looks like this:

{
  "developers": {
    "alice": {
      "git_user_name": "Alice Example",
      "git_user_email": "alice@example.com"
    },
    "bob": {
      "git_user_name": "Bob Example",
      "git_user_email": "bob@example.com"
    }
  },
  "dirty_interval_s": 2.0,
  "main_branch": "main",
  "remote": "origin",
  "socket_path": ".cocodex/cocodex.sock",
  "worktree_root": ".cocodex/worktrees"
}

Use "remote": null if the repository is local-only. The keys under developers are the only names accepted by cocodex join <user_name>, so cocodex join alice requires an alice entry.

command is optional per developer; when omitted, Cocodex starts codex. For a custom Codex launch, use a JSON string array such as "command": ["codex", "--model", "gpt-5.5"].

Starting Codex Sessions

Start one daemon in a long-running terminal from the project repository:

cocodex daemon

The daemon prints an operational log in that terminal: session joins, sync requests, task starts, busy sync rejections, integration lock changes, publish events, remote sync failures, and recovery transitions.

Start each Codex session through Cocodex from that developer's tmux window:

cocodex join alice
cocodex join bob

join has the same form for first-time use and restart. The developer name comes from .cocodex/config.json; Git identity and the Codex launch command come from the matching config entry.

Each joined session gets:

  • a branch named cocodex/<name>;
  • a worktree under .cocodex/worktrees/<name>;
  • a session agent that receives Cocodex tasks;
  • a Git-ignored AGENTS.md in that worktree, unless the project already has its own AGENTS.md.

join reads the developer's Git identity from .cocodex/config.json and writes it into that worktree's per-worktree Git config, so Cocodex snapshot commits and Codex candidate commits have the right author.

Cocodex assumes join is run from the developer's own tmux pane. When TMUX_PANE is present, join automatically binds the session agent to that pane, so sync tasks and restart notices are pasted into the running Codex as ordinary user prompts.

For advanced setups, override the target explicitly:

cocodex join --tmux-target "$TMUX_PANE" alice

For normal semantic sync tasks, join must be running inside tmux so Cocodex can paste the prompt and send Enter. If no tmux target is available, Cocodex refuses task startup and restores the session snapshot for retry.

The generated AGENTS.md tells Codex that it is in a Cocodex-managed collaboration session and that normal synchronization uses only cocodex sync from inside the managed worktree.

Restarting A Session

If a developer closes their Codex window, restart with the same session name:

cocodex join alice

Cocodex reuses .cocodex/worktrees/alice and cocodex/alice. On startup, join checks for unfinished Cocodex responsibilities before normal development continues:

  • an active sync task is re-announced with its task and validation file paths;
  • a safely recoverable interrupted task is kept or moved back to fusing;
  • an interrupted task startup that never safely reached Codex is normalized after restoring the snapshot when possible;
  • a clean session that only fell behind main is reported, but not moved;
  • local unintegrated work is reported so Codex reviews it before starting unrelated work.

If a restart notice appears, handle that notice before accepting new feature work. In the normal tmux workflow, Cocodex pastes the notice into the Codex pane automatically.

What sync Does

Clean Session

If Alice has no local work and main has advanced, this catches Alice up:

cocodex sync

If Alice is already current, Cocodex reports that the session is already synced.

Dirty Session Based On Current Main

If Alice has local edits or commits and main has not advanced since Alice last synced, Cocodex publishes Alice's current worktree directly:

cocodex sync

If the worktree has uncommitted changes, Cocodex creates a snapshot commit with Alice's configured Git identity, fast-forwards local main to that commit, and best-effort syncs the remote. This path does not create a Codex fusion task, because there is no newer main work to merge with.

Dirty Session After Main Advanced

If Alice has local edits or commits and main has advanced since Alice last synced, cocodex sync starts integration only if no other session is already syncing. If another session owns the integration lock, the command fails with integration busy; Alice should retry after that session finishes. When the lock is free, Cocodex:

  1. freezes Alice's session;
  2. snapshots Alice's current work;
  3. tries a normal Git merge of latest main into Alice's snapshot;
  4. runs lightweight checks: the worktree must be clean, the candidate must contain both latest main and Alice's snapshot, and git diff --check must pass for the candidate diff;
  5. publishes the merge commit directly if those checks pass.

If Git reports a conflict, leaves an unsafe state, or the lightweight checks fail, Cocodex resets Alice's worktree to latest main, writes a task file under .cocodex/tasks/, pastes the sync prompt into Alice's Codex terminal, and sends Enter. A semantic task is accepted only after that prompt injection succeeds. If Cocodex cannot safely inject the prompt, it restores Alice's snapshot, releases the lock, rejects this sync, and leaves Alice's work available for retry. Alice's Codex then reads the task file and re-implements or semantically merges Alice's feature on top of the latest main. If the task arrives while Codex is working on another request, Codex should pause at a safe point, preserve that request's remaining intent, complete the sync task, and then resume the paused work after sync succeeds.

For each task, Codex designs and runs sufficient validation for the semantic merge. That can mean existing tests, new or updated tests, targeted scripts, or manual checks when the project has no suitable test framework. Before running sync again, Codex writes the requested validation report under .cocodex/tasks/. After it commits the final candidate and the worktree is clean, it runs the same command again:

cocodex sync

Cocodex then requires the validation report, fast-forwards local main, and best-effort syncs the configured remote if one exists. Other session worktrees are not moved or notified as part of this publish.

If the task cannot be completed safely, Codex should stop and explain the blocker in its session output. Cocodex keeps the active task in fusing, keeps the lock with that session, and rejects later sync attempts until the same session has a committed candidate and validation report.

Normal Example

Alice and Bob both start Codex through Cocodex. Alice implements feature A; Bob implements feature B. Neither branch is integrated automatically.

Alice runs:

!cocodex sync

If no one else has advanced main since Alice last synced, Cocodex publishes Alice directly. If main has advanced, Cocodex first tries a normal Git merge under the integration lock. If that merge and the lightweight checks pass, Cocodex publishes without involving Codex. Only when Git cannot merge cleanly or the checks fail does Cocodex give Alice's Codex a task; Alice's Codex applies feature A on latest main, commits, and runs:

!cocodex sync

Now feature A is the new main.

Bob later runs:

!cocodex sync

Because Alice has now advanced main, Bob receives a task based on the current main, which already includes feature A. Bob's Codex applies feature B on top of that, commits, and runs:

!cocodex sync

This gives the team a serial mainline even though the Codex sessions worked asynchronously.

If Bob runs !cocodex sync while Alice's task is still active, Cocodex rejects Bob's command with integration busy. Bob keeps working in his own worktree and runs !cocodex sync again after Alice's sync finishes.

Safety Behavior

Cocodex prefers stopping over guessing:

  • a dirty session is not integrated until its owner runs sync;
  • one session's sync never fast-forwards another session's worktree;
  • remote sync only force-pushes local main, the current session branch, and Cocodex recovery refs after local sync has succeeded;
  • only one session owns the integration lock at a time;
  • a second session's sync is rejected while another session is already syncing;
  • clean Git merges are attempted under the same lock before a Codex semantic task is created;
  • local Git hooks block ordinary direct writes and pushes to main;
  • running sync before committing a task candidate is rejected;
  • rejected active-task publishes do not create persistent blocked states; the task remains active so the same session can fix the issue and run sync again;
  • active-task publish requires the task file, snapshot ref, base ref, validation report, and candidate commit to remain intact;
  • startup cleanup creates backup refs before clearing interrupted task state;
  • remote sync failures do not block local progress; Cocodex warns and retries on the next sync;
  • interrupted active tasks are re-announced by cocodex join <name>; legacy recovery states are normalized instead of becoming the normal workflow.

Refusals And Recovery

Use cocodex status first. It shows daemon/session versions, guard status, each session state, active task, branch head, configured remote, and whether the integration lock is held. When a session has an active task, status also shows its task file, validation file, snapshot ref, base ref, and next safe action.

When a Cocodex command fails, do not immediately run Git recovery commands by hand. Keep the affected worktree as-is and follow this order:

  1. Read the refusal output. Cocodex prints a Cocodex sync refused block with the next safe action.
  2. Run cocodex status from the project repository to identify the affected session, state, active task, lock owner, and version mismatch if any.
  3. If the session has an active task, read the task file and validation path shown by status or the refusal output.
  4. Let the same developer session continue whenever possible. Most refusals are fixed by committing the candidate, cleaning the worktree, adding validation, restarting cocodex join <name>, or retrying after the current lock owner finishes.

Common cases:

  • integration busy: do nothing to the worktree; retry cocodex sync from the same worktree after the current lock owner finishes. If the message says the owner is disconnected, ask that developer to restart with cocodex join <name> and then run cocodex sync from their managed worktree.
  • Active task refused because the candidate is missing, dirty, lacks a validation report, or is missing its task/snapshot/base handles: the task remains fusing; the same Codex session fixes the issue and runs cocodex sync again.
  • Main worktree dirty or unsafe: clean the project repository's main worktree, then retry cocodex sync from the managed session worktree. Cocodex has not moved main or discarded the session work.
  • version mismatch: restart that developer's cocodex join <name> after the installed Cocodex package has been upgraded.
  • Remote sync warning: local publish already completed; fix network or Git authentication later and let a later cocodex sync retry.
  • Cocodex protects main: the hook blocked direct main work. Continue in a managed worktree and publish through cocodex sync.

If a developer's Codex window was closed during an active task, restart it with:

cocodex join alice

Cocodex re-announces the active task and keeps the lock with Alice until Alice publishes through cocodex sync. If legacy state from an older Cocodex version is present, cocodex sync and cocodex join <name> normalize what is safe to normalize and otherwise print the owning session and next action.

Keep the project repository's main worktree clean during normal operation. Developer edits belong in .cocodex/worktrees/<name>. Uncommitted files in the main worktree can block Cocodex from fast-forwarding local main.

Command Reference

Normal developer command:

cocodex sync

Common operator commands:

cocodex init --main main --remote origin
cocodex daemon
cocodex join alice
cocodex status
cocodex log

Troubleshooting

Developer 'alice' is not configured in .cocodex/config.json means the operator has not added an alice entry under developers, or the command is being run from a repository with a different Cocodex config.

cocodex sync must run inside a Git worktree or Run cocodex sync inside a managed worktree means the command was not run from .cocodex/worktrees/<name>. Start or re-enter the session with cocodex join <name>, then run !cocodex sync from that Codex session.

If Cocodex refuses a semantic task because no tmux target is available, join was probably not started from the developer's tmux pane. Restart the session from that pane with cocodex join <name>, then retry !cocodex sync. Cocodex restores the snapshot before rejecting the task, so the developer's work remains available for retry.

Remote sync warnings are non-fatal. Fix the network or Git authentication problem when convenient; Cocodex retries remote synchronization on later cocodex sync commands.

integration busy: <name> is syncing ... means another session currently owns the integration lock or is about to receive a sync task. Keep your worktree as-is and run !cocodex sync again after that sync finishes.

integration busy: <name> is disconnected while syncing ... means the lock owner was interrupted while holding an active sync task. Do not work around the lock. The owner should run cocodex join <name> from the project repository and then run cocodex sync from their managed worktree. Other developers should keep their own worktrees unchanged and retry after the owner finishes.

Cocodex protects main means a Git hook blocked a direct write or push to main. Do developer work in .cocodex/worktrees/<name> and publish with cocodex sync.

version mismatch means the daemon and a running cocodex join agent are from different Cocodex versions. Stop and restart that developer's cocodex join after upgrading the installed package.

If local main advances but the Git remote never changes, check cocodex status and .cocodex/config.json. Cocodex only pushes when remote is configured, for example "remote": "origin". A repository can have a Git origin remote while Cocodex still shows remote: none if it was initialized without --remote origin; edit the config or reinitialize intentionally before expecting remote sync.

sync already in progress (publishing) should be short-lived. If it persists, restart the daemon and run cocodex join <name> for the affected session. Startup normalization keeps active tasks in fusing when they can continue and creates backup refs before clearing incomplete task startup state. If a legacy session has no recorded baseline, Cocodex adopts a safe baseline only when Git ancestry proves the session is merely ahead of or behind main; divergent unknown-baseline sessions are refused for operator inspection.

Implementation details are documented in docs/DEV.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cocodex-0.1.8.tar.gz (82.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cocodex-0.1.8-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file cocodex-0.1.8.tar.gz.

File metadata

  • Download URL: cocodex-0.1.8.tar.gz
  • Upload date:
  • Size: 82.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cocodex-0.1.8.tar.gz
Algorithm Hash digest
SHA256 b77edf118b410e4ecd4f312ed05578224b33393277c8de13994c237234722632
MD5 d3f30dfdd496c8a5db1b469dbe66eb6c
BLAKE2b-256 656df545ab0a42f43211fe1005641951632ccddbe504d410d49162bb7beaa222

See more details on using hashes here.

Provenance

The following attestation bundles were made for cocodex-0.1.8.tar.gz:

Publisher: release.yml on ivowang/cocodex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cocodex-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: cocodex-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 45.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cocodex-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 71fc4417affb1ed576bca2a20d6362ecdda3d5630153b5dab254b136f226b9c0
MD5 a18c370d2e40659eff43ac5b14b370ea
BLAKE2b-256 84ab730da7e57efb55065f1b58f70176343655f200c9d504741a2b618f6a995a

See more details on using hashes here.

Provenance

The following attestation bundles were made for cocodex-0.1.8-py3-none-any.whl:

Publisher: release.yml on ivowang/cocodex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page