Skip to content

Subagents ​

Spexso defines six specialist subagents. Each is a markdown file in .claude/agents/ with a YAML frontmatter defining its role, tools, and isolation level. Subagents are spawned by slash commands — never directly by the developer.

Agent Table ​

AgentSpawned byIsolationPurpose
spexso-planner/spexso-plan, /spexso-marathon (plan-wave scouts)None (reads only)Decomposes spec into ordered tasks with dependencies, file targets, test requirements. Outputs structured task list + proposes ADRs for any architecturally significant decisions it makes. Never emits verification-only tasks ("run the full suite") — the full suite belongs to the finish gate and CI. When docs/architecture/btp-services.md or docs/architecture/tenant-requirements.md exists, it also runs a Binding Impact Assessment and/or a tenant-scope classification (see Project registries) before task breakdown. When a task introduces a new module or service (or moves logic between modules) it reads .spexso/rules/architecture.md and plans the smallest interface that hides the most behaviour, with a seam only where something actually varies. In scout mode (marathon plan-wave, parallel.budget > 1) it fetches the spec itself, writes the plan to .spexso/plans/issue-{id}.md, and returns only the file footprint that feeds the marathon's wave-cut. Before planning, the scout self-assesses the spec (testable acceptance criteria? unambiguous scope? identifiable files/systems?) and may decline a clearly unplannable issue with plannable: false + a one-line reason instead of a plan — the marathon labels it needs::attention and drops it from the run before three strikes get burned; borderline specs still get planned.
spexso-tester/spexso-buildWorktree (current)Writes failing tests BEFORE implementation. Reads spec + task + the test rulebook, generates Jest/Mocha/cds.test()/OPA5 tests. Ensures red phase. The clause tests are the ceiling, not the floor — one test per EARS clause plus the edge cases the spec names, at the cheapest level that can falsify them (see what earns a test). Writes ONLY its own task's test files — wave siblings run concurrently under parallel RED (see TDD execution waves). When faithful spec tests pass because the behavior already exists (brownfield), it reports "already implemented" instead of distorting tests — see TDD terminal states. For docs/config-only tasks it may return no-test-needed; the orchestrator verifies the claim against the changed-file extensions. When the task lists EARS clauses it ends its report with an AC mapping (clause id → test names) that feeds the reviewer's per-clause rubric — see Clause rubric.
spexso-builder/spexso-buildWorktree (current)Writes minimum code to make tests pass. Reads the matching domain rulebook + CLAUDE.md patterns before GREEN. Does not modify tests. Deferrals are explicit: the builder marks knowingly deferred work with // spexso-debt: <what + upgrade trigger> instead of bare TODOs; npx spexso debt harvests all markers into the committed .spexso/debt.md ledger (refreshed by /spexso-finish).
spexso-reviewer/spexso-reviewNone (reads + writes fixes)Reviews all changes against spec, domain rulebooks, and patterns. Spec compliance is a binary per-EARS-clause rubric (pass/fail/not-testable) checked against the tester's AC mapping — never open-ended judge prose; every finding is anchored to a clause, a rulebook rule, or a deterministic check output (incl. the opt-in mutation spot-gate). Tags issues by severity. Auto-rewrites critical/high.
spexso-researcher/spexso-tinderNone (reads + web search)Competitive research — searches web and SAP ecosystem, generates comparison matrix, auto-creates enhancement issues.
spexso-issue-runner/spexso-marathonSubagent context (fresh per issue)Sequences start → plan → build → review → finish --train for ONE issue and returns a JSON summary (ready_to_merge + mr_id + changed_files). The merge itself happens in the marathon's merge train, not in the runner. Keeps the marathon orchestrator's context flat across long runs. In an auto-pick run it starts with --autopick (atomic branch claim) and returns skipped when the claim is lost. With a bundle (issue_ids) it starts the lead, locks the companions with bundle join, plans and builds each member in the lead's worktree (a failing companion is reset out and dropped), then reviews and finishes once; its summary adds bundle and dropped.

All 6 subagents — detailed ​

AgentRoleIsolationDefault model
spexso-plannerDecomposes spec into ordered tasksNone (reads only)opus
spexso-testerWrites failing tests (red phase)Worktree (current)sonnet
spexso-builderWrites code to pass tests (green phase)Worktree (current)sonnet
spexso-reviewerReviews code, auto-rewrites critical/highWorktree (current)sonnet
spexso-researcherCompetitive research for /spexso-tinderNone (reads + web search)sonnet
spexso-issue-runnerSequences one issue through MR creation for marathonSubagent context (fresh per issue)sonnet

Model defaults are set in each agent's YAML frontmatter. Override per project in .spexso/config.json — each entry is a plain model string, or an object that also sets the reasoning effort (low / medium / high / xhigh / max):

json
{
  "models": {
    "planner": { "model": "opus", "effort": "high" },
    "tester": "sonnet",
    "builder": "sonnet",
    "reviewer": "opus",
    "researcher": "sonnet",
    "issue_runner": "sonnet"
  }
}

This is configured during /spexso-setup and can be edited directly between sessions.

Effort needs npx spexso agents sync

Claude Code resolves a subagent's model at spawn time (the slash commands read it from config.json on every spawn), but reads effort from the agent's frontmatter only — it cannot be passed per spawn. npx spexso agents sync stamps the config.json models block into the frontmatter of .claude/agents/spexso-*.md: it rewrites the model: line and adds/updates/removes the effort: line. /spexso-setup runs it automatically; run it manually after hand-editing an effort in config.json. Omitting effort falls back to the agent default (the sync removes a stale effort: line).

Domain rulebooks ​

Three agents — the builder, the tester, and the reviewer — share a project-tunable knowledge layer: domain-scoped rulebooks in .spexso/rules/. These hold this project's code quality tips, tricks, and rules for each SAP domain, and sit between the generic external skills and CLAUDE.md:

LayerSourceScopeTunable per project
Skills (sap-cap-capire, sapui5)plugin, externalgeneric SAPno (shared)
.spexso/rules/{domain}.mdthis projectper domain, loaded on demandyes — edit the files
CLAUDE.mdthis projecteverything, always loadedyes (keep lean)

Every rulebook is a pair: ui5.md is the baseline spexso ships, ui5.local.md is the project's own file. The agents read both and the .local.md line wins on conflict — see Baseline + local rules below.

Both agents match the file paths in the task/diff against domain triggers and load the matching rulebook (plus its .local.md companion):

DomainTrigger filesRulebookRead by
TypeScript (base)any .ts/.tsx/.js/.jsxtypescript.mdbuilder, tester, reviewer
CAP.cds, srv/**, db/**cap.mdbuilder, tester, reviewer
UI5 (code)*.view.xml, *.fragment.xml, webapp/controller/**, manifest.jsonui5.mdbuilder, tester, reviewer
Fiori UX (design)*.view.xml, *.fragment.xml, webapp/**, manifest.jsonux.mdplanner (Part A), builder, reviewer (comment-only)
Test*.test.ts/js, test/**, QUnit/OPA5test.mdtester, reviewer
ABAP*.abapabap.md (ships since v1.5: clean core, released-APIs-only, RAP, ABAP Unit)builder, tester, reviewer
Interview (process)none — read by nameinterview.md/spexso-brainstorm, /spexso-kickstart
Architecture (process)none — read by namearchitecture.md/spexso-architecture, planner (new module or service)
Prototype (process)none — read by nameprototype.md/spexso-prototype, /spexso-brainstorm --design

interview.md is a process rulebook: how Spexso interviews a developer before writing a spec. Facts are looked up, decisions are asked; questions come in frontier rounds of at most 4 with a recommended answer first; a coverage checklist (users, SAP system, data model, integrations, UI, edge cases, out of scope, local startup, demo) decides when the interview is done. interview.local.md adds team branches.

Domain glossary ​

A project's domain language lives in GLOSSARY.md at the project root, in the client's language (format: .spexso/templates/glossary.md). The doctrine's Domain language section tells every agent and subagent to read it once and name tasks, tests, entities and findings with its terms. Terms grow through the spec: /spexso-brainstorm writes new terms into the spec's ## Glossary section, /spexso-finish merges them into GLOSSARY.md so they ship with the code, and GLOSSARY.md merge=union in .gitattributes (set by spexso init) reconciles concurrent issues. On opencode, subagents don't get a doctrine re-injection, so there only the main session sees the glossary rule.

typescript.md is a base layer that applies to any TS/JS file; the domain rulebooks add their rules on top (a CAP .ts handler → typescript.md + cap.md; a plain TS utility → typescript.md only).

  • The builder reads the domain rulebook before the GREEN phase → prevents issues at the source.
  • The tester reads test.md (+ the domain rulebook) before the RED phase → tests target the right behaviour with the project's testing discipline (what earns a test at all and at which level, behavioural over structural, worktree path rules).
  • The reviewer reads the same rulebooks → catches what slipped through, as extra review criteria classified by the normal severity table.

One file, multiple consumers — no drift between what gets tested, built, and reviewed. This extends the build-phase defense-in-depth: rulebook + skills (prevent) → tests (verify) → static checks (gate) → reviewer re-checks the same rulebook (catch).

The Fiori UX rulebook (ux.md) is a separate design lens — floorplan & layout choice, action placement, control/element states, status semantics, information architecture, copy, accessibility, localization — distinct from ui5.md's code lens. It has three consumers across the lifecycle:

  • the planner reads its Part A ("Choosing the right pattern") to pick floorplan, layout, Fiori Elements vs freestyle, table type, selection control, and the control for each data shape (§5b: a timeline for history, a progress/rating indicator or micro chart for a number with a target or scale, KPIs in the object header — a table is not the default for everything) up front, recording significant choices as ADRs. It writes the controls and header content into each UI task's UI: line, starting from the spec's UI table, which the interview fills per screen (user goal, data shape, control, header key figures);
  • the builder builds the task's UI: contract and follows §5b and Parts B–C to build the UI correctly the first time. A contracted control is never an "unrequested" addition, also not for shortest-diff plugins such as ponytail, and "follow existing patterns" covers code conventions, not an older screen's control choice;
  • the reviewer checks the diff against the UI: contract — a missing or replaced control is HIGH "UI contract drift", like floorplan drift — and raises other violations as a UX critique, including the "plain-UI smells" of §5b with the better control named. Its UX trigger includes Fiori Elements annotations (annotations.cds, app/**/*.cds).

Reviewer findings from ux.md are advisory: comment-only, never auto-rewritten (a second exception alongside static-check findings), because UX is a design judgement for a human. Code-level UI5 findings from ui5.md still follow the normal severity + auto-rewrite rules.

Baseline + local rules ​

.spexso/rules/ui5.md         # spexso's baseline — refreshed on update, not yours to edit
.spexso/rules/ui5.local.md   # the project's own rules — committed, never touched by spexso

Splitting the two is what makes updating painless: the baseline can improve with every spexso release without ever colliding with what the team added. .local.md files are short and additive (no need to restate the baseline), committed with the project so the whole team shares them, and they are where /spexso-review (pilot/copilot) and /spexso-rulebook promote a systemic lesson.

spexso init creates each companion empty — just an HTML comment naming what belongs there — so the place to put project rules shows up in the directory listing instead of only in this page. doctrine.local.md follows the same pattern and is appended to the doctrine injected at every SessionStart/SubagentStart; an untouched stub injects nothing.

On spexso init — which is also the update path — every baseline is either seeded (missing), refreshed (unchanged since spexso installed it), or kept with a warning (the team edited the baseline itself: move those lines into {domain}.local.md and delete the baseline to start receiving updates again). .spexso/rules/.baselines.json records the installed hash and is how "edited by you" is detected; commit it. *.local.md is never read, written, or overwritten by that sync.

Precedence on conflict: agent process rules > CLAUDE.md > {domain}.local.md > {domain}.md > generic skill knowledge. See Skills Architecture for how this sits alongside the skill hierarchy.

MCP-first lookups & the UI5 linter gate ​

The builder, tester, and reviewer are MCP-first for anything SAP-specific. Before answering an API, control/aggregation, annotation, or best-practice question from memory — and before falling back to WebSearch — they consult the relevant domain MCP server (for annotation-driven Fiori Elements apps the Fiori MCP first, UI5 MCP as fallback; cds-mcp / sap-cap-capire, ui5-mcp, sapdocs, Fiori MCP). SAP APIs version fast, so memory is the least reliable source and WebSearch is a fallback only when no MCP plugin is installed. A confidently-wrong answer (or auto-rewrite) from stale memory is worse than a verified one.

This is codified as the Tier-0 SAP grounding rules in the CLAUDE.md snippet (trimmed per detected stack by /spexso-setup): search-before-generate, explain-before-change, verify-before-GREEN (cds build / run_ui5_linter / abap_lint+ATC), released-APIs-only for ABAP Cloud. The degradation ladder is explicit — MCP → skill/rulebook → WebFetch → tag the task "docs-unverified" (a reviewer finding), never silently proceed on memory. MCP servers are wired per detected stack, never all at once (context is a budget); the Fiori MCP joins only when an actual Fiori app exists.

Each agent's tools: list is an allowlist, so it names Skill and one wildcard per SAP MCP server (mcp__plugin_ui5_ui5-mcp-server__*, mcp__plugin_cds-mcp_cds-mcp__*, mcp__plugin_sap-cap-capire_sap-cap-capire__*, mcp__plugin_sap-fiori-tools_fiori-tools__*, mcp__fiori-mcp__*, mcp__sap-docs__*). A bare mcp__* grants nothing, and a server that isn't installed simply contributes no tools. Plugin servers are named mcp__plugin_<plugin>_<server>__<tool>. A server your project registers under its own name (mcp__cap__* at user scope, mcp__cap-mcp-server__* from .mcp.json) needs its own entry: add it to the installed .claude/agents/spexso-*.md. spexso update and a re-init keep those additions and append them to the stock list.

For UI5 work the linter is a required "done" gate, the UI equivalent of cds build for CAP. The builder runs run_ui5_linter (ui5-mcp) on changed UI files after GREEN and fixes findings before reporting success — capped at 3 fix cycles via npx spexso attempt {task} lint (the same code-enforced counter as RED/GREEN); at the ceiling the builder stops and reports the remaining findings for escalation instead of looping. The reviewer runs the linter across UI diffs. It catches runtime-only defects that the production build and source-based unit tests pass over — invalid aggregations/control settings, growing bindings missing $$operationMode: 'Server', wrong xmlns. When the plugin is unavailable, the shell npx ui5lintstatic check is the fallback. Linter findings are HIGH and never auto-rewritten (human judgement — same exception as other static-check findings).

Being MCP-first doesn't mean being MCP-heavy: both the builder and the reviewer read code and rulebooks before querying any MCP, fire one query per distinct concern instead of overlapping lookups, and cache results in memory for the rest of the task rather than repeating a lookup.

Language ​

Every Spexso agent responds in the language the user is using (Dutch → Dutch, English → English) — applied to all reports, findings, summaries, and questions. This is set by a # Language block at the top of each agent definition.

Agent Definition Format ​

yaml
# Example: .claude/agents/spexso-reviewer.md

---
name: spexso-reviewer
description: Code review agent with severity-based auto-rewrite
model: sonnet
tools:
  - Read
  - Write
  - Bash
  - Task
---

# Role
You are a senior code reviewer for a Flexso SAP BTP project.
Review all code changes in the current branch against the spec
and project conventions from CLAUDE.md.

# Severity Classification
CRITICAL — auto-rewrite immediately:
- Security vulnerabilities (SQL injection, auth bypass, XSS)
- Data loss risks (missing transactions, no error handling on writes)
- Broken API contracts (response shape differs from spec)
- Hardcoded secrets or tenant IDs

HIGH — auto-rewrite immediately:
- Missing error handling on external calls
- Spec deviation (behavior doesn't match acceptance criteria)
- No asserting test for an implemented EARS clause
- Missing input validation
- CDS model doesn't match spec data model

LOW — comment only, do not rewrite:
- Over-test: tests no clause requires, or an OPA5 journey for what a unit test could falsify
- Naming convention deviations
- Code style preferences
- Minor refactoring opportunities
- Documentation improvements

# Process
1. Run: git diff main...HEAD --name-only (list changed files)
2. For each changed file, review against spec and CLAUDE.md
3. Collect all issues with severity tags
4. For CRITICAL and HIGH: apply fix, run tests, commit with
   message "fix(spexso-review): [severity] description"
5. For LOW: collect as comments only
6. Output structured review report