Context management and session jumps (open-science-context)¶
Codex shares the same context files and handoff skills as Claude Code; its session controls differ and are described in Codex session controls.
An agent can hold only a limited amount of conversation. On long work it would otherwise
slow down or lose track. With the open-science-context plugin, the agent saves where the
work stands in the project's context files, clears its own conversation, and carries on from
those files. This is called a jump.
There are two kinds. In an active jump, the context is over 250k tokens (or a piece of work is finished), so the agent saves its state to the task's context file and the plugin clears the session and resumes it from that file. In a wait jump, the agent submits a SLURM job, saves its state and clears; the idle session is woken when the job leaves the queue and resumes from the context file. Clearing before a long wait matters because the prompt cache expires while the session sits idle: waking a session that still holds a long conversation would resend all of it uncached, which costs far more than a fresh start from the context file. The cache-expiry explanation describes Claude Code; Codex does not use the Claude cache-cold timer or its fixed context thresholds.
On Claude Code, the jumps are done by a Claude Code mod. A mod is code that Claude Code
runs inside itself (see Claude Code's mods documentation).
The plugin's mod clears the session with Claude Code's own /clear, runs the resume
command, wakes a waiting session when its SLURM jobs end, sends the cache-cold notice, names
the session after its task, and reads the context size of the session and of each subagent.
It also shows the context size and how long the prompt cache stays warm, and asks you before
a prompt you send to a cold cache (The context bar).
Nothing is typed into the terminal, and tmux is not needed. The mod needs Claude Code 2.1.287
or later, and mods must be switched on for your account (Anthropic is switching them on step
by step; /open-science:onboard checks). Where the mod is not loaded, the plugin falls back
to typing into the agent's tmux pane, and you will see
the agent type into its own pane. jump.sh status says which one a session has.
Jumps are optional. Without them, the plugin still registers each session to its context
file, the agents still keep the context files current, and any session can take over a task
with open-science-context:continue-context. See Choosing which jumps.
claude plugin install open-science-context@open-science
Installing it also installs open-science-project: a jump is only as good as the context
file it resumes from. It needs jq and opsci. With the mod, tmux is optional; it is still
useful for keeping sessions alive after you disconnect, and SLURM resurrection
needs it. Working in tmux shows how to arrange your work (one pane per task), how
to set tmux up for the mouse, and how to run it on a compute node of a cluster. Without the
mod, and for Codex's jumps (Codex session controls), tmux is
required.
| skill | use it to |
|---|---|
open-science-context:context-management |
the rules for jumps, registration and subagent checkpoints; main agents load it at session start |
open-science-context:continue-context |
take over the work a context file describes; the first step after every jump |
open-science-context:advise-with-context |
ask questions about a context file or plan without acting on it |
Registration¶
Each session records which context file it drives, so that
open-science-context:continue-context finds the right file after a jump with no file named.
When the main agent starts driving a task it runs:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/pane_context.sh" set tasks/<id>/context.md
Codex does not set ${CLAUDE_PLUGIN_ROOT}; it runs the same script from the installed
plugin's scripts/ directory. Outside tmux, Codex registers the file for its session
(CODEX_THREAD_ID) instead of the pane.
pane_context.sh get prints the registered path, and pane_context.sh clear drops it. The
registration follows the session through a jump, a plain /clear and a SLURM resurrection.
With the mod it is kept by session id: the mod hands it to the new session at every clear,
and a session resumed with claude --resume keeps its id. Without the mod it is kept by tmux
pane, and a new session in the same pane keeps it. Subagents never register; they use the
main agent's registration.
Session names. If you said yes in onboarding (OPSCI_SESSION_NAMES=1), the session is
named after the project and, once registered, the task's short name, for example
quad-ratio-2 · pp-real. This is also its name in the Remote Control list. With the mod the
name changes as soon as the turn that registered the task ends; without it, at your next
prompt.
To take over work in a pane yourself, type /open-science-context:continue-context, with or
without a file. With no file it uses the pane's registered file and prints which one; with
nothing registered it lists candidates, newest first, and asks you. Typing it in the wrong
pane resumes the wrong work, so check the line that names the file. In Codex, ask it to use
open-science-context:continue-context, with or without a file.
Jumps¶
| jump | when | command |
|---|---|---|
| active | the context is above about 250k tokens, a subtask finished, or before a fan-out of subagents | bash "${CLAUDE_PLUGIN_ROOT}/scripts/jump.sh" active <context file> --report "<report>" |
| wait | only background work is left (subagents, a background shell, a SLURM job) and it will take longer than about 45 minutes | bash "${CLAUDE_PLUGIN_ROOT}/scripts/jump.sh" wait <context file> --report "<report>" |
| cache-cold | the plugin sends [open-science] cache-cold: ... to the session after 58 minutes idle with work still running |
a wait jump, now |
Before either command, in the same turn, the agent writes the jump's record: the task
context.md (state, what is in flight and how to check it, the exact next step), the project
context.md if it changed, one line in the task log, and opsci notify for anything you
would otherwise miss. The jump.sh call is the last action of the turn.
Every jump also reports to you. A jump clears the conversation, so in the session you see
only /clear; jump.sh therefore requires --report and posts it with opsci notify
--kind status --no-mention (to the project's Feed in Notion, or to your configured back
end) before it requests the jump. The report gives a headline, what the session did, the
state, what is running and what comes next, followed by the kind of jump and the context
file. The agent does not wait for your approval. If the send fails, the jump still happens,
and the message is kept in messages/.
With the mod, the report is also shown at the top of the cleared session, right after
/clear, as the output of the mod's /opsci-note command, so the session shows what the
previous session said instead of only /clear, in the terminal and in Remote Control. After a
wait jump it begins by saying that the session was cleared and is waiting, and for how many
running tasks. Showing it starts no turn, so a session left waiting stays asleep. The agent of
the new session reads it with its next prompt, as it reads any command's output; the context
file stays the record it resumes from. Typing /opsci-note shows the last note again.
When the turn ends, the mod runs Claude Code's /clear, checks that a new session started,
hands the registration to it, and, for an active jump, runs
/open-science-context:continue-context <context file>. In our tests the resumed turn
started about half a second after the old turn ended.
A cleared session is woken by a background subagent's report, a background Bash task
exiting, a Monitor event, or a queued SLURM waker. For SLURM jobs the agent queues a waker
before a wait jump: bash "${CLAUDE_PLUGIN_ROOT}/scripts/wait_slurm.sh" --notify <jobid> [<jobid>...].
The mod checks the queue every 60 seconds (OPSCI_WAIT_POLL); when the jobs have left it,
it sends their final states to the session, which starts a turn. The waker follows the
session through jumps, and because it is kept by session id it also survives a SLURM
resurrection. A session that was waiting on background tasks when Claude Code restarted (a
resurrection, or a resume by hand) has lost them; the mod then runs
/open-science-context:continue-context for it at once.
When the agent does not jump¶
- While it waits for you. If the turn ends with a question, a decision or a hold point, the agent saves the state, asks, and ends the turn, whatever the context size. It jumps after you answer, if work is left.
- As a subagent. Subagents never jump.
- While another component owns the pane, for example SLURM resurrection during its wind-down.
What jump.sh refuses¶
| refusal | why |
|---|---|
no --report text |
a jump the user is not told about leaves them only /clear |
OPSCI_JUMPS is off, or wait for an active jump |
you switched these jumps off |
| not in tmux, without the mod | the fallback types into the pane |
| the context file is missing, or was not saved in the last 15 minutes | the state to resume from was not written |
| the session's state file cannot be found, without the mod | the clear could not be confirmed |
an active jump below 100k tokens without --force |
a jump costs a full reload; small contexts do not need one |
| an inhibit file exists | another component owns the pane |
| at the stop: a wait jump with nothing running that could wake the session | the session would never wake; start the waker or do an active jump |
jump.sh cancel drops a pending request; jump.sh status shows it.
At every stop¶
At every stop of a main session that has registered a task, the plugin:
- starts a pending jump, or refuses a wait jump that nothing would wake;
- arms the cache-cold timer if something will wake the session (a running background task, a scheduled prompt or a queued waker), and stops it otherwise;
- if the context is above the threshold (250k tokens) and no jump is pending, blocks the stop once with an instruction to do an active jump. It blocks the same session again only after its context has grown by another 50k tokens, so a session waiting for you is not stopped at every reply.
The mod reads the context size from Claude Code and runs these steps through the plugin's
Stop policy (cm_stop.sh --mod); without the mod, the Stop hook runs them itself and does
nothing outside tmux. With OPSCI_JUMPS=wait step 3 is skipped; with OPSCI_JUMPS=off
only a leftover timer is stopped.
Choosing which jumps¶
Onboarding asks which jumps you want and records the answer as OPSCI_JUMPS in the env
block of the Claude settings.json ($CLAUDE_CONFIG_DIR/settings.json or
~/.claude/settings.json). Edit it there to change it; new sessions pick it up. Codex
reads OPSCI_JUMPS from the environment it is started in, for example
OPSCI_JUMPS=wait codex --add-dir ~/.local/state/open-science/inbox.
OPSCI_JUMPS |
what happens |
|---|---|
all (the default, recommended) |
active, wait and cache-cold jumps |
wait |
no active jumps and no size notice from the Stop hook: the conversation grows as long as it needs to. Wait jumps and the cache-cold notice still clear the session before a long wait, when resending the conversation uncached would cost the most |
off |
no jumps: jump.sh refuses every jump, and nothing is done at a stop but stopping a leftover cache-cold timer |
Jumps are recommended: they reduce usage and keep the agent working from the current state
of the work. Newer models cost less and work well with a long conversation, so keeping the
conversation (wait or off) is a reasonable choice. With every setting, registration,
the context files, continue-context and subagent checkpoints work as described on this page.
The context bar and the cold-cache question¶
With the mod, every session shows a line with its context size and the state of the prompt
cache, above the prompt on the terminal and in the Code tab of the Claude Desktop app. The context size is the one the main agent's last model response left (what
it read plus what it wrote), updated at the end of every request, in thousands with one decimal
(123.4k) and as a plain count below 1000. The cache lives one hour from the start of the last
request that read it; the line's clock starts at the end of the main agent's last request (a
subagent's requests do not count), so it calls the cache cold at 59 minutes:
| idle since the last request | the line says |
|---|---|
| no request yet in this session (a new or cleared session) | context 12.3k tokens · no cache yet |
| under 59 minutes | context 180.2k tokens · cache warm, 32 min left, in green |
| 59 minutes or more | ⚠ context 180.2k tokens · cache cold (75 min idle), in yellow |
Remote Control (from claude.ai, the desktop app or the mobile app) does not show the line:
Claude Code draws a mod's interface only in the terminal and the Desktop app's Code tab. When
the cache of an idle session goes cold, the mod therefore also adds one row to the
conversation, which Remote Control shows too: the output of /opsci-note, Cache cold: 59 min
since the last request. The next prompt reads the whole context (180.2k tokens) again at the
full price; /clear starts a fresh session. It starts no turn; the model reads the line with
your next prompt.
The 59 minutes come from Claude Code's own transcripts: in 958 transcript files, every request sent less than 60 minutes after the previous response still read the cache, and none sent later did.
When the cache is cold, a prompt you send to the idle session is held back, and the agent is not woken until you answer:
- Typed in the terminal: the prompt is not sent, and a dialog opens with the question (
⚠ The cache is cold (75 min since the last request). The whole context (180.2k tokens) will be read again at the full price. Are you sure you want to submit this prompt?) and two buttons. Submit (or1) sends it as you typed it, shown in the conversation as a message from the plugin (Claude Code labels every prompt a plugin sends that way, and no plugin can change it); Do not submit (or2, or Esc) puts it back in the prompt box. - From Remote Control: the app shows Claude Code's question with the same text and the answers Submit and Do not submit. The app adds its own free-text answers; anything but Submit leaves the prompt unsent. A typed prompt with an image attached gets the same question in the terminal.
Slash commands (/clear is the usual answer to a cold cache), task notifications and the
plugin's own prompts are not asked about.
This is separate from the cache-cold notice above. That notice is for a session that ended its turn with work still running that will wake it; after 58 minutes, a minute before the line turns cold, it tells the agent to do a wait jump while the cache is still warm. A session that ended its turn waiting only for you gets no notice and is never woken by the plugin: whether to clear or to continue is up to you, and the question above is asked when you do.
Settings¶
Environment variables read by the scripts:
| variable | default | what |
|---|---|---|
OPSCI_JUMPS |
all |
which jumps are allowed: all, wait or off (Choosing which jumps) |
OPSCI_JUMP_THRESHOLD |
250000 | context size (tokens) at which the Stop hook asks for an active jump |
OPSCI_JUMP_REPEAT |
50000 | growth (tokens) before the hook asks the same session again |
OPSCI_ACTIVE_JUMP_FLOOR |
100000 | below this, an active jump needs --force |
OPSCI_CACHE_COLD_MIN |
58 | minutes idle, with work running, before the cache-cold notice |
OPSCI_CACHE_TTL_MIN |
59 | with the mod: minutes idle after which the context bar says the cache is cold and prompts are asked about |
OPSCI_JUMP_FRESH_MIN |
15 | the context file must have been saved within this many minutes |
OPSCI_SUBAGENT_LIMIT |
200000 | with the mod: a subagent's context size (tokens) at which it is told to checkpoint |
OPSCI_WAIT_POLL |
60 | seconds between two checks of a SLURM waker's jobs |
OPSCI_STATE_DIR |
$XDG_STATE_HOME/open-science, else ~/.local/state/open-science |
where registrations, requests, wakers, timers, locks and the log cm.log are kept |
Subagents¶
The main agent dispatches subagents in the background. Every dispatch prompt states:
- the path of the subagent's working document,
tasks/<id>/subcontext/subagent_<slug>.md, which it rewrites at the end of every round as if a different agent takes over next; - above 200k tokens of its own context, it stops at the next clean boundary, updates the
document, and ends its report
PAUSED - <doc path> - <exact next step>; a finished task endsDONE; - a job whose wait is over about 45 minutes is submitted, recorded, and reported as
SUBMITTED - <job id> - <doc path> - <check command> - <next step>; the main agent owns the wait; - every report ends with
If you have no context, use the open-science-context:continue-context skill.
With the mod, the plugin also reads each subagent's context size from every request it sends
to the model. Above 200k tokens (OPSCI_SUBAGENT_LIMIT) it sends that subagent one message,
open-science: your context is <n> tokens, above 200000. Stop at the next clean boundary,
..., so the subagent does not have to estimate its own size. Without the mod, the subagent
checks its own size.
On PAUSED or SUBMITTED the main agent dispatches a fresh subagent against the same
document.
Without the mod: the tmux fallback¶
If Claude Code does not load the mod (a version before 2.1.287, mods not yet switched on for
your account, or a session started with --safe-mode), the plugin's shell hooks do the same
work by typing into the agent's tmux pane:
- Claude Code must run inside tmux. Outside tmux,
jump.shrefuses and the Stop hook does nothing. - Jumps: when the turn ends, the Stop hook starts a worker that waits for the pane to be
idle, types
/clear, confirms that the session id changed, and, for an active jump, types/open-science-context:continue-context <context file>. You will see the agent type into its own pane. The agent never types into its own pane itself. - Cache-cold notice: a timer types the notice into the pane.
- SLURM wakers:
wait_slurm.sh --notifysays the mod is not loaded; the agent runswait_slurm.sh <jobid>...as a background Bash task instead, and its exit wakes the session. - Registration is kept by pane. After a SLURM resurrection the session runs in a new pane; at its first stop the Stop hook copies the session's own record into the new pane's.
- Session names change at your next prompt, from the
UserPromptSubmithook. - Context size is read from the session's transcript, one message behind.
The plugin decides per session: when the mod loads, it sets OPSCI_MOD=1 for Claude Code
and everything it starts, and the shell hooks then leave the work to it. Nothing needs to be
configured to switch between the two.
open-science-context:advise-with-context¶
For questions about work another session is driving. It reads the context file and what the
question needs, and answers with a file:line for every claim. It does not run the plan, fix
anything, or edit the context file or the plan unless you ask. Registering the file to the
pane is the one write it makes.
Codex session controls¶
For Codex, install these plugins after adding the marketplace:
codex plugin add open-science-project@open-science
codex plugin add open-science-context@open-science
Restart Codex and review the hooks using /hooks. Start Codex from a shell in the tmux
pane, not with exec codex, since an automated jump needs that shell to launch the next
session. For ordinary handoff, a named context file works without tmux. Registration
without tmux is per Codex thread, so one session cannot inherit another's task by accident.
The state directory (default ~/.local/state/open-science, or OPSCI_STATE_DIR) holds
records that the hooks act on outside the sandbox: they end and start Codex, type into the
pane and run codex queue. Sandboxed tools must therefore not be able to write it. They
write only its inbox subdirectory, and the Codex hook checks what they leave there and
writes the records itself. When using Codex's workspace-write sandbox, make the inbox and
nothing else writable with --add-dir, and keep your other model, sandbox and approval
settings:
mkdir -p ~/.local/state/open-science/inbox
codex --add-dir ~/.local/state/open-science/inbox
or, for every start, sandbox_workspace_write.writable_roots =
["/home/<you>/.local/state/open-science/inbox"] in ~/.codex/config.toml. If you set this
up with an earlier version, which made the whole state directory writable, replace that
path with its inbox. The state directory is created private (mode 700, files 600), and
the scripts refuse a state directory owned by another user.
Do not broaden sandbox permissions merely to make a jump work. Without a writable inbox,
continue from an explicitly named context file and save changes in the project.
For SLURM recovery, the state directory must be on storage shared by the compute nodes.
The context plugin's pane_context.sh check tests write access and explains how to fix
it, and warns when the sandbox can write the whole state directory; registration or jump
requests return exit 3 when the inbox is read-only.
An active Codex jump saves the research state, ends the old TUI after its turn, and starts
Codex again in the pane shell with the original launch options and a handoff prompt.
It never types Claude /clear into Codex. Unknown launch options and managed --worktree
sessions are refused. Model and permissions are preserved; a hook's permission_mode
field is not used to infer launch permissions.
A Codex wait jump needs the plugin's detached SLURM watcher, requested with
wait_slurm.sh --notify <jobid>... before jump.sh wait <context file>. When jobs leave
the queue, it uses codex queue to wake the new thread in that pane. Ordinary background
shells and subagents are not supported as wait-jump wakers. Recheck the scheduler's final
state and outputs; leaving the queue is not proof of success.
OPSCI_JUMPS=all|wait|off retains its meaning. Codex has no cache-cold timer, no context
bar, no cold-cache question or note, no jump report at the top of the new session, and no
Claude-style automatic session naming. Token usage is best effort from Codex's changing
rollout format; if unreadable, no size notice is issued. By default a notice occurs at
60% of the reported model window; OPSCI_CODEX_JUMP_THRESHOLD sets an explicit threshold.
The active-jump floor is 100000 tokens unless OPSCI_CODEX_ACTIVE_JUMP_FLOOR overrides it.
Manual requests can use --force after saving state; it bypasses only the size floor.