Spike: send one message to another session and show the reply in a pane

An agent harness for Claude Code. Stratum runs every change through a lane sized by its impact: a main session (the orchestrator) plans with you, hands the work to st-* subagents, checks what they return, and stops at a few clear gates for your decision.
Stratum merges two workflows and adds its own layer:
The full design and the reason for each choice are in DESIGN.md.
Stratum is a Claude Code plugin and its own marketplace.
claude plugin marketplace add HyuseCS/stratum
claude plugin install stratum@stratum --scope project
Then, in a new session in your project:
/stratum:st-init
st-init creates the project's .stratum/ files and specs/, asks for the git guard modes and any model overrides, asks for the advisor mode, grills you for the project's rules (the constitution), writes the AGENTS.md and CLAUDE.md pointers, sets the statusline and asks for its theme, shape and Token Weather line, turns off a global ponytail or Token Weather plugin for this project, offers to upgrade graphify, installs its post-commit hook, and checks your tools.
| Tool | Used for |
|---|---|
git, python3 | git guard, model overrides hook, phase hook, statusline, handoff facts, scripts |
node | ponytail hooks |
puppeteer-core (npm i -g puppeteer-core), Chrome or Chromium | the Design lane's render check |
bunx (Bun) | statusline bar (falls back to markers only) |
graphify 0.9.74 or later (uv tool install graphifyy) | code navigation graph, Dart support |
gh | optional, for st-issues and st-pr |
herdr 0.9.4, jq | optional, for st-herdr-workspace and st-worker |
codex 0.163.0 or later, logged in, and GNU timeout | optional, the Codex advisor |
Describe the work. Stratum picks the lane:
/stratum:st add a parent notice when the bus is 10 minutes away
It answers with one line, for example Lane: full (privacy and access: new notice to parents). Say full, fast, or quick to override.
The lane follows the highest impact area the change touches.
| Impact area | Examples | Lane |
|---|---|---|
| Privacy and access | access rules, data model, contracts, sign-in, personal data | Full |
| New look, page or screen | a new look, or a page or screen the project does not have yet, when the screen is the whole task | Design |
| New user story or feature | anything not in a current spec | Full |
| Core runtime | background services, offline queue, workers, notices | Fast |
| Screens | layout, components, navigation | Fast (Quick for a small fix in a few files) |
| Text and docs | labels, typos, wording | Quick |
Full lane: five phases and three gates.
| # | Phase | What happens | Gate |
|---|---|---|---|
| 1 | Define | Grilling, then the spec, then clarify (if needed) and checklist | You agree the spec |
| 2 | Plan | st-plan writes the plan and tasks. Open decisions come to you one at a time. It reads docs/lessons.md and cites the lessons that apply. | Asked: make GitHub issues? |
| 3 | Inspect | st-inspect checks the files agree and the plan can be built | You OK the build |
| 4 | Build | Per task: failing test, build, check, commit. Per story: one st-review pass, design review for screens. | None unless blocked |
| 5 | Close | Fix spec drift, optional gap report, proposed lessons, shortcut list | Push only when you say "push" |
Fast lane: a short change plan in specs/<feature>/changes/, your OK, test-first build, st-review, commit, drift fix and shortcut list, commit docs.
Quick lane: one file, no gate, related tests, commit. A small fix across a few files goes to st-build in brief mode. It moves up a lane if it needs a change plan.
Design lane: a new look or a new page or screen. New look: a grill for the design file, one reference screen, its checks, your yes. Autonomous: the page in the existing look, checked, then your OK.
| Skill | What it does | ||||
|---|---|---|---|---|---|
st <task> | Main entry. Picks the lane and runs it. | ||||
st-full, st-fast, st-quick | Force a lane. | ||||
st-design <task> | Design lane: design file, reference screen or new page, checked before you see it. | ||||
st-define, st-plan, st-inspect, st-build, st-close | Run or resume one Full-lane phase. | ||||
st-review, st-test, st-debug, st-git | Run one Build-loop agent on demand. A missing feature comes from .stratum/state.json, a missing range is main..HEAD. | ||||
st-triage <issue#>... | Check GitHub issues against the code and git history, read-only: verdict, what is built, blocker, next step. | ||||
st-herdr-workspace <folder> | Open a project folder as a new herdr workspace with the full or basic layout, here or on a saved machine. | ||||
st-worker <task>... | Run tasks in worker sessions (a claude in its own herdr pane and worktree, here or on a saved machine, or headless), then review and merge their branches. | ||||
st-status | Feature, phase, lane, tasks done, git guard options, next gate, missing tools, duplicate installs, days since the last upstream sync, model overrides. | ||||
| `st-shape arrow\ | rounded\ | slanted\ | blocks\ | flat` | Set the statusline shape. |
st-theme <name> | Set the color theme of the statusline and Token Weather. | ||||
st-model <agent> <value>... | Set an agent's model or effort for this project. Plain words work too: "make st-close use opus". | ||||
st-handoff | Write the session handoff. | ||||
st-init | Set up a project. | ||||
st-template <name> | Copy a template into the project to edit it. | ||||
st-constitution | Amend the project rules. | ||||
st-issues | Turn tasks into GitHub issues. | ||||
st-pr [base] | Open a draft PR from git facts and the spec; ready when CI passes. | ||||
st-sync | Maintainers: port upstream changes into Stratum's forks. | ||||
st-grill | Grilling interview, one question at a time. | ||||
st-ui-ux, st-impeccable, st-frontend-design, st-hallmark | Design build rules, design review, visual direction, new-look engine. | ||||
st-graphify | Build or query the code graph. | ||||
st-ponytail, st-ponytail-review, -audit, -debt, -gain, -help | Minimal-code mode and its reviews. |
All skills are called as /stratum:<name>.
| Agent | Model | Job |
|---|---|---|
st-plan | Opus | Plan, research, data model, contracts, quickstart, tasks |
st-inspect | Opus | Spec, plan, and tasks agree; setup, test coverage, breaking changes, security |
st-build | Opus | Builds tasks or a brief; loads design rules for screens |
st-test | Sonnet | Writes each test and shows it fails first |
st-review | Opus | Reviews each finished story |
st-debug | Opus | Takes over after 2 failed tries |
st-design | Opus | Design-lane build: one screen, its component tests and the engine's checks |
st-fast | Sonnet | Fast-lane change plans |
st-quick | Sonnet | Quick-lane edits (one file) |
st-close | Sonnet | Drift fixes, gap report, lessons, shortcut list |
st-git | Sonnet | Commits each finished task, exact paths only |
st-impeccable | Opus | Background impeccable audit, read-only |
st-ponytail-audit | Opus | Background ponytail audit, read-only |
st-ponytail-debt | Sonnet | Background ponytail debt ledger, read-only |
st-triage | Opus | Checks a GitHub issue against the code and git history, read-only |
The Model column is the plugin default. st-init's template runs st-build, st-debug, st-quick, st-plan, and st-fast on Opus with effort high, and st-close, st-git, and st-test on Haiku with effort xhigh. See Model overrides under Built in.
Every agent reads the SR-OPUS-5 communication contract first and finds files graph first, then search, then read. Code-writing agents carry the ponytail rule and add no explanatory comments.
.stratum/
├── constitution.md project rules (committed)
├── state.json current feature, phase, lane (committed)
├── advisor.json advisor mode (codex, both, off), optional Codex model and effort (committed)
├── templates/ optional overrides of plugin templates (committed)
├── handoff.md session handoff (git-ignored)
├── powerline.json statusline theme, shape, colors (git-ignored, per machine)
├── git-guard.json git guard modes for this repo (git-ignored, per machine)
├── models.json agent model and effort overrides (git-ignored, per machine)
└── weather.json Token Weather growth and compact point (git-ignored)
specs/NNN-feature/ spec, plan, research, data model, contracts, quickstart, tasks, checklists/, changes/, advisor.md
AGENTS.md, CLAUDE.md point every tool at the constitution
Templates and scripts stay in the plugin. A file in .stratum/templates/ overrides the plugin's copy for that project only.
codex CLI), a model from another vendor, runs at two points in the Full lane. At the end of the Plan phase, before its gate, it only advises on a simpler or better approach and does not validate the plan (st-inspect does that). It also reviews a test still red after two tries. The full diff before the build is called done and the whole Fast lane use /advisor if you set one. The Quick lane has no advisor. The orchestrator runs scripts/st-advisor.sh in the foreground and waits for the answer. Codex runs in the read-only sandbox and only reviews: it never builds or fixes. It answers APPROVED, REVISE (findings, checked against the source like any finding, then one more run, two rounds at most), or BLOCKED (the orchestrator stops and asks you). A failed, empty, malformed or late answer (540 seconds) is BLOCKED, never approval. Each run appends the point, a SHA256 of the input, the verdict and the findings to specs/<feature>/advisor.md. An approval of a plan that changed since then is stale. What leaves the machine: each call sends the prompt and the files Codex reads to OpenAI, on your Codex login and quota (about 37,000 tokens per plan review). At the plan point the prompt names the plan files and Codex reads them; at the test point it holds the task text, the test path and both failure outputs. The prompt tells Codex to stay inside the named inputs, never open env or secret files, and never quote a secret value. The sandbox does not block reads. Whether Codex's own shell sees your environment variables is not checked. .stratum/advisor.json sets the mode: {"mode": "codex"}, {"mode": "both"} (Codex, then /advisor if you set one), or {"mode": "off"}. "mode": "off" is the off switch: nothing goes to OpenAI. With no file the advisor is off and prints no advisor: mode not set. Write .stratum/advisor.json with "mode": "codex", "both" or "off". st-init writes the file for new projects. A project set up before this feature has no file, so the advisor stays off until you write it. With no codex on PATH you get one line, no advisor: codex not found, and the flow runs as before, with /advisor if you set one. Optional model and effort keys in the same file pick the Codex model and reasoning effort, for example {"mode": "codex", "model": "gpt-6-astra", "effort": "high"}. Without them Codex uses its own default. The call runs with --ignore-user-config: neither ~/.codex/config.toml nor a project .codex/config.toml is read, so no MCP server, hook or notify command from them runs during a review, and the model and effort come only from this file. Your Codex login works as before.commit, worktree_remove, worktree_prune, branch_delete, reset_hard, clean (clean -f), discard (checkout ., restore .), and force_push. Each is auto (runs), ask, or deny (blocked). The guard reads the repo's .stratum/git-guard.json first (st-init writes it), then the plugin option of the same name from /config or /plugin config, which is global to the machine. The default is ask. A commit that asks or is blocked shows a staged-file summary. Push, rebase, and commit --amend always ask. git config writes, git add -A, git add ., and --no-verify are always blocked. Remote branch delete is blocked: you delete remote branches yourself. commit: auto runs only in this checkout. A commit asks when cd, pushd or -C points at another checkout, or with --git-dir, --work-tree, or a GIT_DIR= or GIT_WORK_TREE= prefix. merge, pull, cherry-pick, revert and am there ask too. update-ref, branch -f, -M or -C, and a fetch or pull with a <src>:<dst> refspec always ask: they move a branch with no commit. -c core.hooksPath is blocked like --no-verify, and -c alias.* asks. A command the guard cannot parse asks. Known gap: an env, export, sudo, nice, nohup or timeout wrapper, or a GIT_CONFIG_* override, is not checked.st-worker writes the worker's guard file with commit: auto and its branch. Commit and the merge family run without a question only while that branch is checked out. st-worker.sh close deletes the file, and refuses while the branch has an open PR.st-pr and stays open until it is merged. Nothing merges before you test it. An Approve at the st-pr gate counts as "push". A worker whose branch goes to main opens its own PR..stratum/models.json maps an agent name to an optional model and effort, for example { "st-close": { "model": "haiku", "effort": "xhigh" } }. Agents: st-build, st-close, st-debug, st-design, st-fast, st-git, st-impeccable, st-inspect, st-plan, st-ponytail-audit, st-ponytail-debt, st-quick, st-review, st-test, st-triage. Models: sonnet, opus, haiku, fable. Efforts: low, medium, high, xhigh, max. st-init writes it from templates/models.json: st-build, st-debug, st-quick, st-plan, st-fast on opus with effort high, and st-close, st-git, st-test on haiku with effort xhigh. st-inspect and st-review have no entry and keep the plugin default. st-design, st-impeccable, st-ponytail-audit, st-ponytail-debt and st-triage have no entry either. A hook applies the file each time a Stratum agent starts, so the next start picks up an edit. A bad file blocks every Stratum agent start with a message that names the problem. Only Stratum agents are affected. CLAUDE_CODE_EFFORT_LEVEL beats the file's effort. The per-call effort needs Claude Code 2.1.292 or later. On an older client, remove the effort keys from the file. A linked worktree has no models.json: it is git-ignored and scripts/st-worktree.sh does not link it, so agents there use the plugin defaults, as with git-guard.json. To change an entry, run /stratum:st-model st-close opus, say it in plain words ("make st-close use opus"), or edit the file.st-handoff writes the goal, decisions, open questions, and next step. Mid-session it runs in a background fork that has the whole conversation, so you keep talking. When you say you are done, it runs in the main session. A hook adds a facts block (branch, phase, tasks done, last commits, uncommitted files) at session end and before compaction. The next session starts by reading it.phase and lane in .stratum/state.json when a phase skill, st-fast, st-quick or st-full starts, whether you type /stratum:st-plan or the orchestrator starts it. A phase out of order gets one warning, then runs.[Full lane | Plan], [Fast lane] or [Quick lane] while a lane runs, else [Orchestrator]. The phase hook adds the label from state.json; the st-close agent (Full and Fast lanes) or the st-quick agent (Quick lane) sets phase to done at the end of its run. After you upgrade, a project whose last feature is finished may still show its old phase: set phase to done once. Each Agent call is named with only its task, like US1 review, and each agent's report starts with its tag, like [st-build].scripts/st-verify.sh '<test command>' <files>. It re-runs the test and checks the listed files (new ones too) for added comment lines, and prints one ok or FAIL line, so each check is one short call.Stratum, project path, and session length on the left, git branch on the right. Line 2: model with its thinking level, and ponytail on the left, the commit option on the right. Each line fits the terminal width: the path becomes the folder name, other segments get shorter, then drop. The branch and the commit option always stay. See Statusline colors.☂ Showers ━━━━━━━━━━━━──────── 600k/1M about 4 turns left.scripts/st-worktree.sh <branch> <name> makes a sibling worktree that shares this project's local settings and Claude memory. Worktrees and branches stay until you delete them.The defaults are in statusline/powerline.json (rose-pine). To change them for one project, create .stratum/powerline.json. Its keys merge over the defaults. Git ignores it, so each person keeps their own look.
Pick a theme: rose-pine, nord, tokyo-night, gruvbox, dark, or light. All segments and the Token Weather line follow it. Pick a shape: arrow (default), rounded, slanted, blocks, or flat (colored text, no backgrounds). arrow, rounded, and slanted need a Nerd Font. /stratum:st-shape <shape> and /stratum:st-theme <theme> set them for you.
Claude Code draws the statusline a few columns narrower than the terminal and cuts what does not fit with …. Each line leaves reserve columns free (default 8). If the right side is still cut, raise it: { "reserve": 10 }.
To hide the Token Weather line, set { "weather": false }.
The Stratum label starts with a Nerd Font layers icon (). Set "logo" to another character, or to "" for none. The font itself comes from your terminal settings.
{ "theme": "nord", "shape": "rounded" }
Or set your own colors. Keys you leave out keep the rose-pine color.
{
"theme": "custom",
"colors": {
"custom": {
"git": { "bg": "#1f1d2e", "fg": "#9ccfd8" },
"ponytail": { "bg": "#2a273f", "fg": "#eb6f92" },
"stratum": { "bg": "#191724", "fg": "#ebbcba" },
"commit": { "bg": "#1f1d2e", "auto": "#9ccfd8", "ask": "#f6c177", "deny": "#eb6f92" }
}
}
}
Other keys: directory (project folder), model, and metrics (session length). The stratum label uses the model colors unless you set it. Other color options from the claude-powerline docs also work here.
If ponytail, impeccable, ui-ux-pro-max, graphify's skill, grilling, or hallmark are also installed globally, their hooks or skills can run twice. st-init turns off a global ponytail or Token Weather plugin in the project's .claude/settings.json. st-status lists the rest. Turn the global ones off for projects that use Stratum.
Stratum owns modified copies of its sources. vendor.lock pins each upstream commit. Once a month, run /stratum:st-sync in this repo: it lists upstream changes since the pin, drafts the ports, and waits for review before updating vendor.lock.
python3 tests/test_git_guard.py
python3 tests/test_model_hook.py
python3 tests/test_phase_hook.py
bash tests/test_advisor.sh
bash tests/test_design_check.sh
bash tests/test_design_lane.sh
bash tests/test_model_files.sh
bash tests/test_herdr_workspace.sh
bash tests/test_inspect.sh
bash tests/test_on_demand.sh
bash tests/test_pr_skill.sh
bash tests/test_session_hooks.sh
bash tests/test_shortcuts.sh
bash tests/test_st_worker.sh
bash tests/test_statusline.sh
bash tests/test_verify.sh
node tests/test_token_weather.mjs
node tests/test_pr_workflow.mjs
claude plugin validate .claude-plugin/plugin.json
Stratum is MIT licensed. It contains modified copies of GitHub Spec Kit, vibecode-pro-max-kit, ponytail, mattpocock/skills, ui-ux-pro-max, impeccable, frontend-design, graphify's skill, Token Weather, and the theme colors of claude-powerline, each under its own MIT or Apache-2.0 license. See THIRD_PARTY_NOTICES.md and licenses/.
hooks/register.tsx 60 lines1import type { Register } from 'claude-code'
2
3const PANE = 'spike-send'
4const sent = { plugin: 'spike-send', key: 'sent' } as const
5const reply = { plugin: 'spike-send', key: 'reply' } as const
6const origin = { plugin: 'spike-send', key: 'origin' } as const
7
8export const register: Register = on => {
9 on('session.start', async ($, e, next) => {
10 await $.command.register({
11 name: 'spike-send',
12 description: 'Send one spike message to a session and show the reply in a pane',
13 })
14
15 return next(e)
16 })
17
18 on('command.run', { command: 'spike-send' }, async ($, e) => {
19 await $.ui.open({ id: PANE, title: 'spike-send' })
20 const arg = e.args.trim()
21 const id = arg.startsWith('sid:') ? arg.slice(4).trim() : ''
22 if (!arg || (arg.startsWith('sid:') && !id)) {
23 return { text: 'Usage: /spike-send <name> | sid:<session id>' }
24 }
25 const to = id ? { sessionId: id } : arg
26 const result = await $.session.send({
27 to,
28 text: 'spk003 ping: reply to this message with SendMessage, text exactly spk003-pong',
29 })
30 const line = result.isDelivered ? 'delivered' : `not delivered ${result.reason}`
31 await $.state.set(sent, line)
32
33 return { text: `spike-send: ${line}` }
34 })
35
36 on('session.receive', async ($, e, next) => {
37 if ((e.origin.kind === 'peer' || e.origin.kind === 'peer-send-message') && e.text.includes('spk003')) {
38 await $.state.set(reply, e.text)
39 await $.state.set(origin, e.origin.kind)
40 }
41
42 return next(e)
43 })
44
45 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
46 const { Box, Text } = $.ui.resolve(e)
47 const { value: s = '' } = await $.state.get(sent)
48 const { value: r = '' } = await $.state.get(reply)
49 const { value: o = '' } = await $.state.get(origin)
50
51 return (
52 <Box flexDirection="column">
53 <Text>{`sent: ${s}`}</Text>
54 <Text>{`reply: ${r}`}</Text>
55 <Text>{`origin: ${o}`}</Text>
56 </Box>
57 )
58 })
59}
60types/index.d.ts 6 lines1declare module 'claude-code' {
2 interface PluginState {
3 'spike-send': { sent: string; reply: string; origin: string }
4 }
5}
6