Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .ai/prompts/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ No description provided.

Implement the solution, create a PR, and ensure it passes all quality checks.

## Rule

Always sign git commits. If a gpg-agent is not available with a signing key,
stop and note that on the linear issue, do not create an unsigned commit.

## First run

1. Read the investigation summary from the Linear comments.
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
url = git@github.com:fuelen/owl
[submodule "vendor/stokowski"]
path = vendor/stokowski
url = https://github.com/Sugar-Coffee/stokowski
url = git@github.com:bougyman/stokowski
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ and best practices for agents to follow.

## Project Structure

### Linear Team and Project

The `Linear CLI` project of the "Elixir Traitors" (`EXT`) team on the
https://linear.app/the-rubyists[Rubyists Linear Organization] manages
the project roadmap and issue tracking.

#### Rules for Interacting with Linear

Always only use `mix lc` to interact with linear.app. Do not look for an
mcp or any other way to interact with issues or the project.

`mix lc --help`
`mix lc issue --help`
`mix lc issue list --help`
`mix lc issue create --help`
`mix lc issue status --help`

This should be enough to get started

### Repository layout

This repo uses two Mix projects side-by-side, not nested:
Expand Down
397 changes: 397 additions & 0 deletions documents/phase-13-plan.adoc

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@
erlang = "29.0.3"
elixir = "1.20.3"
python = "3.14.1"
[env]
ANTHROPIC_BASE_URL = "http://127.0.0.1:13305"
ANTHROPIC_AUTH_TOKEN = "lemonade"
LEMONADE_API_KEY = "lemonade"
ANTHROPIC_DEFAULT_OPUS_MODEL = "user.Qwen3.6-35B-A3B-ThinkingCoder"
ANTHROPIC_DEFAULT_SONNET_MODEL = "user.Qwen3.6-35B-A3B-ThinkingCoder"
ANTHROPIC_DEFAULT_HAIKU_MODEL = "user.Qwen3.6-35B-A3B-ThinkingCoder"
CLAUDE_CODE_SUBAGENT_MODEL = "user.Qwen3.6-35B-A3B-ThinkingCoder"
CLAUDE_CODE_ATTRIBUTION_HEADER = 0
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = 1
2 changes: 1 addition & 1 deletion vendor/stokowski
263 changes: 263 additions & 0 deletions workflow.claude.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# =============================================================================
# Stokowski — Example State Machine Workflow
# =============================================================================
#
# This is linear-cli's own checked-in workflow config, not a template to
# copy elsewhere. Stokowski re-reads this file on every poll tick — changes
# take effect without restart.
#
# Prompt files live in the .ai/prompts/ directory. Each agent state
# references its prompt via the `prompt` field. A global prompt (shared
# preamble) is injected before the stage-specific prompt when set.
#
# Linear states mapping:
# active → the "working" state for agent stages
# review → human review / PR review state
# gate_approved → human approved a gate (triggers transition)
# rework → human requested changes (triggers rework_to)
# terminal → list of done/closed states
#
# State types:
# agent — Stokowski dispatches Claude Code in this state
# gate — pauses for human review; approve → next, rework → rework_to
# terminal — issue is finished; workspace cleaned up
#
# -----------------------------------------------------------------------------
# Multi-project mode (optional — single-project YAML below still works as-is)
# -----------------------------------------------------------------------------
#
# To monitor multiple Linear projects from one Stokowski process, replace the
# top-level `tracker / workspace / hooks / prompts / states` blocks with a
# `projects:` list. Each entry has its own tracker/workspace/hooks/prompts/
# state machine. Top-level `polling`, `agent`, `claude`, `linear_states`, and
# `server` stay global and act as defaults each project can override.
#
# agent:
# max_concurrent_agents: 8 # global pool across all projects
# max_concurrent_per_project: # optional fairness caps
# synced-sport: 6
# client-site: 2
#
# projects:
# - name: synced-sport # required, used in dashboard + `p` menu
# tracker:
# kind: linear
# project_slug: "abc123def456"
# api_key: "$LINEAR_API_KEY"
# workspace:
# root: ~/.local/share/stokowski/workspaces/synced-sport
# hooks: { after_create: "git clone git@github.com:org/synced-sport.git ." }
# prompts: { global_prompt: prompts/synced-sport/global.md }
# states: { ... } # state machine, same as single-project
#
# - name: client-site
# paused: true # start paused; toggle with `p` key
# tracker:
# kind: linear
# project_slug: "def456abc789"
# api_key: "$LINEAR_API_KEY"
# workspace:
# root: ~/.local/share/stokowski/workspaces/client-site
# hooks: { after_create: "git clone git@github.com:org/client-site.git ." }
# prompts: { global_prompt: prompts/client-site/global.md }
# claude: { model: claude-opus-4-7 } # per-project override
# max_concurrent: 2 # per-project cap (also via agent map above)
# states: { ... }
#
# In the terminal, `p` opens a numbered pause menu; in the web dashboard, each
# project tile gets a Pause/Resume button and a per-project filter dropdown.
# =============================================================================

# ---------------------------------------------------------------------------
# Tracker
# ---------------------------------------------------------------------------
tracker:
kind: linear
project_slug: "94a76f2ac65f" # hex slugId from your Linear project URL
# api_key: "$LINEAR_API_KEY" # your Linear API key — agents inherit this
assignee: me # only issues assigned to the API key's user

# ---------------------------------------------------------------------------
# Linear state name mapping
# ---------------------------------------------------------------------------
# These map Stokowski's internal lifecycle roles to your Linear state names.
# You can rename values to match your Linear setup (e.g. todo: "Ready"),
# but all six roles are required — removing or reordering them will break
# the dispatch and gate protocol.
linear_states:
todo: "Todo" # issues picked up from this state
active: "In Progress" # moved here automatically when agent starts
review: "Human Review" # agent pauses here at a gate for human review
gate_approved: "Gate Approved" # human approved — agent advances to next state
rework: "Rework" # human requested changes — agent re-enters rework target
terminal: # issues in these states stop any running agent
- Done
- Closed
- Cancelled
- Canceled
- Duplicate

# ---------------------------------------------------------------------------
# Polling
# ---------------------------------------------------------------------------
polling:
interval_ms: 60000 # 15 seconds between ticks

# ---------------------------------------------------------------------------
# Workspace
# ---------------------------------------------------------------------------
workspace:
root: ~/.local/share/stokowski/workspaces/linear-cli

# ---------------------------------------------------------------------------
# Hooks — shell scripts run in the workspace directory
# ---------------------------------------------------------------------------
hooks:
after_create: |
git clone --depth 1 --recursive git@github.com:rubyists/linear-cli .
mix setup
before_run: |
set -eu
git fetch origin main
changes="$(git status --porcelain)"
if [ -n "$changes" ]; then
echo "before_run: preserving uncommitted workspace changes; skipping rebase"
else
git rebase origin/main
fi
timeout_ms: 120000

# ---------------------------------------------------------------------------
# Claude defaults (inherited by all agent states unless overridden)
# States using runner: codex will use the Codex CLI instead.
# ---------------------------------------------------------------------------
claude:
permission_mode: auto
max_turns: 20
turn_timeout_ms: 3600000 # 1 hour per turn
stall_timeout_ms: 900000 # 15 minutes with no output → kill

# ---------------------------------------------------------------------------
# Agent concurrency
# ---------------------------------------------------------------------------
agent:
max_concurrent_agents: 4
max_retry_backoff_ms: 300000 # 5-minute cap on exponential backoff
max_concurrent_agents_by_state:
investigate: 2 # allow 2 concurrent investigations
implement: 2
code-review: 1 # serial reviews prevent merge conflicts

# ---------------------------------------------------------------------------
# Prompts
# ---------------------------------------------------------------------------
prompts:
global_prompt: .ai/prompts/global.md

# ---------------------------------------------------------------------------
# Server (optional web dashboard)
# ---------------------------------------------------------------------------
server:
host: 127.0.0.1
port: 4200

# ---------------------------------------------------------------------------
# State machine
# ---------------------------------------------------------------------------
#
# Order matters: the first agent state is the entry state for new issues.
#
# Transitions fire automatically on agent success or human action:
# - Agent finishes successfully → "complete" transition
# - Human approves a gate → "approve" transition
# - Human requests rework → rework_to target
#
states:

# ── 1. Investigate ──────────────────────────────────────────────────────
# Read the codebase, understand the problem, post an investigation summary.
# Uses Opus for deeper reasoning. Low turn count — investigation only.
investigate:
type: agent
prompt: .ai/prompts/investigate.md
linear_state: active
max_turns: 8
session: inherit
transitions:
complete: research-review

# ── 2. Research Review (gate) ───────────────────────────────────────────
# Human reviews the investigation before implementation starts.
# Rework loops back to investigate.
research-review:
type: gate
linear_state: review
rework_to: investigate
max_rework: 3
transitions:
approve: implement

# ── 3. Implement ────────────────────────────────────────────────────────
# Write code, create branch, run tests, open PR.
# Uses Sonnet for fast iteration. Higher turn count for complex work.
implement:
type: agent
prompt: .ai/prompts/implement.md
linear_state: active
max_turns: 30
session: inherit
transitions:
complete: implementation-review

# ── 4. Implementation Review (gate) ─────────────────────────────────────
# Human reviews the PR before automated code review.
# Rework loops back to implement.
implementation-review:
type: gate
linear_state: review
rework_to: implement
max_rework: 5
transitions:
approve: code-review

# ── 5. Code Review ─────────────────────────────────────────────────────
# Automated adversarial review: fresh session, no prior context.
# Opus reviews the diff and posts findings.
# Tip: set runner: codex here to get a second-opinion from a different
# provider — different states can use different runners in the same run.
code-review:
type: agent
prompt: .ai/prompts/review.md
linear_state: active
runner: claude # "claude" (default) or "codex"
max_turns: 10
session: fresh # no prior context — adversarial review
transitions:
complete: merge-review

# ── 6. Merge Review (gate) ──────────────────────────────────────────────
# Human reviews the code review findings and decides to merge or rework.
# Rework goes back to implement (not code-review).
merge-review:
type: gate
linear_state: review
rework_to: implement
max_rework: 5
transitions:
approve: merge

# ── 7. Merge ────────────────────────────────────────────────────────────
# Merge the PR and clean up. Short turn count — just the merge.
merge:
type: agent
prompt: .ai/prompts/merge.md
linear_state: active
max_turns: 5
session: inherit
transitions:
complete: done

# ── 8. Done (terminal) ─────────────────────────────────────────────────
done:
type: terminal
linear_state: terminal
Loading
Loading