ssdavidai/alfred/alfred-chore-management

Claude Code and agent skill by ssdavidai/alfred

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Inspect, trigger, pause, and reason about Sir's scheduled chores. Chores are recurring Temporal workflows — some shipped as platform built-ins, some bespoke Python generated for Sir during onboarding. Use whenever Sir asks about what's running on a schedule, wants to manually trigger a chore, pause/unpause one, or check why a chore hasn't produced results.

Install

npx vskill@latest install ssdavidai/alfred/alfred-chore-management

SKILL.md

Alfred — Chore Management

Chores are Sir's recurring background workflows. They live in the vault as chore/ records and are paired with a Temporal schedule named chore-<slug>.

Two tiers — and why this matters before you diagnose

Every chore is one of two tiers. Telling them apart is the single most common place a fresh agent gets it wrong.

TierWhere the Python livesWho authored it/source endpoint returns
Standard libraryInside the alfred-learn Docker image at packages/learn/src/workflows/chores/<template>.py (in the alfred-platform repo)Platform team. Same code on every tenant.{generated: false, tier: "standard-library", builtin_path: "..."}
GeneratedPer-tenant file at /alfred-data/user-chores/<template>.pyOpus, during onboarding, against Sir's specific matters{generated: true, tier: "generated", source: "<full .py>"}

Today's standard-library templates: daily_morning_briefing, daily_evening_digest, subscription_watcher, weekly_matter_digest. Anything else is generated.

The morning brief and evening digest form a continuous narrative loop. The brief reads last night's digest as its starting point; the digest reads this morning's brief. Both persist their output as event/daily-brief-<date>.md and event/daily-digest-<date>.md so each can find the other. If Sir asks why the brief feels disconnected from yesterday, check whether the digest landed (event/daily-digest-<yesterday>.md should exist).
Warning — `generated: true` in frontmatter does NOT mean the chore is in the generated tier. When a template gets promoted from generated to standard-library, existing chore records keep their original generated: true flag. The /api/v1/chores/<slug>/source endpoint detects promoted templates and returns the right tier regardless of the flag. Trust the `tier` field in the response, not the frontmatter flag.
Warning — a 404 from `/api/v1/chores/<slug>/source` does NOT automatically mean the chore is broken. For standard-library chores the endpoint returns {generated: false, tier: "standard-library"} (200 OK) — the .py living outside /alfred-data/user-chores/ is the correct, healthy state. If you do see a 404 with tier: "generated-orphaned", cross-check before declaring breakage: read /api/v1/schedules/chore-<slug> for ActionCounts.Total and read the chore body's ## Run log section. If both show recent activity, the chore is running fine via a built-in workflow class — the platform's promotion list just needs updating; the chore itself is healthy.

Two layers to a chore

  1. Vault record (chore/<slug>.md) — the user-facing configuration: name, schedule, status, description, run log, quarantine state.
  2. Temporal schedule — the actual recurring invocation, registered with Temporal. Its ID is chore-<slug>.

Both layers must stay in sync. Use the self MCP tool to query both sides.

Endpoints for chore management

Read
  • `self endpoint="/api/v1/chores"` — list all chore records, with frontmatter (name, schedule, status, template, quarantine, last_run).
  • `self endpoint="/api/v1/chores/{slug}"` — read the full chore body, including run log and generated description.
  • `self endpoint="/api/v1/chores/{slug}/source"` — chore source + tier. For standard-library chores returns {tier: "standard-library", builtin_path, source: null}. For generated chores returns {tier: "generated", source: "<full .py>"}. For a missing-but-claimed-generated file returns 404 with tier: "generated-orphaned" and a hint about how to disambiguate.
  • `self endpoint="/api/v1/schedules"` — list all Temporal schedules. Cross-reference with vault records to spot orphans.
Act
  • `self endpoint="/api/v1/chores" method="POST"` — create a new chore from scratch. See "Creating a chore" below.
  • `self endpoint="/api/v1/chores/{slug}/trigger" method="POST"` — manually fire a chore once, out of cycle. Use when Sir says "run the cashflow forecast now".
  • `self endpoint="/api/v1/chores/{slug}/pause" method="POST"` — pause a chore (both vault record and Temporal schedule).
  • `self endpoint="/api/v1/chores/{slug}/resume" method="POST"` — resume a paused chore.
  • `self endpoint="/api/v1/chores/{slug}" method="DELETE"` — remove a chore and its schedule.

Creating a chore

When Sir asks for a new recurring job ("every morning, tell me what happened overnight", "every Friday summarize the week"), you are expected to design and install it yourself via POST /api/v1/chores.

Body shape
{
"slug": "daily-morning-briefing", // kebab-case, unique
"workflow_class_name": "DailyBriefingWorkflow",
"python_source": "<full .py file>",
"schedule": "30 4 * * *", // 5-field cron
"name": "Daily morning briefing", // optional
"user_facing_description": "...", // optional, shown in dashboard
"params": { "preview_only": false }, // optional; chore_slug is auto-added
"tags": ["morning", "digest"], // optional
"overlap_policy": "Skip", // optional, default "Skip"
"restart_worker": true // optional, default true
}

The endpoint writes three things atomically: the .py file under /alfred-data/user-chores/, the chore/<slug>.md vault record, and the Temporal schedule chore-<slug>. On failure it rolls back so you never end up with partial state.

The three primitives — compose these, don't reinvent

A chore workflow is plain Python plus calls to three generic activities. You don't write new activities per chore; you compose from these.

`call_self(endpoint, method="GET", body=None, query=None) -> dict` The same surface as the self MCP tool you use. List matters, read events, write vault records, trigger sessions, read schedules — anything ctrl-api does. method is "GET" by default; use "POST" / "PATCH" / "DELETE" for writes.

`call_composio(action, arguments=None) -> dict` Execute any Composio action: GMAIL_SEND_EMAIL, GOOGLECALENDAR_CREATE_EVENT, NOTION_CREATE_PAGE, GITHUB_CREATE_ISSUE, SLACK_POST_MESSAGE, 1000+ more. Check the per-app SKILL.md for action names and argument schemas.

`spawn_subagent(prompt, agent_id="learn-clerk", timeout_s=300) -> str` Fire-and-wait a subagent on openclaw-workers. Only use when the step genuinely needs LLM reasoning — filtering, formatting, aggregating, deduping all stay in the workflow's Python body where they're cheaper and deterministic. The default learn-clerk has self + composio tools in scope so the subagent can continue working autonomously within the one turn.

Rule of thumb: if the step's output can be described as "return this structured data", do it in plain Python with call_self / call_composio. If the step is "decide what matters" or "write this as a paragraph", use spawn_subagent. Put as little logic as possible inside subagents — they are 100× slower and 1000× more expensive than a ctrl-api call.

Legacy activities. The manifest also exposes fetch_financial_events, write_matter_digest_via_llm, ask_alfred_to_judge_anomalies, fetch_matter_events_last_week, save_digest_to_vault, send_chore_notification, and others. These are bespoke helpers kept around for existing generated chores. Do NOT use them for new chores — reach for the three primitives instead. Most bespoke LLM activities can be replaced by a well-prompted spawn_subagent; most bespoke data activities become a call_self + a filter loop. The exception is send_chore_notification, which is the sanctioned delivery path and should still be used for the final "deliver to Sir" step (it's also achievable via call_self to /api/v1/notifications, but the helper does the right formatting).

Fetch the full list with self endpoint="/api/v1/chore-actions" before writing Python. If you reference an activity not in the manifest, the validator will reject the source.

Writing the Python source

The validator enforces:

  1. Size < 100KB, valid Python syntax.
  2. Only imports from: __future__, dataclasses, datetime, typing, json, temporalio.workflow, temporalio.common, src.workflows.chores._base, src.activities.chore_actions.
  3. Module scope: only class defs, function defs, imports, one docstring, with workflow.unsafe.imports_passed_through():, and literal constants.
  4. Exactly one @workflow.defn class with exactly one @workflow.run method.
  5. No forbidden names: eval, exec, open, compile, __import__, getattr/setattr/delattr, globals/locals, vars, dir, breakpoint.
  6. No non-deterministic calls at workflow scope: datetime.now, random.*, uuid.*, time.time.
  7. Every workflow.execute_activity(<name>, ...) must reference a name imported from the two allowed modules.

Before submitting, sanity-check the chore_actions manifest: self endpoint="/api/v1/chore-actions" returns every activity name you're allowed to import plus its reads/writes/llm metadata. If the activity you need isn't in the manifest, you can't use it — fall back to a subagent step or propose a new activity to Sir for platform work.

Example: "every Friday, summarise my week and email me"
python
"""Every Friday 17:00 — weekly summary email."""
from __future__ import annotations
from datetime import timedelta
from temporalio import workflow
with workflow.unsafe.imports_passed_through():
from src.activities.chore_actions import call_self, call_composio, spawn_subagent
@workflow.defn
class WeeklySummaryEmailWorkflow:
@workflow.run
async def run(self, chore_slug: str) -> dict:
# 1. Structured pull — plain Python, cheap.
matters = await workflow.execute_activity(
call_self,
args=["/api/v1/vault/list/matter", "GET"],
start_to_close_timeout=timedelta(seconds=30),
)
events = await workflow.execute_activity(
call_self,
args=["/api/v1/streams/events", "GET", None, {"limit": "200"}],
start_to_close_timeout=timedelta(seconds=30),
)
# 2. LLM reasoning — single subagent call, returns prose.
body = await workflow.execute_activity(
spawn_subagent,
args=[
f"Summarise this week for Sir. Matters: {matters}. "
f"Recent events: {events}. Write 4-5 crisp bullets plus one "
f"headline sentence. Butler tone. Reply with email body only.",
"learn-clerk",
300,
],
start_to_close_timeout=timedelta(minutes=6),
)
# 3. Delivery — Composio for the actual email send.
await workflow.execute_activity(
call_composio,
args=[
"GMAIL_SEND_EMAIL",
{
"recipient_email": "sir@example.com",
"subject": "Weekly summary",
"body": body,
},
],
start_to_close_timeout=timedelta(seconds=60),
)
return {"delivered": True, "bytes": len(body)}

Three activity calls. All three are generic. No bespoke fetch_weekly_events or write_weekly_summary_via_llm needed.

After creation

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/ssdavidai/alfred/alfred-chore-management/badge)](https://verified-skill.com/skills/ssdavidai/alfred/alfred-chore-management)

Something wrong with this skill or its page? Tell us.