Skip to content

Agent clans

This page describes the grouping and display behavior of agents working in parallel.

Parallel Agent Clans

An agent clan is a container, never an agent. A %clan prompt spells its full hood-qualified %id; other members can use %id(<id>, clan=<name>) to derive that name and join the same clan:

%id:release.build
%clan:release
Compile the release.
---
%id(test, clan=release)
Test the release.
---
%id(land, clan=release)
%wait:release.build,release.test
Publish the release after both members finish.

The short form is %c:release. %clan is create-only: exactly one prompt may declare a clan, and declaring a clan that already exists is an error. Use %clan(<name>, tribe=<tribe>) (or the %c(...) alias) to assign the declaration's tribe to the entire clan generation; tribe= on %id cannot be combined with clan membership. Every other member uses clan=. That form joins an existing clan or creates it implicitly without a tribe, takes exactly one member id, allows dotted ids, and accepts a leading ! for forced reuse. Static names and templates such as %id(cld, clan=research.{@1}) work; the derived research.{@1}.cld name flows through normal template allocation. In macro swarms, prefer keyed markers like {@1} so every clan member, wait, fork/resume target, and prose reference is substituted in the parent dispatch before any member starts. Bare templates such as research.@ still work, but their references use latest-wins lookup and are unsafe for late-starting swarm members. See Macro template directives for {@<id>} and {@<id>!}.

Launch-time clan summaries

The declaring member can attach a short description to its clan generation. sase's TUI displays this description near the top of the clan's CLAN panel; it is metadata and is not sent to the member as work instructions. When the clan belongs to a tribe, the tribe's TRIBE document (shown when the whole tribe panel is focused) also indexes this description in its CLAN SUMMARIES section (see Tribe Side Panels). Use a literal for stable context, the double-colon shorthand for a larger text block, or an executable when the description depends on state available as the runner starts. A literal keeps the work prompt immediately below the declaration:

%id:research.lead
%clan(research, tribe=study, summary="Audit authentication and authorization")
Review the security boundary.

The text-block shorthand ends when the next top-level directive or macro reference begins:

%id:release.lead
%clan:release:: Coordinate the release checks and summarize blockers.
#release_work

For a computed description, name an executable instead:

%id:triage.lead
%clan(triage, summary_script=./describe-triage)
Triage the current failures.

The value may also contain shell-style quoted arguments. Use the directive text-block form when the command itself contains spaces or quotes:

%id:release.lead
%clan(release, summary_script=[[sase_clan_summary_plan "plans/release plan.md"]])
Coordinate the release.

sase_clan_summary_plan renders any valid tale or epic plan with the same logical PLAN-lane presentation used by sase's TUI. Its first argument is the plan reference; when that argument is omitted, it reads SASE_EPIC_PLAN_REF instead.

summary= accepts the usual directive values: a bare token, a quoted string for text containing spaces or special characters, or a multiline [[...]] text block. The :: shorthand requires a following space or end of line and captures everything until the next top-level line that starts a directive (%) or macro reference (#). That captured text becomes only the summary, so use summary= when ordinary work instructions follow the declaration directly. summary= and summary_script= are mutually exclusive, and both belong only on the create-only %clan declaration; %id(..., clan=...) joiners cannot declare or replace a summary.

A summary executable may run synchronously twice: first while SASE extracts launch directives, before dependency waits, runner-slot admission, and workspace preparation, and again after the primary workspace and every applicable sidecar and linked-repository workspace have been prepared. The first attempt uses the workspace path with which the runner started; for a deferred-workspace launch, that is the placeholder workspace. The second attempt uses the real prepared workspace claimed after the wait. A waiting runner that re-execs to load updated SASE code can repeat this sequence, so summary executables must be read-only and idempotent.

A bare executable name is resolved next to the running Python interpreter and then on PATH; a value containing / is resolved as an executable absolute path or a path relative to that initial workspace. SASE first probes the complete value as a literal executable, preserving executable filenames that contain spaces. If that fails, it applies shell-style quoting, resolves the first token by the same rules, and passes the remaining tokens directly as argv; it never invokes a shell.

Each attempt inherits the runner environment available at that point. The post-preparation attempt therefore sees environment updates made while preparing the workspace and linked repositories. SASE gives both attempts the same identity contract: it overrides SASE_CLAN_NAME, SASE_CLAN_GENERATION, and, when the declaration has one, SASE_CLAN_TRIBE (otherwise an ambient clan-tribe value is removed). Epic work also provides SASE_EPIC_PLAN_REF, SASE_EPIC_PLAN_SNAPSHOT, SASE_EPIC_BEAD_ID, SASE_PHASE_BEAD_ID, and SASE_EPIC_CLAN_TRIBE; SASE reconstructs these values from persisted agent metadata after launch-only variables have been consumed. The snapshot is an absolute, project-scoped, guaranteed-local best-effort copy used by the built-in epic summary script after ordinary checkout candidates. The original plan reference remains the displayed and persisted association.

Standard output becomes the summary and standard error is appended to the agent log. Every attempt has the same 20-second timeout and non-fatal failure handling, and the stored summary is capped at 32 KiB of UTF-8. Missing executables, malformed quoting, timeouts, nonzero exits, and empty output are logged without blocking the agent launch. SASE uses the last successful non-empty output: a successful post-preparation attempt replaces the extraction-time value, while a later failed or empty attempt leaves the newest successful summary untouched.

SASE trims trailing whitespace and records the result in the clan's durable record under <sase_home>/agent_clans/<clan>.json, keyed by clan generation. What is recorded and when: a %clan declarer records its declared tribe= and either its literal summary= (source declared) or its summary_script= (source declared) plus the script's non-empty output (source script); an epic-nominated joiner records its script output (source script) and script (source propagated); any member carrying the epic environment tribe records that tribe (source propagated, fill-only); every post-preparation summary refresh records the fresh non-empty output (source script); and just before any artifact directory is deleted, its clan attributes are captured into the record (source captured, fill-only).

Precedence: for a (clan, generation) key, an attribute present in the record (including an explicit edited-unset tombstone) wins over member-derived values, so member artifacts without a clan record — imported or remote artifacts — still resolve, but never override it. declared, script, edited, and inherited overwrites beat member values; propagated and captured only fill missing attributes, so a clan-level tribe edit sticks even when newer epic members carry clan_tribe=epic. An empty or failed summary never erases a recorded one. The scan contract therefore resolves summaries and tribes within one clan generation from the record first, falling back to the newest explicit member declaration only when the record is silent. Rich markup is rendered when valid and shown as literal text when invalid.

Because the record outlives any single member, clan summaries and tribes survive kills, dismissals, relaunches, and full restarts: deleting the declaring agent's artifacts captures its attributes first, and later scans — full, bounded, and delta alike — apply the record over whatever member artifacts remain. The saved description is distinct from the foldable sections that sase's TUI synthesizes below it from member artifacts and activity.

Pressing N on a clan row or on any clan member sets the whole clan generation's recorded tribe through the same durable path, and clearing it writes an explicit edited-unset tombstone. The unset sticks even when members still carry an epic tribe, because fill-only propagated values never overwrite the record. Member edits also keep their agent_meta.json and %clan prompt rewrites so older readers still see the new tribe.

New clan generation: when a launch creates a generation of a recorded clan without an explicit tribe= (and without an epic environment tribe), the new member inherits the remembered tribe; without an explicit summary=/summary_script= (and without an epic nomination), it re-runs the remembered summary script through the usual launch-time summary path, falling back to the remembered text when the script is missing or produces no output. Inherited values are stored in the new member's metadata, recorded under the new generation with source inherited, and named with their source generations in one agent-log line. Explicit values always win, tombstoned tribes are never inherited, joining an existing generation changes nothing, and a runner re-exec does not re-apply values already in preserved metadata.

Clan membership is execution-neutral. It does not add wait edges, change launch order, choose a workspace or model, or otherwise rewrite launch behavior. Use %wait explicitly where ordering is required; waits between members of one clan generation are shown as derived dependency waves in the Agents tab. A wave is a layer, not a barrier, so a later wave may start while earlier work is still running. The clan=, session=, and tribe= keywords on %id are mutually exclusive, and none can be combined with %clan in the same segment.

The retired family keyword on %id is always accepted as session=. The retired family and kind agent-query values, the --next-fork family value, a gate "fork": "family", and the SASE_AGENT_FAMILY_ATTACH environment variable are always accepted as their session replacements. New prompts should use the session spellings. A record that was already stored still reads. The retired family keyword and session= together are an error. See feature_flags.

The clan name is permanently reserved as a container name and cannot also belong to an agent. Each member must be named <clan>.<suffix>; launch planning rejects an out-of-hood name before spawning it. A clan may contain ordinary agents, workflow steps, and sequential sessions whose names stay inside the same hood.

%wait:<clan> waits for every member of the newest clan generation. An exact member name targets only that member. Killing or dismissing the synthetic clan row cascades to its live members, while acting on one member leaves the rest of the clan alone.

Retrying a clan member from sase's TUI keeps it in the same named clan. sase's TUI rewrites the prompt into %id(<new-member-id>, clan=<clan>), where the member id carries the retry suffix. It does not repeat the create-only %clan declaration. If the original prompt used a clan template such as research.@ or research.{@1}, sase's TUI first substitutes the member's concrete clan, such as research.2.

sase's TUI renders every grouping row with a trailing color-coded name and no kind icon. A clan is synthetic and ends with an orchid <name> after its rolled-up status counts; its @tribe labels follow the name. A real multi-member session root ends with its bare azure container <name>, while its concrete member rows retain their exact --<suffix> names. Plain agent annotations and a lone plan proposer with only its display-only planner child remain gold. Press l once on a collapsed clan to reveal its direct members. The clan's outer fold is binary, so move to a session or workflow row and press l there to reveal that row's descendants. Lowercase h moves from any agent, Bash, Python, parallel, embedded, or compatibility workflow step to its validated immediate workflow, session, clan, or tribe parent without changing fold state. Uppercase H first retreats a selected open workflow or sequential-session agent node by one fold level. After that agent node is collapsed, the next press fully collapses every remaining open agent node in the next grouping scope, then collapses only the open canonical clan enclosing the selection. The next press from that collapsed clan container collapses every remaining open canonical clan in the group, and only then does a later press fall back to selected-row structural handling and grouping collapse. A selection without an open enclosing workflow, session, or clan proceeds directly to the group-wide remaining-agent-node sweep. Selecting the clan row shows an aggregate CLAN header and a navigable summary of every section represented across its members. In the Agents list, one-wave clans keep status priority — Failed, Stopped, Running/Starting, Queued, Waiting, Done — and then launch recency within a bucket. Layered clans use dependency wave order, with launch order breaking ties and unresolved members trailing. The CLAN MEMBERS jump-panel roster uses wave order for layered clans, matching the tree; one-wave clans keep chronological launch order. These numbers stay stable while statuses change. The runtime is the union of member run intervals, with human-wait windows excluded — including a gate turn's pending and settling window — so concurrent members are not double-counted. When a sequential session has a concrete agent or monitor turn currently executing, the collapsed and expanded session container row shows 🏃‍♂️ <current-turn-runtime> / <session-total-runtime> so the active turn duration is visible without opening the session. A clan container's live suffix collapses its parallel lanes with a minimum instead, since more than one lane can be live at once: <lowest-running-lane-runtime> / <clan-total-runtime>, where a sequential-session lane contributes its own total runtime -- the same value its own row shows to the right of its suffix.

Clan summary folding

Clan summaries collect member errors, output and workflow variables, replies, SASE context, slow tool calls, and prompts below the saved clan summary in the metadata body; the numbered CLAN MEMBERS roster lives in the jump panel, followed by CLAN NEIGHBORS when another clan shares its dotted hood. Two clans are neighbors when their presented names share the same root hood (foo, foo.bar, foo.baz, and foo.bar.deep share foo), compared case-insensitively at dot boundaries and excluding the selected clan, ordinary rows, malformed names, and unrelated prefixes such as foobar. Neighbor rows show full clan names and keep each target's stable identity including its generation. Known-empty section kinds are omitted. If required disk-backed content is not known yet, the document ends with one dim ⋯ scanning member data… tail instead of showing a placeholder for each section. Members and neighbors share one continuous number ladder up to 100 targets: 0–9 for documents with at most ten entries, or 00–99 for the first 100 entries in a larger document. Additional entries appear only in an unnumbered remainder count. Press a number while the clan is selected to expand only that member's ancestor chain and jump to its row, or to reveal a neighboring clan through folds and panels; with a clan member selected, the neighbor digit lands on its neighboring clan and back again, and Ctrl+O returns to the previous selection. Esc cancels a pending first digit. Use Ctrl+J and Ctrl+K to move between the visible section headings; numbered roster rows live in the jump panel, so they are neither Ctrl+J/Ctrl+K stops nor za/zA targets.

The summary has three session-only fold levels:

Level Clan summary content
1 Up to 100 numbered member and neighbor rows (in the jump panel) plus a heading and count for each represented section
2 Bounded triage digests, such as one-line error and reply previews, variable values, and context-lane summaries
3 Full section bodies grouped by member for detailed investigation

Press zz to cycle levels 1 → 2 → 3 → 1. Press zZ below level 3 to open every fold to level 3, or press it at level 3 to close every fold to level 1. Use z1-z3 to select an exact level. za cycles only the section at the top of the metadata viewport; zA toggles that section between collapsed and fully expanded. A valid panel-level cycle, extreme toggle, or direct selection clears these per-section overrides. The Fold: N/3 field in the CLAN header always shows the current panel level, while the ▸/▾/▼ heading glyph shows each section's effective level. Unknown disk-backed sections stay hidden during enrichment behind one scanning member data… tail; represented sections appear when known, and known-empty sections remain omitted. The compact roster and its numeric jumps remain available at every level.

The fold prefix is available only while the Agents tab is active. Press uppercase Z to zoom an agent row's detail panel or the selected tribe panel's metadata document, = to isolate or restore a tribe panel from whole-panel focus or a row selection inside a panel, and - to sweep every open agent node and clan (never a grouping banner) in the focused tribe panel closed in one press (or restore the last sweep) from whole-panel focus, a row selection, or merged layout; lowercase z starts fold mode. Fold state is panel-wide and applies when a clan or a sase agent is selected. Using a fold chord on a regular sase agent folds its NEIGHBORS and SLOW TOOL CALLS sections across that agent's own three-level scale, leaves its other sections unchanged, and updates the session state for the next container selection.

Epic bead-work example

sase bead work <epic-id> puts every phase worker and the final land agent in clan <epic-id> and tribe @epic. %wait(for_epic=) follows epics an agent launches and is unrelated to tribe @epic. For an epic named sase-<epic>, the generated prompt has this shape:

%id(!sase-<epic>.<phase>, bead=sase-<epic>.<phase>)
%clan(sase-<epic>, tribe=epic, summary_script=sase_clan_summary_epic)
#bd/work_phase_bead:sase-<epic>.<phase>
---
%id(!land, clan=sase-<epic>, bead=sase-<epic>)
%wait(sase-<epic>.<phase>, for_epic=false)
%wait(bead=sase-<epic>.<phase>)
#bd/land_epic:sase-<epic>

Generated phase and land waits use for_epic=false on their agent dependency and also require phase-bead closure. A user-authored %wait:worker normally follows any epics that worker launches, but this generated schedule already uses its phase beads as the completion condition. The clan container itself is not a land agent or other executable process. The bead= association lets each runner claim its phase or epic only after those waits and workspace preparation. If the epic clan already exists during a re-work, every phase and land segment uses the clan= join form. The built-in sase_clan_summary_epic script renders the epic's launch-time clan summary.

_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.