obligations
Skill3 starsChanged 10 days agoMITView on GitHub
Create, view, cancel, and evaluate cross-session obligations. Use when the user types /obligations, says 'remind me', 'watch this', 'nudge me', 'track this', 'show my obligations', 'cancel obligation', or asks to set up a recurring check on a PR or Jira ticket.
In this repository36 other files it gives its agents.
- agent-statsSkill
- aristotle-deconstructorSkill
- babysit-prsSkill
- briefingSkill
- code-auditorSkill
- core-editSkill
- create-jira-ticketSkill
- create-tech-specSkill
- cyrus-tdd-engineerSkill
- doctorSkill
- dotfiles-syncSkill
- forgeSkill
- google-docsSkill
- google-driveSkill
- grill-meSkill
- handoffSkill
- lessons-reviewSkill
- mermaid-diagramsSkill
- metrics-emitSkill
- optimus-plannerSkill
- overlay-initSkill
- performance-reviewSkill
- pr-create-from-commitsSkill
- ranger-reviewerSkill
- record-demoSkill
- review-contextSkill
- review-respondSkill
- scout-reviewerSkill
- self-evaluateSkill
- smart-compactSkill
- smart-statuslineSkill
- swarm-retroSkill
- team-leadSkill
- ticket-pickupSkill
- ticket-swarmSkill
- to-prdSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 3 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 68
When the user asks to create an obligation in natural language:
- Distinctive: line 143
When the user asks to see obligations ("show my obligations", "what am I tracking", "obligations list"):
- Very specific: line 161
If no active obligations exist, show: No active obligations.
- Distinctive: line 167
When the user asks to cancel ("cancel obligation 1", "remove the PR #482 reminder", "cancel all"):
- Very specific: line 186
If last_fired is set and last_fired + cooldown > now → skip (still cooling down)
- Very specific: line 189
If another obligation with the same dedup_key exists with a newer created date → skip this one (superseded)
- Very specific: line 204
After firing, check if the inverse condition is now met (e.g., PR was merged, ticket is Done) → set status: resolved
- Very specific: line 208
Error handling: If a condition query fails (MCP unavailable, gh error), skip that obligation and note: Could not evaluate obligation …
- Very specific: line 218
Leave obligations.yaml in place even if only archived items remain (preserves audit trail)
- Very specific: line 254
If the user declines the paired obligation, /handoff writes the handoff artifact and exits without creating any obligation.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Overview
Obligations are commitments the system makes to check a condition and take an action across session boundaries. Unlike in-session reminders, obligations persist in ~/.claude/tasks/<project>/obligations.yaml and survive conversation resets. The /briefing skill evaluates all active obligations at session start; this skill manages their full lifecycle — create, view, cancel, evaluate, and garbage-collect.
Resolve <project> as: basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
Data Model
| Field | Type | Description |
id | string | Unique ID: ob-YYYYMMDD-NNN |
name | string | Human-readable name (e.g., nudge-reviewer-pr-482) |
created | ISO 8601 | When the obligation was created |
created_by | string | user, policy, or briefing |
condition.type | string | gh_query, jql, compound, time |
condition.query | string | The query to execute |
condition.predicate | string | The condition to check against query results |
action.type | string | notify, invoke_skill, approve_then_invoke |
action.message | string | Message to display when condition is met |
gate | string | autonomous, notify, approve |
expiry.type | string | date, condition, count |
expiry.value | string | Expiry value (ISO date, condition expression, or integer count) |
cooldown | string | Duration string (e.g., 24h, 1h) |
max_fires | integer or null | Max times this obligation can fire; null = unlimited |
fired_count | integer | How many times it has fired |
last_fired | ISO 8601 or null | Last fire timestamp |
status | string | active, expired, resolved, cancelled |
dedup_key | string or null | Only the newest obligation with this key fires |
Fully populated example:
- id: "ob-20260413-001"
name: "check-pr-482-review"
created: "2026-04-13T09:00:00"
created_by: "user"
condition:
type: "gh_query"
query: "gh pr view 482 --json reviewDecision"
predicate: "reviewDecision == 'REVIEW_REQUIRED' OR reviewDecision is null"
action:
type: "notify"
message: "PR #482 still waiting for review. Nudge reviewer?"
gate: "notify"
expiry:
type: "date"
value: "2026-04-16T00:00:00"
cooldown: "24h"
max_fires: 3
fired_count: 0
last_fired: null
status: "active"
dedup_key: "pr-482-review-check"Creating Obligations
When the user asks to create an obligation in natural language:
- Parse the intent — identify the target (PR, Jira ticket, generic), the condition (status check, review state, time-based), the action (notify, suggest), and timing constraints.
- Generate the obligation struct with sensible defaults:
gate: "notify"— show the user, don't auto-actcooldown: "24h"— avoid spammingmax_fires: 3— auto-expire after 3 notificationsexpiry: { type: "date", value: "+3d" }— 3 days from now (resolve to absolute ISO datetime)status: "active",fired_count: 0,last_fired: null
- Resolve the file path —
~/.claude/tasks/<project>/obligations.yaml - Read existing file (if present) — load current obligation list to check for existing IDs
- Generate a unique ID —
ob-YYYYMMDD-NNNwhere NNN is the next sequential integer for today (001, 002, …) - Append the new obligation and write the file back
- Confirm — show the created obligation with its ID, condition summary, expiry, and cooldown
Examples
"Remind me to check PR #482 tomorrow if it hasn't been reviewed"
- id: "ob-20260413-001"
name: "check-pr-482-review"
condition:
type: "gh_query"
query: "gh pr view 482 --json reviewDecision"
predicate: "reviewDecision == 'REVIEW_REQUIRED' OR reviewDecision is null"
action:
type: "notify"
message: "PR #482 still waiting for review. Nudge reviewer?"
gate: "notify"
expiry: { type: "date", value: "2026-04-16T00:00:00" }
cooldown: "24h"
max_fires: 3"Watch PROJ-1234 and tell me when it's Done"
- id: "ob-20260413-002"
name: "watch-proj-1234-done"
condition:
type: "jql"
query: "key = PROJ-1234 AND status = 'Done'"
predicate: "result_count > 0"
action:
type: "notify"
message: "PROJ-1234 has been moved to Done!"
gate: "notify"
expiry: { type: "date", value: "2026-04-20T00:00:00" }
cooldown: "1h"
max_fires: 1"Nudge me daily if my sprint has unstarted tickets"
- id: "ob-20260413-003"
name: "sprint-unstarted-nudge"
condition:
type: "jql"
query: "assignee = currentUser() AND sprint in openSprints() AND status = 'To Do'"
predicate: "result_count > 0"
action:
type: "notify"
message: "{count} sprint tickets still in To Do. Consider swarming them."
gate: "notify"
expiry: { type: "condition", value: "result_count == 0" }
cooldown: "24h"
max_fires: nullViewing Obligations
When the user asks to see obligations ("show my obligations", "what am I tracking", "obligations list"):
- Resolve the file path and read
obligations.yaml - Filter to
status: activeonly - Render in this format:
Active obligations (2):
1. [ob-20260413-001] Check PR #482 review status — next eval: tomorrow (0/3 fired)
Condition: PR #482 reviewDecision == REVIEW_REQUIRED
2. [ob-20260413-002] Watch PROJ-1234 for Done — next eval: next briefing (1/1 fired)
Condition: PROJ-1234 status == Done
-> cancel 1 = Remove obligation 1
-> cancel all = Remove all obligations
-> check = Evaluate all obligations nowIf no active obligations exist, show: No active obligations.
Cancelling Obligations
When the user asks to cancel ("cancel obligation 1", "remove the PR #482 reminder", "cancel all"):
- Resolve the target — by index number (from view list), by ID (
ob-20260413-001), by name fragment (pr-482), or the keywordall - Set
status: cancelledon the matched obligation(s) — do not delete, keep for audit trail - Write the updated file
- Confirm — "Cancelled obligation ob-20260413-001 (check-pr-482-review)."
Evaluating Obligations
The evaluation loop is called by both /obligations check and the /briefing skill. For each obligation where status == active:
- Check expiry:
type: date— ifexpiry.value< now → setstatus: expired, skiptype: count— iffired_count >= max_fires→ setstatus: expired, skiptype: condition— evaluate the expiry expression; if met → setstatus: resolved, skip
- Check cooldown:
- If
last_firedis set andlast_fired + cooldown > now→ skip (still cooling down)
- If
- Check dedup:
- If another obligation with the same
dedup_keyexists with a newercreateddate → skip this one (superseded)
- If another obligation with the same
- Evaluate the condition:
gh_query— run theghCLI command, parse JSON output, check predicate against resultjql— run via MCP: primarymcp__claude_ai_Atlassian__searchJiraIssuesUsingJql<!-- BEGIN OVERLAY-FRAGMENT: obligations-jql-fallback-tool --><!-- END OVERLAY-FRAGMENT: obligations-jql-fallback-tool -->; check predicate againstresult_count. For company-specific Jira fallback tool name, consult## Jira fallbackin~/.claude/overlay-context.md. If that file is absent, proceed with the primary tool only.compound— evaluate all sub-conditions; all must be true for the obligation to firetime— check ifcondition.after< now
- If condition is met:
gate: notify→ displayaction.messageinlinegate: approve→ display message and ask for confirmation before invoking any actiongate: autonomous→ invoke the action immediately without prompting- Increment
fired_count, setlast_firedto now
- Staleness check:
- After firing, check if the inverse condition is now met (e.g., PR was merged, ticket is Done) → set
status: resolved
- After firing, check if the inverse condition is now met (e.g., PR was merged, ticket is Done) → set
- Write back — persist updated
fired_count,last_fired, andstatusto the YAML file
Error handling: If a condition query fails (MCP unavailable, gh error), skip that obligation and note: Could not evaluate obligation {name}: {error}. Do not mark it expired or resolved on failure.
Garbage Collection
On each evaluation pass and when /obligations is invoked directly:
- Identify obligations with
statusin (expired,resolved,cancelled) that are older than 7 days (created< now - 7d) - Move them to an
archivelist at the bottom ofobligations.yaml— store onlyidandname, not the full struct - Leave
obligations.yamlin place even if only archived items remain (preserves audit trail)
archive:
- id: "ob-20260406-001"
name: "check-pr-475-review"Paired handoff obligation
The /handoff skill optionally creates a paired obligation as part of its
write flow. The obligation is a normal entry written via the standard
create flow (above) — there is no special call path. A typical paired
obligation watches the PR or branch for external motion:
- id: ob-YYYYMMDD-NNN
name: handoff-watch-<task-key>
created_by: handoff
condition:
type: gh_query
query: gh pr list --head <task-key> --json reviewDecision,state
predicate: state == "MERGED" OR reviewDecision == "APPROVED" OR reviewDecision == "CHANGES_REQUESTED"
action:
type: notify
message: "Open handoff for <task-key> — external motion detected. Resume the session?"
gate: notify
expiry:
type: date
value: "+14d"
cooldown: 24h
max_fires: 3If the user declines the paired obligation, /handoff writes the handoff
artifact and exits without creating any obligation. The pairing is
strictly opt-in.
Future Enhancement — RemoteTrigger
This section documents a planned Phase 2 integration. Do not implement it yet.
When an obligation needs to fire outside of active sessions, a corresponding RemoteTrigger can be registered alongside it. The RemoteTrigger's prompt would re-invoke /obligations check for that specific obligation ID. On trigger execution, the obligation condition is re-evaluated before any action is taken — the obligation file remains the source of truth; the RemoteTrigger is only the alarm clock. If the obligation has been cancelled or expired before the trigger fires, evaluation exits cleanly with no action. This pattern decouples the scheduling mechanism from the obligation logic and keeps the YAML file authoritative.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

