workflow-patterns
Frappe Workflow design — multi-state document lifecycles, role-based transitions, state-scoped field permissions, workflow handlers, and the team's pragmatic deviations (Custom Field for workflow_state, db_set over apply_workflow, custom workflow DocTypes). Use when designing or modifying an approval flow, a state machine on a DocType, or anything involving Workflow / Workflow State / Workflow Transition.
What this skill does
# Frappe Workflow Patterns
Reference for designing Frappe workflows, aligned with the team's edu_quality conventions: `workflow_state` added via Custom Field in the customizations sub-app (not source DocType edits), pragmatic use of `db_set` over `apply_workflow` for programmatic transitions, and willingness to build a custom DocType + Page when stock Workflow doesn't fit.
## Team conventions
These are the rules the existing codebase follows. New workflows should follow them; deviations need a reason.
1. **Add `workflow_state` via Custom Field** in `sc_customizations` (or equivalent customizations sub-app), not by editing the source DocType JSON. Keeps customizations isolated from upstream apps.
2. **`Custom Field` JSON for `workflow_state`** uses these settings: `fieldtype: "Link"`, `options: "Workflow State"`, `default: "Approved"` (or your default state), `hidden: 1`, `no_copy: 1`, `allow_on_submit: 1`. See the example in `sc_customizations/custom_field/Fees-workflow_state.json`.
3. **Use `db_set("workflow_state", "X")` for programmatic transitions** in most code paths. Pragmatic — bypasses transition validation, runs no `condition`, fires no email — but matches what the codebase does today (e.g. `refund_request.py:208`, `scan_receipts.py:17`). Use `apply_workflow` only when you specifically want the role check + condition + notification.
4. **Always wrap `db_set` calls with `hasattr(doc, "workflow_state")`** as a defensive check. The Custom Field may not be applied in every site; the code should still work.
5. **When stock Workflow doesn't fit, build a custom workflow DocType** plus a Frappe Page for the UI. We have precedent: `Funnel Workflow` for student application funnel, `fee_workflow` Page for fee operations. Don't try to bend Workflow to fit funnels or multi-doc orchestration.
## When to use a stock Workflow
A stock Frappe Workflow is the right tool when **all** of these are true:
- The document moves through a fixed sequence of named states (Draft → Pending → Approved → ...).
- Different roles can move it between specific pairs of states.
- The "current state" is meaningful in the UI and reports.
Use a plain `Select` status field instead when:
- States are advisory rather than gating.
- Transitions don't need permission checks.
- You don't care who moved it from one state to another.
Build a **custom workflow DocType + Page** when:
- The workflow involves multiple linked documents (a funnel through Application → Enrollment → Fee Setup → Payment).
- The state transitions need a richer UI than the standard "Action" buttons (drag-and-drop, multi-select bulk actions, custom validation per stage).
- You need to track state changes as their own queryable records.
Don't use a Workflow alongside `is_submittable: 1` unless you understand both will run — the workflow controls visible state, but Frappe still tracks `docstatus` (0 / 1 / 2) underneath.
## Adding workflow_state via Custom Field
Create a Custom Field JSON in the customizations sub-app:
```json
{
"doctype": "Custom Field",
"name": "Fees-workflow_state",
"dt": "Fees",
"fieldname": "workflow_state",
"label": "Workflow State",
"fieldtype": "Link",
"options": "Workflow State",
"default": "Approved",
"hidden": 1,
"no_copy": 1,
"allow_on_submit": 1,
"module": "Sc Customizations",
"creation": "2023-10-03 17:26:45.823538",
"modified": "2023-10-03 07:22:09.967376",
"modified_by": "Administrator"
}
```
File location: `sc_customizations/custom_field/{DocType}-workflow_state.json`. Register via fixtures in the customizations sub-app's `hooks.py`:
```python
fixtures = [
{"dt": "Custom Field", "filters": [["module", "=", "Sc Customizations"]]}
]
```
**Why hidden by default:** the workflow state is shown by the workflow timeline UI; the field itself doesn't need to be visible on the form.
**Why `no_copy: 1`:** a copied document should start at the default state, not inherit the source's state.
**Why `allow_on_submit: 1`:** the field can be updated on submitted docs (otherwise approval transitions on a submitted doc would fail).
## Workflow anatomy
Workflows themselves are DocTypes — `Workflow`, `Workflow State`, `Workflow Action Master`, `Workflow Transition`. The Workflow doc lives in the database (created via the desk), exported with:
```bash
bench --site <site> export-fixtures
```
A workflow has these key fields:
| Field | What it does |
|-------|--------------|
| `document_type` | The DocType this workflow applies to |
| `workflow_state_field` | Fieldname holding the current state — the team uses `workflow_state` consistently |
| `is_active` | `1` to enable. Only one active workflow per DocType. |
| `send_email_alert` | `1` sends notifications to the next actor on transition |
| `states` | Child table of `Workflow Document State` |
| `transitions` | Child table of `Workflow Transition` |
### States — `Workflow Document State`
```json
{
"state": "Pending Approval",
"doc_status": "0",
"allow_edit": "Manager",
"update_field": "status",
"update_value": "Pending"
}
```
| Field | Use |
|-------|-----|
| `state` | Name of the state — must match a `Workflow State` row (define those first) |
| `doc_status` | `"0"` (draft), `"1"` (submitted), `"2"` (cancelled) |
| `allow_edit` | Role allowed to edit fields while in this state. Other roles see read-only. |
| `update_field` / `update_value` | Optional: when the doc enters this state, set this field on the doc to this value. Useful for keeping a denormalized `status` field in sync with the workflow. |
### Transitions — `Workflow Transition`
```json
{
"state": "Draft",
"action": "Submit for Approval",
"next_state": "Pending Approval",
"allowed": "Employee",
"condition": "doc.amount > 0",
"allow_self_approval": 0
}
```
| Field | Use |
|-------|-----|
| `state` | Source state |
| `action` | Button label the user clicks |
| `next_state` | Target state |
| `allowed` | Role(s) that can perform this transition. Multiple roles → comma-separated. |
| `condition` | Optional Python expression evaluated with `doc` in scope. Empty = always allowed. |
| `allow_self_approval` | If `0`, blocks the user who created/last-edited the doc from performing this transition. **Always set to 0 on approval transitions.** |
`condition` runs in a restricted context. Keep it simple: `doc.amount > 1000`, `doc.school == "Pune"`. Function calls are limited; complex logic belongs in `validate()`.
## State design rules
1. **Terminal states are idempotent.** "Approved" and "Rejected" should have no outgoing transitions.
2. **Sequential states should be linearly ordered in the `states[]` table.** The order is what the timeline UI shows.
3. **Avoid more than 7 states** — past that, users get lost. Split into two workflows on related DocTypes if needed.
4. **Match `doc_status` to lifecycle.** Submitted (`"1"`) means the doc is locked from edits except `on_update_after_submit`. Cancelled (`"2"`) means archived.
5. **Default state must be reachable on insert.** Frappe sets `workflow_state` to the first state in the table on new docs. The Custom Field `default` should match.
## Programmatic transitions: `db_set` vs `apply_workflow`
There are two ways to move a doc between states programmatically. The team predominantly uses `db_set`.
### `db_set` (team default)
```python
# refund_request.py — pattern from edu_quality
bank_account.save(ignore_permissions=True)
if hasattr(bank_account, "workflow_state"):
bank_account.db_set("workflow_state", "Approved")
```
```python
# scan_receipts.py — pattern from edu_quality
receipt_doc.workflow_state = "Received"
receipt_doc.save(ignore_permissions=True)
```
What this does:
- Sets the field directly in the DB (or in memory + on next save).
- Skips workflow validation: no `condition` check, no role check, no notification.
- Doesn't fire `update_field` / `update_value` from the state definition.
- Fast, predictable, but bypasses the audit tRelated in Design
contribute
IncludedLocal-only OSS contribution command center. Auto-refreshes the user's in-flight PR and issue state on invoke so conversations start with full context — no need to brief Claude on what's in flight. Helps the user find issues to contribute to on GitHub, builds per-repo dossiers of what each upstream expects (CLA, DCO, branch convention, AI policy, draft-first, review bots, issue templates), runs deterministic gates before any external action so AI-assisted contributions don't reach maintainers as slop. State is markdown-only: candidate files at ~/.contribute-system/candidates/, repo dossiers at ~/.contribute-system/research/, append-only event log at ~/.contribute-system/log.jsonl. No database, no cloud calls. Use when the user asks about their PRs / issues / contributions, wants to find new work to take on, claim an issue, build/refresh a repo's dossier, or draft a Design Issue or PR. Trigger with "/contribute", "what's my PR status", "find a contribution", "claim issue X", "draft a Design Issue for Y", "refresh dossier for Z".
architectural-analysis
IncludedUser-triggered deep architectural analysis of a codebase or scoped subtree across eight modes — information architecture, data flow, integration points, UI surfaces, interaction patterns, data model, control flow, and failure modes. This skill should be used when the user asks to "diagram this codebase," "map the architecture," "show the data flow," "give me an ERD," "trace control flow," "find the integration points," "verify the layout pattern," "audit the UX architecture," or any similar request whose primary deliverable is mermaid diagrams plus cited reports under docs/architecture/. Dispatches haiku/sonnet sub-agents in parallel for per-mode exploration, then verifies every citation mechanically before any node lands in a diagram. Not for one-off prose explanations of code (use code-explanation) or for high-level system design from scratch (use system-design).
mcp
IncludedModel Context Protocol (MCP) server development and tool management. Languages: Python, TypeScript. Capabilities: build MCP servers, integrate external APIs, discover/execute MCP tools, manage multi-server configs, design agent-centric tools. Actions: create, build, integrate, discover, execute, configure MCP servers/tools. Keywords: MCP, Model Context Protocol, MCP server, MCP tool, stdio transport, SSE transport, tool discovery, resource provider, prompt template, external API integration, Gemini CLI MCP, Claude MCP, agent tools, tool execution, server config. Use when: building MCP servers, integrating external APIs as MCP tools, discovering available MCP tools, executing MCP capabilities, configuring multi-server setups, designing tools for AI agents.
react-native-skia
IncludedDesign, build, debug, and optimise high-polish animated graphics in React Native or Expo using @shopify/react-native-skia, Reanimated, and Gesture Handler. Use when the user wants canvas-driven UI, shaders, paths, rich text, image filters, sprite fields, Skottie, video frames, snapshots, web CanvasKit setup, or performance tuning for custom motion-heavy elements such as loaders, hero art, cards, charts, progress indicators, particle systems, or gesture-driven surfaces. Also use when the user asks for fluid, glow, glass, blob, parallax, 60fps/120fps, or GPU-friendly animated effects in React Native, even if they do not explicitly say "Skia". Do not use for ordinary form/layout work with standard views.
plaid
IncludedProduct Led AI Development — guides founders from idea to launched product. Six capabilities: Idea (discover a product idea), Validate (pressure-test the idea against fatal flaws, problem reality, competition, and 2-week MVP feasibility), Plan (vision intake + document generation), Design (translate image references into a design.md spec), Launch (go-to-market strategy), and Build (roadmap execution). Use when someone says "PLAID", "plaid idea", "help me find an idea", "product idea", "idea from my business", "idea from my expertise", "plaid validate", "validate my idea", "pressure-test", "is this idea good", "find fatal flaws", "validate the problem", "plan a product", "define my vision", "generate a PRD", "product strategy", "plaid design", "design from image", "translate image to design", "create design.md", "extract design tokens", "plaid launch", "go-to-market", "launch plan", "GTM strategy", "launch playbook", "plaid build", "build the app", "start building", or "execute the roadmap".
nextjs-framer-motion-animations
IncludedAdds production-safe Motion for React or Framer Motion animations to Next.js apps, including reveal, hover and tap micro-interactions, whileInView, stagger, AnimatePresence, layout and layoutId transitions, reorder, scroll-linked UI, and lightweight route-content transitions. Use when the user asks to add, refactor, or debug Motion or Framer Motion in App Router or Pages Router codebases, especially around server/client boundaries, reduced motion, LazyMotion, bundle size, hydration, or route transitions. Avoid for GSAP-style timelines, WebGL or 3D scenes, heavy scroll storytelling, or CSS-only effects unless Motion is explicitly requested.