# Fulcra Agent Roles

> Add durable roles to a fulcra-agent-teams space: agents claim leases on named roles (reviewer, maintainer, on-call), liveness is tracked, and a role left vacant past its SLA escalates to its maintainer.

- **Type:** Skill
- **Install:** `agentstack add skill-ashfulcra-fulcra-tools-fulcra-agent-roles`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ashfulcra](https://agentstack.voostack.com/s/ashfulcra)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ashfulcra](https://github.com/ashfulcra)
- **Source:** https://github.com/ashfulcra/fulcra-tools/tree/main/skills/fulcra-agent-roles
- **Website:** https://fulcradynamics.com

## Install

```sh
agentstack add skill-ashfulcra-fulcra-tools-fulcra-agent-roles
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Fulcra Agent Roles

Enhances the [`fulcra-agent-teams`](https://github.com/fulcradynamics/agent-skills) skill. A team's
`member//role.md` says what a *member* does, but teams has no notion of a **durable role** that
outlives any one session — "who is the reviewer right now?", "is anyone on-call?", "this role has been
unattended too long." This skill adds that, as a pure OKF-markdown convention over the team namespace
(lease mechanics via `coord-engine roles` verbs; everything else plain `fulcra-api file` + the OKF
standard).

## Where to start — the re-entrancy probes

Before claiming, escalating, or reading a role's status, probe where this role stands. Enter at the
**first probe that fails** (per the repo's skill-quality pattern, `docs/skill-quality-pattern.md`);
`roles claim` is a refresh, so re-entry is always safe:

| Probe (run in order) | Command | Passes when | If it fails, enter at |
|---|---|---|---|
| Role doc registered? | `fulcra-api file download team//roles/.md -` | prints a doc with `type: Role` (NON-mutating — do not probe registration via `roles claim`: claiming writes your lease before you've read the prior one, destroying same-id takeover evidence per "Role-as-identity" below) | **Establish a role** — write `roles/.md` (`type: Role` + policy/SLA/maintainer) |
| Lease held by you? | `coord-engine roles status  ` | prints `role  in team/: HELD` and your agent id is among the `fresh holders:` line | **Claim / hold** — run `roles claim  ` to write/refresh your lease shard |
| Today's escalation clear? | `fulcra-api file download team//roles//escalations/$(date -u +%Y-%m-%d).md -` | download FAILS (no marker) — the role is not sitting escalated today | **Escalate a vacancy** — a marker present means a vacancy already fired today; drain it per "Escalate a vacancy" below |

All probes pass → the role is registered, you hold a fresh lease, and no vacancy escalation is
outstanding for today; just re-claim on your cadence to keep the lease fresh.

## Concepts
- **Role** — a named, durable function in the team (e.g. `reviewer`, `maintainer`, `on-call`). Defined
  once; sessions come and go.
- **Lease** — an agent's claim on a role, refreshed to prove liveness. A role is *held* while a fresh
  lease exists.
- **Policy** — `shared` (many holders allowed) or `exclusive` (one holder; a second fresh lease is a
  contention signal).
- **SLA / escalation** — if a role sits vacant longer than `sla_hours`, its `maintainer` is notified.

## Layout (under `team//roles/`)
- **`roles/.md`** — the role registry doc. OKF `type: Role`. Created once when the role is
  established. Frontmatter carries the policy and SLA:
  ```yaml
  ---
  type: Role
  title: Reviewer
  description: Adversarial code/plan review for the team's PRs.
  policy: shared            # shared | exclusive
  sla_hours: 24             # vacancy longer than this escalates
  maintainer: ash           # who gets the escalation (an agent or member name)
  ---
  # Duties
  - Pick up review requests from the team inbox…
  ```
- **`roles//leases/-.md`** — one lease per holder, named by the engine from the
  holder's id (`agent_key`); never hand-name lease files. OKF `type: Lease`. The `timestamp` is the
  liveness signal — **refresh it** (re-claim) each time you act in the role:
  ```yaml
  ---
  type: Lease
  title: reviewer lease — treecle
  agent: treecle
  timestamp: 2026-07-01T18:00:00Z
  ---
  Holding the reviewer role. Next: drain the review inbox.
  ```
- **`roles//escalations/.md`** — a first-writer-wins daily marker so a vacant role
  escalates at most once per day (avoids spamming the maintainer).

## Lifecycle

### Establish a role (once)
Write `roles/.md` with `type: Role` + policy/SLA/maintainer — complete worked
examples (exclusive maintainer, shared reviewer, shared multi-host monitor) are in
[`examples/`](examples/). A `roles/index.md` is optional human courtesy: the engine
folds role status from the directory listing, and the reference deployment does not
maintain one.

**Parking a role for a successor? The role doc is YOUR job, not theirs.** A role
can be claimed and worked without `roles/.md` ever being written — `claim`
only warns — but the successor then inherits a role with broken review
role-routing and status folds falling back to fabricated defaults (observed live
2026-07-22: a handed-off role was registered on the bus with no role doc, and
the successor's claim landed on the warning). Before you park (see
fulcra-agent-continuity, "Parking for a successor"), run the first re-entrancy
probe above; if the doc is missing, write it — duties, policy, SLA, maintainer,
and the `checkpoint_ref` your park will set — so the successor claims into a
fully-routed role.

### Claim / hold
`coord-engine roles claim  ` writes your lease shard (engine-named `-.md`;
the command echoes the filename). **Re-run it** whenever you do work in the role — the refreshed
`timestamp` is what keeps the role "held". Never hand-upload a lease file: a hand-named shard makes a
SECOND lease for your id (spurious CONTESTED on exclusive roles). The Fulcra File Store versions every
write, so the lease's history is an audit trail of your tenure.

### Release
`coord-engine roles release  ` deletes your engine-named shard. (Deletion is intentional
and not undoable — correct for releasing.)

### Determine role status (the fold) — **use the engine, do not eyeball timestamps**
Classifying a role from many lease files is a *fold* over derived state: two agents must AGREE on
whether a role is vacant before one escalates. Eyeballing timestamps drifts (the exact failure coord
exists to prevent), so this is a deterministic **`coord-engine`** command, not a prose instruction:
```bash
coord-engine roles status   --json
```
It reads the role's `policy`/`sla_hours`, folds the leases, and returns:
- `status` — **HELD** (≥1 fresh lease) / **VACANT** (none) / **DORMANT** (vacant but deliberately parked, see "Park a role") / **CONTESTED** (`exclusive` + ≥2 fresh) / **UNKNOWN** (unreadable),
- `fresh_holders`, and `escalation_due` (true iff vacant past SLA, not parked, and today's marker isn't present).

For **CONTESTED**, resolve by having all but one holder release.

### Role-as-identity (recommended)
When a session exists to serve one role, use the role name AS its agent identity
(`FULCRA_COORD_AGENT=coord-maintainer`) — see fulcra-agent-presence's "Pick your identity by ROLE"
section. Claim the role's lease while you act as it. Know what each guard does and does not catch:

- **Different ids claiming an exclusive role** (e.g. `coord-maintainer` and a stray
  `claude-code:host:repo`): two FRESH lease shards (within `sla_hours`) → `roles status` reports
  **CONTESTED**. A stale stray shard yields HELD, not CONTESTED. Detected.
- **Two sessions under the SAME id string**: they write the SAME lease shard (shard names derive from
  the id), so leases alone CANNOT see this — last write silently wins. Since the
  session-nonce verify was shipped, the engine detects this automatically: every `roles claim`
  writes a session nonce into the lease and compares on refresh — a foreign nonce prints a loud
  stderr WARNING ("nonce mismatch ... same-id double-acting"), and claiming with no local state over
  an existing shard prints a takeover note. Heed those. The manual fallback, in this order at the start
  of every work burst: (1) `roles status   --json` — proceed only if VACANT or the sole
  holder is your id; (2) read your lease shard raw (`fulcra-api file download
  team//roles//leases/.md` — learn your `` by listing the leases
  dir, or from `presence beat` output, which prints the same key) and compare its `timestamp` to when YOU last
  claimed — a fresher timestamp you did not write means another session is acting under your id;
  (3) only then re-claim to refresh. Re-claiming FIRST destroys that evidence.

Multi-host variants (`coord-maintainer@host1`, `@host2`) are acceptable when one role legitimately
runs in several places — each host claims the SAME role (`roles claim  coord-maintainer --agent
coord-maintainer@host1`), never a role named after the variant. Such a role needs `policy: shared`:
on `exclusive` it would sit in permanent CONTESTED by construction — and note `shared` trades away
the CONTESTED collision guard for that role. Keep the role doc's `maintainer:` field
a distinct SUPERVISING identity (e.g. `maintainer: ash`): vacancy escalations are assigned to that
field, so pointing it at the role itself mails the alert to the very inbox that just went dark.

### Escalate a vacancy — engine decides, you act
The engine already computed `escalation_due` above. When it is **true**, perform the single-file actions
(these are reliable as prose):
1. Write today's dedupe marker `roles//escalations/.md` (first-writer-wins).
2. Drop a message into the maintainer's inbox
   (`team//member//inbox/__role-vacant-.md`) per the
   `fulcra-agent-teams` inbox lifecycle, stating which role is vacant and for how long.

### Park a role (dormancy)
To deliberately leave a role unattended without alarming — a reviewer on leave, a
seasonal on-call — set `dormant_until: ` in the role doc's frontmatter (e.g.
`dormant_until: 2026-08-05T09:00:00Z`). While that timestamp is in the future the ENGINE
treats the role as **DORMANT**: `roles status` prints `DORMANT (until )` instead of
VACANT and the mechanical `escalate` sweep suppresses its vacancy escalation on every
heartbeat host — no agent-side convention required. Escalation resumes automatically once
the date passes (past-or-absent `dormant_until` = normal behavior); a live lease outranks
the park (a held-and-dormant role still shows HELD). An unparseable `dormant_until` fails
OPEN — it is treated as absent, a stderr note is printed, and escalation still fires — so a
typo can never silently mute a role. Unpark early by deleting the field.

## When to use
- Establishing "someone owns X" in a team without pinning it to one session.
- Routing work by role ("the reviewer") instead of by name.
- Making sure a critical function (on-call, maintainer) is never silently unattended.

## Efficiency (per the teams OKF directive)
If you keep a `roles/index.md` (optional — see "Establish a role"), do **not** index every lease or escalation marker — describe the
`leases/` and `escalations/` directories as a whole. Keep the team `log.md` for role *creation* and
*handoff* milestones, not every lease refresh.

See [`references/roles-cli.md`](references/roles-cli.md) for exact commands.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ashfulcra](https://github.com/ashfulcra)
- **Source:** [ashfulcra/fulcra-tools](https://github.com/ashfulcra/fulcra-tools)
- **License:** MIT
- **Homepage:** https://fulcradynamics.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-ashfulcra-fulcra-tools-fulcra-agent-roles
- Seller: https://agentstack.voostack.com/s/ashfulcra
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
