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
mainadvanced, 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.mdin that worktree, unless the project already has its ownAGENTS.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
mainis 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:
- freezes Alice's session;
- snapshots Alice's current work;
- tries a normal Git merge of latest
maininto Alice's snapshot; - runs lightweight checks: the worktree must be clean, the candidate must
contain both latest
mainand Alice's snapshot, andgit diff --checkmust pass for the candidate diff; - 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
syncnever 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
syncis 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
syncbefore committing a task candidate is rejected; - rejected active-task publishes do not create persistent
blockedstates; the task remains active so the same session can fix the issue and runsyncagain; - 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:
- Read the refusal output. Cocodex prints a
Cocodex sync refusedblock with the next safe action. - Run
cocodex statusfrom the project repository to identify the affected session, state, active task, lock owner, and version mismatch if any. - If the session has an active task, read the task file and validation path
shown by
statusor the refusal output. - 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; retrycocodex syncfrom the same worktree after the current lock owner finishes. If the message says the owner is disconnected, ask that developer to restart withcocodex join <name>and then runcocodex syncfrom 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 runscocodex syncagain. - Main worktree dirty or unsafe: clean the project repository's main worktree,
then retry
cocodex syncfrom the managed session worktree. Cocodex has not movedmainor discarded the session work. version mismatch: restart that developer'scocodex 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 syncretry. Cocodex protects main: the hook blocked directmainwork. Continue in a managed worktree and publish throughcocodex 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b77edf118b410e4ecd4f312ed05578224b33393277c8de13994c237234722632
|
|
| MD5 |
d3f30dfdd496c8a5db1b469dbe66eb6c
|
|
| BLAKE2b-256 |
656df545ab0a42f43211fe1005641951632ccddbe504d410d49162bb7beaa222
|
Provenance
The following attestation bundles were made for cocodex-0.1.8.tar.gz:
Publisher:
release.yml on ivowang/cocodex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cocodex-0.1.8.tar.gz -
Subject digest:
b77edf118b410e4ecd4f312ed05578224b33393277c8de13994c237234722632 - Sigstore transparency entry: 1437475177
- Sigstore integration time:
-
Permalink:
ivowang/cocodex@9b74116d8ebad901b06ac1531886bd08e1efeb0a -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/ivowang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9b74116d8ebad901b06ac1531886bd08e1efeb0a -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71fc4417affb1ed576bca2a20d6362ecdda3d5630153b5dab254b136f226b9c0
|
|
| MD5 |
a18c370d2e40659eff43ac5b14b370ea
|
|
| BLAKE2b-256 |
84ab730da7e57efb55065f1b58f70176343655f200c9d504741a2b618f6a995a
|
Provenance
The following attestation bundles were made for cocodex-0.1.8-py3-none-any.whl:
Publisher:
release.yml on ivowang/cocodex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cocodex-0.1.8-py3-none-any.whl -
Subject digest:
71fc4417affb1ed576bca2a20d6362ecdda3d5630153b5dab254b136f226b9c0 - Sigstore transparency entry: 1437475255
- Sigstore integration time:
-
Permalink:
ivowang/cocodex@9b74116d8ebad901b06ac1531886bd08e1efeb0a -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/ivowang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9b74116d8ebad901b06ac1531886bd08e1efeb0a -
Trigger Event:
push
-
Statement type: