SLOPSHOPPER

stratum

Agent harness for Claude Code: spec-driven lanes, an orchestrator with st-* subagents, commit guard, handoff, and forked design and navigation tools.

new
★ 3v0.1.25MITupdated 2026-10-08HyuseCS/stratum
A shopper browsing a rack in a slop shop
README

Stratum

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:

  • GitHub Spec Kit: the spec, plan, and tasks files, and their templates.
  • vibecode-pro-max (RIPER-5): role agents for validate, build, test, review, debug, and git.
  • Stratum's own layer: lane selection, the build loop, a git guard, a session handoff, a statusline, and forked design and navigation tools, all shipped as one plugin.

The full design and the reason for each choice are in DESIGN.md.

Install

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/, grills you for the project's rules (the constitution), 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.

Requirements

ToolUsed for
git, python3git guard, scripts
nodeponytail hooks
bunx (Bun)statusline bar (falls back to markers only)
graphify 0.9.74 or later (uv tool install graphifyy)code navigation graph, Dart support
ghoptional, for st-issues and st-pr

Use

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.

Lanes

The lane follows the highest impact area the change touches.

Impact areaExamplesLane
Privacy and accessaccess rules, data model, contracts, sign-in, personal dataFull
New user story or featureanything not in a current specFull
Core runtimebackground services, offline queue, workers, noticesFast
Screenslayout, components, navigationFast (Quick for a one-file visual fix)
Text and docslabels, typos, wordingQuick

Full lane: five phases and three gates.

#PhaseWhat happensGate
1DefineGrilling, then the spec, then clarify (if needed) and checklistYou agree the spec
2Planst-plan writes the plan and tasks. Open decisions come to you one at a time.Asked: make GitHub issues?
3Checkst-check checks the files agree, st-validate checks the plan can be builtYou OK the build
4BuildPer task: failing test, build, check, commit. Per story: review, ponytail review, design review.None unless blocked
5CloseFix spec drift, optional gap report, proposed lessonsPush only when you say "push"

Fast lane: a short change plan in specs/<feature>/changes/, your OK, test-first build, ponytail review, commit, drift fix.

Quick lane: one file, no gate, related tests, commit. It moves up a lane if it needs more.

Skills

SkillWhat it does
st <task>Main entry. Picks the lane and runs it.
st-full, st-fast, st-quickForce a lane.
st-define, st-plan, st-check, st-build, st-closeRun or resume one Full-lane phase.
st-statusFeature, phase, lane, tasks done, git guard options, next gate, missing tools.
`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-handoffWrite the session handoff.
st-initSet up a project.
st-template <name>Copy a template into the project to edit it.
st-constitutionAmend the project rules.
st-issuesTurn tasks into GitHub issues.
st-pr [base]Open a draft PR from git facts and the spec; ready when CI passes.
st-syncMaintainers: port upstream changes into Stratum's forks.
st-grillGrilling interview, one question at a time.
st-ui-ux, st-impeccable, st-frontend-designDesign build rules, design review, visual direction.
st-graphifyBuild or query the code graph.
st-ponytail, st-ponytail-review, -audit, -debt, -gain, -helpMinimal-code mode and its reviews.

All skills are called as /stratum:<name>.

Agents

AgentModelJob
st-planOpusPlan, research, data model, contracts, quickstart, tasks
st-checkSonnetSpec, plan, and tasks agree (read-only)
st-validateOpusSetup, test coverage, breaking changes, security
st-buildOpusBuilds tasks; loads design rules for screens
st-testSonnetWrites each test and shows it fails first
st-reviewOpusReviews each finished story
st-debugOpusTakes over after 2 failed tries
st-fastSonnetFast-lane change plans
st-quickSonnetQuick-lane edits
st-closeSonnetDrift fixes, gap report, lessons
st-gitSonnetCommits each finished task, exact paths only

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.

Project files

.stratum/
├── constitution.md      project rules (committed)
├── state.json           current feature, phase, lane (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)
└── weather.json         Token Weather growth and compact point (git-ignored)
specs/NNN-feature/       spec, plan, research, data model, contracts, quickstart, tasks, changes/
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.

Built in

  • Advisor. If you set one with /advisor, the orchestrator consults it at three points: on the plan before the build gate, when a test fails twice, and on the full diff before calling the build done. Its notes are checked against the source like any finding. No advisor, no change.
  • Git guard. One mode per command: 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.
  • Session handoff. st-handoff writes the goal, decisions, open questions, and next step. 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.
  • Statusline. A powerline in two lines. Line 1: 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.
  • Token Weather. A context forecast as the statusline's third line, from ☀ Clear to ↯ Compact soon, with a fill bar and tokens used on the left and the turns left before auto-compaction on the right: ☂ Showers ━━━━━━━━━━━━──────── 600k/1M about 4 turns left.
  • Parallel sessions. 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.

Statusline colors

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.

Duplicate installs

If ponytail, impeccable, ui-ux-pro-max, graphify's skill, or grilling 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.

Maintaining the forks

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.

Tests

python3 tests/test_git_guard.py
bash tests/test_session_hooks.sh
bash tests/test_statusline.sh
node tests/test_token_weather.mjs
claude plugin validate .claude-plugin/plugin.json

Credits and license

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/.

Source 1 files
hooks/token-weather.mjs 86 lines
1// Copyright 2026 Anthropic PBC
2// SPDX-License-Identifier: Apache-2.0
3//
4// Token Weather: a live forecast of the context window, drawn by the
5// statusline as its third line (statusline/st-statusline.sh).
6//
7// turn.complete: after each main-loop turn, read the context window's fill
8// from $.session.usage() (the same figures the status line shows) and keep
9// the last HISTORY readings.
10// session.start: take a first reading, so the line shows before any turn.
11// Each reading writes .stratum/weather.json: the auto-compaction threshold
12// and the mean growth of the recent growing turns. The statusline takes the
13// fill from its own input; it cannot ask for the threshold or keep a history.
14//
15// The host reads on(...) and $.noun.method(...) from source, so they are
16// spelled literally, and helpers that take $ are top-level functions.
17
18const HISTORY = 12;
19const GROWTH_TURNS = 5;
20const STATE_FILE = ".stratum/weather.json";
21
22// Readings: { tokens, window, percent, compactAt }, oldest first.
23let readings = [];
24
25export function register(on) {
26  on("session.start", async ($, e, next) => {
27    const result = await next(e);
28    readings = [];
29    await takeReading($);
30    return result;
31  });
32
33  on("turn.complete", async ($, e, next) => {
34    const result = await next(e);
35    if (e.agentId) {
36      return result;
37    }
38    await takeReading($);
39    return result;
40  });
41}
42
43async function takeReading($) {
44  try {
45    if (!(await $.fs.exists(".stratum"))) {
46      return;
47    }
48    const { context } = await $.session.usage();
49    if (!context || !context.window) {
50      return;
51    }
52    const tokens = context.tokens ?? 0;
53    const percent = Math.round(context.percent ?? (tokens / context.window) * 100);
54    const compactAt = await compactThreshold($, context.window);
55    // The session.start reading is 0 before any response; drop it once real readings arrive.
56    readings = readings.filter((r) => r.tokens > 0);
57    readings.push({ tokens, window: context.window, percent, compactAt });
58    if (readings.length > HISTORY) {
59      readings = readings.slice(-HISTORY);
60    }
61    await $.fs.write(STATE_FILE, JSON.stringify({ compactAt, growth: growth() }) + "\n");
62  } catch {
63    // No reading this turn; the file keeps the last one.
64  }
65}
66
67async function compactThreshold($, window) {
68  try {
69    const { context } = await $.session.usage({ breakdown: "summary" });
70    const b = context.breakdown;
71    if (b?.isAutoCompactEnabled && b.autoCompactThreshold) {
72      return b.autoCompactThreshold;
73    }
74  } catch {
75    // No breakdown; count turns to a full window instead.
76  }
77  return window;
78}
79
80// Mean growth of the recent turns that grew; null until there is one.
81function growth() {
82  const recent = readings.slice(-(GROWTH_TURNS + 1));
83  const grew = recent.slice(1).map((r, i) => r.tokens - recent[i].tokens).filter((d) => d > 0);
84  return grew.length ? Math.round(grew.reduce((a, b) => a + b, 0) / grew.length) : null;
85}
86