paired-session
Skill3 starsChanged 6 months agoApache-2.0View on GitHub
Use in exactly four cases - the user explicitly asks for paired-session, the review-loop entry hands off because .review-loop/config.md sets `entry: paired-session`, the review-loop entry hands off because that key is absent or invalid (the default entry), or the review-pr skill hands off on its paired route; with lifecycle on, this is the plan, implementation, independent-review, finish, polish, docs, security and acceptance workflow. Generic review-loop requests go to the review-loop entry first, which routes them here.
In this repository10 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- guideSkill
- review-loopSkill
- review-prSkill
- code-quality-loopSkill
- guideSkill
- paired-sessionSkill
- reorganizeSkill
- review-loopSkill
- review-prSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 62
Run directory: default to <repo>/.review-loop/runs/<UUID>/, where <repo> is the main checkout resolved by git rev-parse …
- Distinctive: line 74
Read a launcher log's last lines only when a polled command ends without its final coordinator line (the shared contract's Work item …
- Very specific: line 165
Add the review-only and review-pr options below to this block as to the POSIX one.
- Very specific: line 176
Add --auto-commit false too when the review-loop entry handed it over.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Paired-session workflow (Codex)
Loading the shared entry contract
First, before any stage A check or question, read <support-root>/docs/protocol/loading.md
and load the shared entry contract (docs/protocol/paired-session-entry.md) as
its own command, cwd in the user's workspace. The bundle must stay out of the
product worktree (FIELD-18): first print a fresh bundle directory as its own
command,
python3 -c 'import os, tempfile, uuid; print(os.path.join(os.path.realpath(tempfile.gettempdir()), f"review-loop-protocol-{os.getuid()}", uuid.uuid4().hex[:12]))',
then write that printed directory out literally as <bundle-dir>:
python3 <support-root>/scripts/read_protocol.py --runtime codex --stage entry-paired-session --output <bundle-dir>/protocol-codex-entry-paired-session.mdOn native Windows Codex runs commands in PowerShell, Python is python (or py -3) and os.getuid does not exist:
print the bundle directory with
python -X utf8 -c "import getpass, os, re, tempfile, uuid; print(os.path.join(os.path.realpath(tempfile.gettempdir()), 'review-loop-protocol-' + (re.sub(r'[^A-Za-z0-9._-]', '_', os.environ.get('USERNAME') or getpass.getuser()) or 'user'), uuid.uuid4().hex[:12]).replace(os.sep, '/'))"
(the loader names the directory after the Windows user name), then run
python -X utf8 '<support-root>\scripts\read_protocol.py' --runtime codex --stage entry-paired-session --output '<bundle-dir>/protocol-codex-entry-paired-session.md'.
The Windows commands in this skill are best effort; the Claude Code entry is the tested Windows path.
Resolve <support-root> to this plugin, not the task workspace. After exit 0,
read the complete output file in bounded chunks and follow it; a missing,
unreadable or incompletely read file is a failed stage A check, reported as
stage A failure: cannot load the shared entry contract (<reason>). After
compaction, reload it the same way when no coordinator command is running;
while one is running, read <support-root>/docs/protocol/paired-session-entry.md
directly (the stage is that whole file), which writes nothing to the workspace.
This file adds only the Codex host rules:
- Legacy pointer: none (the legacy workflow was removed in v2.13.0). Long-command execution failure: a declined or ungranted outside-sandbox approval: HOLD with the reason, on every entry and invocation (nothing falls back).
Outside-sandbox and long commands (critical)
- Outside-sandbox execution: the coordinator spawns provider CLIs and writes to
the selected
$CODEX_HOME; execute each invocation below with the host's full filesystem/network permission, outside the current Codex sandbox, and never inside the sandbox. - Long-command form: every invocation that can dispatch model turns
(
run,resume,reject --expect) starts with a short nonblocking yield (about 1 second); then poll its returned command-session id until it completes. Do not cancel it or launch a duplicate while it is active, except for the lifecycle-mode backstop, which ends the polled command session. - Detached commands: with
--detach, wait until the printed pid has exited (the printed record path then reads "exited" with the exit code), then read the last line of its log andstatus --brief. A report run ends in REPORTED; end the detached command only withstop(paired_session/docs/detach.md).
Run directory
- Run directory: default to
<repo>/.review-loop/runs/<UUID>/, where<repo>is the main checkout resolved bygit rev-parse --path-format=absolute --git-common-dirand its parent when it ends in/.git. A lane's output therefore lives in the main checkout. SetWORKSPACEin the same Bash call that runsproject_paths.py. On native Windows PowerShell, set$Workspacein the same call that runsproject_paths.py.scripts/project_paths.pyresolves this root and creates.review-loop/.gitignore(*,!config.md) only when missing. Not a Git repo, a bare repo or a submodule/separate-git-dir checkout: report fallback to${XDG_STATE_HOME:-$HOME/.local/state}/review-loop(relative XDG uses$HOME/.local/state). An explicit operator-chosen run dir replaces the RUN_DIR assignment below; existing runs resume at their original--run-dir, without migration. The plugin does not createCODEX_HOME. Write launcher logs under the root'slogs/. Persistent output is gitignored; scratch stays outside the repo. Do not rungit clean -xon the workspace while a run is active: it would delete the run. Read a launcher log's last lines only when a polled command ends without its final coordinator line (the shared contract's Work item section).
Run invocation
There is no probe step: permission-probe and strict mode were removed in v3.7.0 (--strict only prints a NOTE).
The invocation below sets up and starts the run.
Invocation (setup, then run):
set -eu
export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
stage_a() { echo "stage A failure: $1"; exit 3; }
test -d "$CODEX_HOME" || stage_a "CODEX_HOME $CODEX_HOME is not an existing directory"
VERSION="$(codex plugin list --json --marketplace review-loop-marketplace | python3 -c 'import json,sys; d=json.load(sys.stdin); rows=[p for p in d.get("installed",[]) if p.get("pluginId")=="review-loop@review-loop-marketplace" and p.get("enabled")]; assert len(rows)==1, "review-loop plugin must be installed and enabled exactly once"; print(rows[0]["version"])')" || stage_a "review-loop plugin is not installed and enabled exactly once"
python3 -c 'import re,sys; v=sys.argv[1]; m=re.fullmatch(r"(\d+)\.(\d+)\.(\d+)(\.\d+)*", v); msg=None if m and tuple(map(int, m.groups()[:3])) >= (2, 10, 0) else ("installed review-loop %s predates the paired-session lifecycle (2.10.0)" % v if m else "cannot parse installed review-loop version %r" % v); msg and print("stage A failure: " + msg); sys.exit(3 if msg else 0)' "$VERSION" || exit 3
echo "VERSION=$VERSION"
PLUGIN_ROOT="$CODEX_HOME/plugins/cache/review-loop-marketplace/review-loop/$VERSION"
test -x "$PLUGIN_ROOT/bin/paired-session" || stage_a 'paired-session executable missing from installed plugin'
WORKSPACE='/absolute/path/to/the-selected-dedicated-worktree'
WORKSPACE="$(cd "$WORKSPACE" && pwd -P)" || stage_a 'selected workspace does not exist'
test "$(git -C "$WORKSPACE" rev-parse --show-toplevel)" = "$WORKSPACE" || stage_a 'selected workspace is not a Git worktree root'
TASK_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
RUN_ROOT="$(python3 "$PLUGIN_ROOT/scripts/project_paths.py" "$WORKSPACE")" || stage_a 'cannot resolve the run root'
RUN_DIR="$RUN_ROOT/runs/$TASK_ID"
WORKITEM="$RUN_DIR/WORKITEM.md"
mkdir -p "$RUN_DIR" || stage_a 'cannot create the run directory'
cat > "$WORKITEM" <<'PAIRED_SESSION_WORKITEM' || stage_a 'cannot write the work item'
[Write the user's approved goal, acceptance criteria, scope, and verification here.]
PAIRED_SESSION_WORKITEM
echo "RUN_DIR=$RUN_DIR"
TEST_COMMAND='[verified project command, or the exact value from the profile]'
"$PLUGIN_ROOT/bin/paired-session" run \
--workspace "$WORKSPACE" --workitem "$WORKITEM" --run-dir "$RUN_DIR" \
--test-command "$TEST_COMMAND" --lifecycle-mode onExit 3 (a line printed stage A failure: …) is a failed stage A check,
including the plugin-version floor (P14); report it per the entry rules, even
though RUN_DIR/state.json does not exist. Any
other non-zero exit from the coordinator is a refusal or HOLD: report its
output verbatim (no fallback).
Replace the placeholders before running. Keep the heredoc delimiter unique and quoted.
Native Windows
Native Windows (PowerShell; best effort, the Claude Code entry is the tested Windows path): the coordinator
refuses --detach. Git for
Windows (Git Bash) must be installed, and the run root and the workspace stay out of OneDrive-synced folders. The
invocation becomes (same placeholders, same exit 3 rule; run the coordinator and every script through
python, never by shebang):
$ErrorActionPreference = 'Stop'
function Stage-A($Reason) { Write-Output "stage A failure: $Reason"; exit 3 }
$CodexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' }
$env:CODEX_HOME = $CodexHome
if (-not (Test-Path -LiteralPath $CodexHome -PathType Container)) { Stage-A "CODEX_HOME $CodexHome is not an existing directory" }
try { $Rows = @((codex plugin list --json --marketplace review-loop-marketplace | ConvertFrom-Json).installed | Where-Object { $_.pluginId -eq 'review-loop@review-loop-marketplace' -and $_.enabled }) } catch { $Rows = @() }
if ($Rows.Count -ne 1) { Stage-A 'review-loop plugin is not installed and enabled exactly once' }
$Version = [string]$Rows[0].version
try { $Parsed = [version]$Version } catch { Stage-A "cannot parse installed review-loop version '$Version'" }
if ($Parsed -lt [version]'3.3.0') { Stage-A "installed review-loop $Version predates native Windows support (3.3.0)" }
Write-Output "VERSION=$Version"
$PluginRoot = Join-Path $CodexHome "plugins\cache\review-loop-marketplace\review-loop\$Version"
$Launcher = Join-Path $PluginRoot 'bin\paired-session'
if (-not (Test-Path -LiteralPath $Launcher -PathType Leaf)) { Stage-A 'paired-session launcher missing from installed plugin' }
$Workspace = 'C:\absolute\path\to\the-selected-dedicated-worktree'
try { $Workspace = (Resolve-Path -LiteralPath $Workspace).ProviderPath } catch { Stage-A 'selected workspace does not exist' }
$Top = git -C $Workspace rev-parse --show-toplevel
if ($LASTEXITCODE -ne 0 -or [IO.Path]::GetFullPath($Top) -ne [IO.Path]::GetFullPath($Workspace)) { Stage-A 'selected workspace is not a Git worktree root' }
if (-not $RunDir) {
$RunRoot = python -X utf8 (Join-Path $PluginRoot 'scripts\project_paths.py') $Workspace
if ($LASTEXITCODE -ne 0) { Stage-A 'cannot resolve the run root' }
$RunDir = Join-Path $RunRoot ('runs\' + [guid]::NewGuid())
}
$WorkItem = Join-Path $RunDir 'WORKITEM.md'
try { New-Item -ItemType Directory -Force -Path $RunDir | Out-Null } catch { Stage-A 'cannot create the run directory' }
$WorkItemText = @'
[Write the user's approved goal, acceptance criteria, scope, and verification here.]
'@
try { [IO.File]::WriteAllText($WorkItem, $WorkItemText.Replace("`r`n", "`n") + "`n", (New-Object Text.UTF8Encoding $false)) } catch { Stage-A 'cannot write the work item' }
Write-Output "RUN_DIR=$RunDir"
$TestCommand = '[verified project command, or the exact value from the profile]'
python -X utf8 $Launcher run --workspace $Workspace --workitem $WorkItem --run-dir $RunDir --test-command $TestCommand --lifecycle-mode on
exit $LASTEXITCODEAdd the review-only and review-pr options below to this block as to the POSIX one; a second quoted
here-string writes pr-pins.json the same way as the work item. Windows PowerShell 5.1 does not pass a " inside an
argument intact to python: keep " out of the test command and the option values, or use PowerShell 7.3 or later.
Per-route extra arguments
A review-only request (the user asks to review an existing change, or the review-loop handoff of
a code target) follows the shared contract's review-only entry: add --review-only (and
--base "$BASE" only when the user named a base) to the invocation.
The workspace is the worktree that holds the change (normally the current checkout). Do not
create or switch to a dedicated worktree, and skip the Stage A dedicated-worktree question.
Add --auto-commit false too when the review-loop entry handed it over.
A review-pr handoff (from the Codex review-pr skill, or the user asks paired-session to review a PR or
ref) follows the shared contract's Review-PR entry.
Resolve the run root from the operator project before switching WORKSPACE to the clone.
Host rules: the run root is the resolved <repo>/.review-loop/ (or the reported state-root fallback) (the materializer's --root); the
materializer reads through gh and the network, so run it as its own invocation outside the sandbox,
with PLUGIN_ROOT resolved as in the invocation, before the invocation. In the
invocation set WORKSPACE to the clone it printed (no input: the current worktree) and
replace the RUN_ROOT resolver (PowerShell: $RunRoot) with the absolute operator-project
root resolved before materialization, so runs stay beside pr/ rather than inside the clone. Skip the
dedicated-worktree question, replace --test-command "$TEST_COMMAND" with --no-test-command unless
the user confirmed a test command, and add --review-only --review-report --auto-commit false,
--base "$BASE" (the printed merge_base; no input: only a base the user named),
--review-pr-pins "$RUN_DIR/pr-pins.json" for a materialized input (the invocation writes the
printed JSON there with a second quoted heredoc, like the work item) and --aspects "$ASPECTS" when
aspects were given. simplify pointer: run /review-loop:code-quality-loop on the change (its POLISH-Q simplifier) (Claude Code).
When this skill is the entry
This skill is the explicit
paired-session entry and the review-loop handoff target only when the config
key entry is paired-session or absent and the work is fresh, an existing plan (as the work item) or a review-only
code target (an invalid value counts as absent, with a warning); entry: legacy is refused by the entry, since the
legacy workflow was removed in v2.13.0.
Operator commands
Operator commands (accept, reject, withdraw-findings) follow the shared
entry contract. Run withdraw-findings only on the user's explicit decision
naming the findings and a nonempty reason, never on the host's own judgment or
in handsfree mode. Issue --intent-only --finding ID --reason TEXT first
(repeat --finding for each named id), show the digest, then submit the same
command with --expect <digest> instead of --intent-only. Relay the ruling,
evidence, next-step hint and status; withdrawal dispatches no model turn.
Lifecycle mode
--lifecycle-mode off is the supported stop-after-gate route: PLAN -> EXEC -> GATE -> DONE (acceptance pending). The operator then handles FINISH, POLISH-Q, docs, security review and merging. Accept never commits on this route: it hands back the uncommitted tree. New runs default to on (the full lifecycle); CLI, operator/workspace profiles and Python entry points may select off, and saved off runs resume, accept, reject, note and abort normally.
For review-pr report mode, always pass --lifecycle-mode on to run. This overrides a profile's off value. For other entries, pass --lifecycle-mode on only when no operator/workspace profile sets lifecycle_mode. When a profile sets it, omit the flag (or pass the profile's value). The CLI value overrides the profile. When the user chooses off for those entries, pass --lifecycle-mode off. Compare the saved lifecycle mode with that selected mode for the backstop; saved off runs continue with off.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

