From 9478b7412a025c3f7681f3f23ee5cf6019a43a17 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 10:30:36 -0700 Subject: [PATCH 01/21] feat(skills): add gravity-flow-abilities skill draft --- skills/gravity-flow-abilities/SKILL.md | 160 ++++++++++++++++++ .../references/processing.md | 69 ++++++++ .../references/status-reference.md | 85 ++++++++++ 3 files changed, 314 insertions(+) create mode 100644 skills/gravity-flow-abilities/SKILL.md create mode 100644 skills/gravity-flow-abilities/references/processing.md create mode 100644 skills/gravity-flow-abilities/references/status-reference.md diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md new file mode 100644 index 0000000..4705ffd --- /dev/null +++ b/skills/gravity-flow-abilities/SKILL.md @@ -0,0 +1,160 @@ +--- +name: gravity-flow-abilities +description: Workflow guidance for AI agents using Gravity Flow abilities via the WordPress Abilities API (MCP). Load this skill when interacting with any gravityflow/* MCP tools — triaging the workflow inbox, approving/rejecting entries, submitting user input, diagnosing stuck workflows, admin actions (cancel/restart/send-to-step), or workflow reports and activity. Provides status vocabulary, assignee-key semantics, sequencing rules, and pitfall avoidance that tool schemas alone cannot express. +license: GPL-2.0+ +compatibility: Requires a WordPress site with Gravity Flow 3.2+ (MCP enabled under Workflow → Settings → MCP) and Gravity Forms 2.9+ +metadata: + author: gravityforms + version: "0.1.0" +--- + +# Gravity Flow Abilities — Agent Skill + +## Ability Routing + +17 abilities. Select based on intent: + +**Orient** → `system-info` (Flow version + workflow-enabled forms with step counts and pending-entry counts — call this first), `system-step-types` (registered step types with `processable` flags) + +**Triage** → `inbox-list` (pending tasks for the authenticated user), `inbox-count` + +**Search** → `status-search` (workflow entries filtered by form, workflow status, step, assignee, date range; paged) + +**Diagnose** → `workflow-status-get` (one entry: final status, current step, live assignees with per-assignee statuses, per-step status map), `timeline-get` (entry history: who did what, when, with notes) + +**Act** → `steps-process` (approve/reject/revert an approval step, or complete a user-input step, with note and field values), `timeline-note-add` (annotate an entry's workflow timeline) + +**Unstick (admin)** → `steps-restart` (mildest), `workflow-send-to-step`, `workflow-restart`, `workflow-cancel` + +**Step configuration (read)** → `steps-list` (a form's steps, summary shape; optional `entry_id` adds per-entry statuses), `steps-get` (one step's sanitized settings) + +**Analyze** → `reports-get` (aggregate completion counts and durations across 7 scopes), `activity-list` (recent workflow event feed — the audit view) + +All ability names use format `gravityflow/{area}-{action}`. On the dedicated endpoint, tool names are hyphenated: `gravityflow-inbox-list`. + +**Not yet available:** step configuration writes (`steps-create`, `steps-update`, `steps-delete`, `system-step-type-schema`) are planned but not shipped. Never attempt to create or modify workflow steps via `gravityforms/feeds-*` — steps are Flow-owned feeds and generic feed tools bypass Flow's validation. Report the limitation instead. + +## Division of Labor: Flow vs Forms + +Gravity Flow abilities return **entry IDs and workflow state — never entry field values**. Hydrate entry data with `gravityforms/entries-get`. The two servers are complementary and usually both connected: + +- `gravityflow/*` — workflow: inbox, statuses, assignees, timeline, processing, admin actions, reports +- `gravityforms/*` — data: entry field values, form structure, submissions, notes + +**Etiquette rules (important):** +- Never edit workflow-relevant fields via `gravityforms/entries-update` while an entry is mid-step — assignee resolution and conditions will silently desync. The one sanctioned exception is the reassignment recipe below. +- Never touch Flow steps via `gravityforms/feeds-*`. +- Workflow entry-meta keys are a legitimate escape hatch in `gravityforms/entries-search` `field_filters` when `status-search` filters aren't enough — see [references/status-reference.md](references/status-reference.md). + +## Access Modes + +Flow's MCP surface is gated in **Workflow → Settings → MCP**, independently of Gravity Forms' MCP settings: + +1. **Enable MCP** (default: off) — master switch. When off, no `gravityflow/*` ability is exposed or callable. +2. **Per-tool checkboxes** (default: every tool off) — grouped into **Read-only Tools** and **Write & Destructive Tools**. A tool is exposed and callable only when its box is checked. There is no blanket "read access." + +**Read-only group** (each opt-in): `inbox-list`, `inbox-count`, `status-search`, `workflow-status-get`, `timeline-get`, `steps-list`, `steps-get`, `system-info`, `system-step-types`, `reports-get`, `activity-list` + +**Write & destructive group** (each opt-in): `timeline-note-add`, `steps-process`, `steps-restart`, `workflow-send-to-step`, `workflow-restart`, `workflow-cancel` + +If a tool is absent from discovery or a call is permission-denied, the admin has not enabled that specific tool (or MCP itself is off). Do not attempt workarounds — tell the user to enable the exact tool in Workflow → Settings → MCP. + +### Endpoint + +The dedicated server lives at `/wp-json/mcp/gravityflow` and works regardless of Gravity Forms' MCP toggle. Tool names there are direct (`gravityflow-steps-process`) with full JSON Schemas. When the site-wide WordPress MCP server exists (GF site mode), Flow abilities also appear through its `discover-abilities` / `execute-ability` meta-tools. Same abilities either way; only transport differs. + +## Acting User Model + +MCP authenticates as a real WordPress user (application password). Every ability acts as that user — there is no "act as user X" input. + +- **Inbox is personal.** `inbox-list` returns tasks assigned to the authenticated user (directly, via role, or via resolved fields). Different credentials = different inbox. +- **Processing as self** requires the authenticated user to be a *pending assignee* on the entry's current step — the same rule the web inbox enforces. +- **Delegated processing** (`steps-process` with `assignee_key`) acts on behalf of another pending assignee. It requires workflow-admin permission AND a `note` explaining why. The action is dual-attributed: the timeline note names both identities (`[On behalf of X (key)] …`) and the activity feed records a `delegated` event. A delegated action is never indistinguishable from the assignee acting themselves. +- **Entry visibility:** entry-scoped abilities (`workflow-status-get`, `timeline-get`, `timeline-note-add`, `steps-list` with `entry_id`) succeed only when the user has view-all permission, is the entry's submitter, or is a current/past assignee. An invisible entry returns the same error as a nonexistent one — do not retry or enumerate; verify existence via `status-search` or ask the user. + +## Core Workflows + +### Triage and act (the primary loop) + +1. `inbox-list` → pending tasks with `entry_id`, `form_id`, `step_id`, `step_name`, `step_type`, `submitter`, `workflow_timestamp`, `due_date` +2. Hydrate details as needed: `gravityforms/entries-get { entry_id }` +3. `steps-process { entry_id, status, note }` — always include a `note`; approvals without context are poor audit trail +4. Check the response: `step_status` confirms your action; `final_status` + `current_step_id` show where the workflow went next + +Sort/prioritize by `workflow_timestamp` (when the task started waiting), not `date_created` (when the entry was submitted). + +### Diagnose and unstick ("where is entry 512 stuck?") + +1. `workflow-status-get { entry_id }` → current step, how long pending, live assignees and who hasn't acted +2. `timeline-get { entry_id }` → what already happened, in whose hands it stalled +3. Escalate mildest-first (all admin-gated): + - `steps-restart` — reset the current step's assignee statuses and re-send notifications. Use when the assignee missed/lost the notification. + - `workflow-send-to-step { step_id }` — jump to a specific step; the current step's pending assignees are discarded. Use to skip a broken/unnecessary step. + - `workflow-restart` — reset EVERY step status and reprocess from the top. Nuclear; requires confirmation phrase. +4. Etiquette: after any admin intervention, `timeline-note-add` explaining what was done and why. + +**Cancel vs restart:** `workflow-cancel` ends the workflow (assignees purged, entry kept, `final_status: cancelled`); `workflow-restart` starts it over. Cancelling is not deleting — the entry survives and can be restarted later. + +**Confirmation echo-back:** `workflow-cancel` requires `confirmation: "CANCEL WORKFLOW {entry_id}"`; `workflow-restart` requires `confirmation: "RESTART WORKFLOW {entry_id}"`. On a mismatch the error returns the exact expected phrase — echo it back precisely. Always confirm with the human before these operations. + +### Search + +`status-search` filters: `form_id`, `status` (workflow final status), `step_id`, `assignee_key`, date range, paging. With view-all permission it spans all entries; without it, results are silently limited to the user's **own submissions** — if results look incomplete, that's why (tell the user which permission is missing rather than retrying). + +For filters `status-search` doesn't offer, use `gravityforms/entries-search` with workflow meta keys as `field_filters` — key table in [references/status-reference.md](references/status-reference.md). + +### Reassignment (sanctioned recipe) + +There is no reassign ability. For steps whose assignees resolve from an entry field (assignee field, user field, email field), reassignment is exactly two calls: + +1. `gravityforms/entries-update` — set the entry's assignee field to the new user/email +2. `gravityflow/steps-restart { entry_id }` — re-resolves assignees from the updated field and re-notifies + +This is the ONE sanctioned exception to "don't edit workflow fields mid-step," and it only works for field-resolved assignees. For steps with fixed user/role assignees, the step configuration itself must change — not yet possible via MCP; direct the user to the step settings screen. + +### Analytics + +- `reports-get { scope }` — scopes and required params: `all_forms` (per-form), `form` (per-month, needs `form_id`), `form_by_step` (needs `form_id`), `step_by_assignee` (needs `step_id`), `form_by_assignee` (needs `form_id`), `all_forms_by_assignee`, `assignee_by_month` (needs `assignee_key`). Defaults to the last 6 months; durations are **seconds** — convert to human units when reporting. +- `activity-list` — newest-first event feed (workflow/step/assignee lifecycle), filter by `objects`, `form_id`, `limit` (default 50, max 400). Use for "who approved what today." Delegated actions appear as assignee `delegated` events where `display_name` is the acting admin and `assignee_key` is who they acted for. +- Pattern: `reports-get` finds the aggregate problem (slowest approver), `status-search` finds the concrete backlog behind it. + +## Processing Rules (steps-process) + +Full detail in [references/processing.md](references/processing.md). The essentials: + +- **Only `approval` and `user_input` steps are processable.** Anything else (notification, webhook, feed add-on steps…) returns `operation_not_supported` — those steps complete on their own; if one is stuck, use the unstick ladder instead. +- **Valid `status` by step type:** approval → `approved` | `rejected` | `revert` (revert only if the step has reverting enabled); user_input → `complete` | `in_progress` (save progress without completing). +- **Note requirements:** approval steps can be configured to REQUIRE a note on reject/revert. On that validation error, retry the same call with a `note`. Delegated calls always require a note. +- **`field_values`** (user_input steps only): keys are `input_{field_id}` / `input_{field_id}.{sub}`. Only the step's *editable fields* are writable — anything else fails closed with an error naming the editable field IDs. Approval steps do not accept field values. +- **Double-submit** → `assignee_already_processed`, no state change. Not an error to retry — the work is done. + +## Status Vocabulary (essentials) + +- **Workflow final status:** `pending` (in flight), `cancelled`, or — on completion — the FINAL step's status: `approved`, `rejected`, or `complete`. A finished workflow is NOT always `complete`; treat all three as terminal. +- **Step statuses:** `pending`, `queued` (not reached yet), `complete`, plus type-specific `approved` / `rejected` / `revert`. +- **Assignee keys:** `type|id` format — `user_id|5`, `role|editor`, `email|pat@example.com`. +- **Configured ≠ actual:** a step's configured assignees (from `steps-get`) may be field-resolved (`assignee_field`, `email_field`, …) and resolve per entry at runtime. To know who can act on an entry NOW, read `workflow-status-get`, never `steps-get`. +- **Email assignees** without a WordPress account act via signed token links in notification emails — they cannot act via MCP, and no credential will give an agent their inbox. + +Full vocabulary, per-assignee meta, and the entries-search escape hatch: [references/status-reference.md](references/status-reference.md). + +## Key Pitfalls + +| Mistake | Consequence | Prevention | +|---|---|---| +| Skipping `system-info` at session start | Wrong form IDs, missed workflow forms | Call it first — it maps the workflow landscape | +| Reading `steps-get` to decide who should act on an entry | Field-resolved assignees differ per entry | Use `workflow-status-get` for live assignees | +| `steps-process` on a notification/webhook/feed step | `operation_not_supported` | Check `step_type` first; only approval and user_input are processable | +| Expecting `final_status: complete` for every finished workflow | Finished workflows record `approved`/`rejected` too | Treat `approved`, `rejected`, `complete` as terminal; `pending` means in flight | +| Editing workflow-relevant entry fields mid-step via `gravityforms/entries-update` | Assignees/conditions silently desync | Only the two-call reassignment recipe is sanctioned | +| Wrong or missing confirmation phrase on cancel/restart | `expected_confirmation` error | Echo the exact phrase from the error, including entry ID | +| Retrying entry IDs after a not-found error | IDs can't be enumerated — invisible ≡ nonexistent | Verify via `status-search`; check permissions with the user | +| `assignee_key` without a `note` | Validation error | Delegation always requires a reason note | +| `field_values` on an approval step, or on non-editable fields | Rejected fail-closed | user_input steps only; respect the editable-field list in the error | +| Sorting inbox by `date_created` | Oldest *submission* ≠ longest-*waiting* task | Sort by `workflow_timestamp` | +| Treating `status-search`'s thin results as "no entries" | Degraded mode shows own submissions only | Report the missing view-all permission instead | +| Tool absent from discovery | Admin hasn't enabled that tool (or MCP is off) | Point to Workflow → Settings → MCP; no workarounds | +| Building/modifying steps via `gravityforms/feeds-*` | Bypasses Flow validation; refused | Step CRUD via MCP is not yet available — say so | + +For steps-process details and error codes: [references/processing.md](references/processing.md). +For status vocabulary, meta keys, and search escape hatches: [references/status-reference.md](references/status-reference.md). diff --git a/skills/gravity-flow-abilities/references/processing.md b/skills/gravity-flow-abilities/references/processing.md new file mode 100644 index 0000000..fbf8fa0 --- /dev/null +++ b/skills/gravity-flow-abilities/references/processing.md @@ -0,0 +1,69 @@ +# steps-process — Complete Guide + +`gravityflow/steps-process` performs an assignee action on an entry's **current** step. It cannot target an arbitrary step — the workflow's position decides what is processable. + +## Inputs + +| Input | Notes | +|---|---| +| `entry_id` | Required | +| `status` | Required — see per-type table below | +| `note` | Optional in general; REQUIRED when the step config demands one for the status (common on reject/revert), and ALWAYS required for delegated calls. Include one anyway — approvals without context make poor audit trail | +| `field_values` | user_input steps only; `input_{field_id}` / `input_{field_id}.{sub}` keys | +| `assignee_key` | Delegation (`type|id`); requires workflow-admin permission + `note` | + +## Valid status by step type + +| Step type | Statuses | Notes | +|---|---|---| +| `approval` | `approved`, `rejected`, `revert` | `revert` only when the step has reverting enabled — it sends the entry back to a designated user-input step without recording a rejection | +| `user_input` | `complete`, `in_progress` | `in_progress` saves `field_values` without completing the step | +| anything else | — | `operation_not_supported`. Notification/webhook/feed steps complete autonomously; if one is stuck, use `steps-restart` / `workflow-send-to-step` | + +`system-step-types` reports a `processable` flag per type — check it when unsure. + +## Authorization + +- **As self:** authenticated user must be a *pending* assignee on the current step (directly, via role, or via a field that resolved to them). Same rule as the web inbox. +- **Delegated (`assignee_key`):** requires workflow-admin permission. The key must resolve to a *pending* assignee on the current step. `note` is required. The result is dual-attributed: + - Timeline note: attributed to the acting admin, body prefixed `[On behalf of {display_name} ({key})]` + - Activity feed: a `delegated` assignee event (acting admin in `display_name`, acted-for assignee in `assignee_key`) alongside the normal status event + +## field_values and editable fields + +User-input steps declare which fields assignees may edit. `field_values` is validated fail-closed: + +- Keys must match `input_{field_id}` or `input_{field_id}.{sub}` / `input_{field_id}_{sub}` for compound fields +- Every referenced field must be in the step's editable list — otherwise the call fails with an error naming the editable field IDs, and nothing is saved +- Approval steps accept no `field_values` at all + +To know the editable fields in advance: `steps-get` on the current step. Value formats follow Gravity Forms conventions (see the gravity-forms-abilities skill's field-config reference). + +## Response shape + +```json +{ + "success": true, + "step_status": "approved", + "feedback": "Entry Approved", + "final_status": "pending", + "current_step_id": 42 +} +``` + +- `step_status` — the status you requested, confirmed +- `feedback` — human-readable pipeline message +- `final_status` + `current_step_id` — where the workflow stands NOW (the workflow may have advanced several steps: notifications/webhooks between interactive steps complete inline). `current_step_id: null` with a terminal `final_status` means the workflow finished + +After processing, trust this response over assumptions — reject destinations and conditional branches mean the "next" step is not always the next in order. + +## Errors + +| Error | Meaning | Agent response | +|---|---|---| +| `operation_not_supported` | Current step type is not processable | Use the unstick ladder (steps-restart / send-to-step), not repeated processing | +| `assignee_already_processed` | This assignee already acted on this step | Done — do not retry; check `workflow-status-get` for current state | +| Validation error naming a required note | Step config requires a note for this status | Retry the same call with `note` | +| Error naming editable field IDs | `field_values` touched a non-editable field | Resend with only listed fields; report the rest to the user | +| Permission error | Not a pending assignee (self) / missing admin permission (delegated) | Check `workflow-status-get` assignees; do not escalate on your own | +| Entry not found | Entry nonexistent OR not visible to this user | Indistinguishable by design — verify via `status-search`, don't enumerate | diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md new file mode 100644 index 0000000..ef4c634 --- /dev/null +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -0,0 +1,85 @@ +# Status Vocabulary, Assignee Keys, and Meta-Key Escape Hatch + +## Workflow final status (`final_status`) + +| Value | Meaning | +|---|---| +| `pending` | Workflow in flight — a current step exists | +| `cancelled` | Workflow cancelled — assignees purged, entry retained | +| `complete` | Finished; final step was a non-decision step (e.g. user input) | +| `approved` | Finished; final step was an approval that was approved | +| `rejected` | Finished; final step was an approval that was rejected | + +Key fact (verified against live behavior): on completion, Flow records the **final step's status** as the workflow status. Agents must treat `approved`, `rejected`, and `complete` all as terminal. `current_step` is `null` for any terminal status and also before the workflow starts. + +A rejected approval does not necessarily end the workflow — reject destinations can route to earlier steps or alternate branches. Read `workflow-status-get` after acting rather than assuming. + +## Step statuses + +| Value | Meaning | +|---|---| +| `queued` | Step not reached yet | +| `pending` | Step active, waiting on assignees | +| `complete` | Step finished (user input completed, notification sent, feed processed…) | +| `approved` / `rejected` | Approval step outcome | +| `revert` | Approval reverted to a user-input step for changes | + +## Per-assignee statuses + +Within a pending step, each assignee has their own status (`pending`, `approved`, `rejected`, `complete`). Multi-assignee approval steps combine them by policy (all must approve / any one approves) — one assignee's `approved` with the step still `pending` means the policy is "all." `workflow-status-get` returns the live list: `assignees[] { key, display_name, status }`. + +## Assignee key format + +`type|id`: + +| Key | Resolves to | +|---|---| +| `user_id|5` | WordPress user ID 5 | +| `role|approver` | Every user with the role | +| `email|pat@example.com` | An email address (may have no WP account) | + +Step *configurations* may also use field-resolved types (`assignee_field`, `assignee_user_field`, `email_field`) that resolve per entry at runtime — which is why `steps-get` shows configured assignees but only `workflow-status-get` shows an entry's actual ones. + +**Email assignees** act through signed token links embedded in notification emails. They never authenticate to WordPress, so no MCP credential corresponds to them. An agent cannot act *as* an email assignee — only a workflow admin can act *on behalf of* one via delegated `steps-process` with `assignee_key: "email|..."`. + +## Workflow entry meta keys (escape hatch) + +When `status-search` filters aren't expressive enough, these meta keys work as `field_filters` keys in `gravityforms/entries-search`: + +| Key | Value | +|---|---| +| `workflow_final_status` | Final status (table above) | +| `workflow_step` | Current step ID (integer) | +| `workflow_step_status_{step_id}` | That step's status for the entry | +| `workflow_user_id_{user_id}` | Per-assignee status when user {id} is/was an assignee | +| `workflow_role_{role}` | Per-assignee status for a role assignee | +| `workflow_email_{email}` | Per-assignee status for an email assignee | +| `workflow_timestamp` | Unix timestamp the current step started | + +Example — all entries stuck on step 41 in form 3: + +```json +{ + "form_id": 3, + "search_criteria": { + "field_filters": [ + { "key": "workflow_step", "value": "41" } + ] + } +} +``` + +Example — everything user 7 has ever approved on form 3: + +```json +{ + "form_id": 3, + "search_criteria": { + "field_filters": [ + { "key": "workflow_user_id_7", "value": "approved" } + ] + } +} +``` + +Prefer `status-search` when its filters suffice — it applies workflow-aware shaping and permission scoping. The escape hatch is subject to GF's entry permissions, not Flow's entry-visibility predicate. From 9fff746978927371e2cd8d2e903ce8092c8ac567 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 11:34:46 -0700 Subject: [PATCH 02/21] feat(skills): fold first scenario-loop findings into gravity-flow-abilities --- skills/gravity-flow-abilities/SKILL.md | 4 +++- skills/gravity-flow-abilities/references/processing.md | 7 ++++--- .../gravity-flow-abilities/references/status-reference.md | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 4705ffd..7c91e14 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -87,6 +87,8 @@ Sort/prioritize by `workflow_timestamp` (when the task started waiting), not `da 1. `workflow-status-get { entry_id }` → current step, how long pending, live assignees and who hasn't acted 2. `timeline-get { entry_id }` → what already happened, in whose hands it stalled +**Where admins get step IDs:** `steps-list` requires the step-builder permission, which workflow admins often lack. The per-step map in `workflow-status-get` (`steps[] { id, name, type, status }`) is the intended step-ID source for send-to-step targets. Skipped steps show status `cancelled` there. + 3. Escalate mildest-first (all admin-gated): - `steps-restart` — reset the current step's assignee statuses and re-send notifications. Use when the assignee missed/lost the notification. - `workflow-send-to-step { step_id }` — jump to a specific step; the current step's pending assignees are discarded. Use to skip a broken/unnecessary step. @@ -114,7 +116,7 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a ### Analytics -- `reports-get { scope }` — scopes and required params: `all_forms` (per-form), `form` (per-month, needs `form_id`), `form_by_step` (needs `form_id`), `step_by_assignee` (needs `step_id`), `form_by_assignee` (needs `form_id`), `all_forms_by_assignee`, `assignee_by_month` (needs `assignee_key`). Defaults to the last 6 months; durations are **seconds** — convert to human units when reporting. +- `reports-get { scope }` — scopes and required params: `all_forms` (per-form), `form` (per-month, needs `form_id`), `form_by_step` (needs `form_id`), `step_by_assignee` (needs `step_id`), `form_by_assignee` (needs `form_id`), `all_forms_by_assignee`, `assignee_by_month` (needs `assignee_key`). Defaults to the last 6 months. Rows carry `count` and `avg_duration_secs` plus scope-specific identifiers; durations are **seconds** — convert to human units when reporting. - `activity-list` — newest-first event feed (workflow/step/assignee lifecycle), filter by `objects`, `form_id`, `limit` (default 50, max 400). Use for "who approved what today." Delegated actions appear as assignee `delegated` events where `display_name` is the acting admin and `assignee_key` is who they acted for. - Pattern: `reports-get` finds the aggregate problem (slowest approver), `status-search` finds the concrete backlog behind it. diff --git a/skills/gravity-flow-abilities/references/processing.md b/skills/gravity-flow-abilities/references/processing.md index fbf8fa0..6576a01 100644 --- a/skills/gravity-flow-abilities/references/processing.md +++ b/skills/gravity-flow-abilities/references/processing.md @@ -63,7 +63,8 @@ After processing, trust this response over assumptions — reject destinations a |---|---|---| | `operation_not_supported` | Current step type is not processable | Use the unstick ladder (steps-restart / send-to-step), not repeated processing | | `assignee_already_processed` | This assignee already acted on this step | Done — do not retry; check `workflow-status-get` for current state | -| Validation error naming a required note | Step config requires a note for this status | Retry the same call with `note` | -| Error naming editable field IDs | `field_values` touched a non-editable field | Resend with only listed fields; report the rest to the user | -| Permission error | Not a pending assignee (self) / missing admin permission (delegated) | Check `workflow-status-get` assignees; do not escalate on your own | +| `gravityflow_ability_validation_failed` | Names each concrete failure (e.g. "note: A note is required") | Retry the same call with the missing/corrected values | +| `gravityflow_ability_field_not_editable` | `field_values` touched a non-editable field; error names the editable IDs | Resend with only listed fields; report the rest to the user | +| `gravityflow_ability_not_pending_assignee` | You are not a pending assignee on the current step | Check `workflow-status-get` assignees; do not escalate on your own | +| `gravityflow_ability_delegation_denied` / `gravityflow_ability_note_required` | Delegation needs workflow-admin permission and a reason note | Add the note; if permission is missing, hand back to the user | | Entry not found | Entry nonexistent OR not visible to this user | Indistinguishable by design — verify via `status-search`, don't enumerate | diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md index ef4c634..893c7a9 100644 --- a/skills/gravity-flow-abilities/references/status-reference.md +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -23,6 +23,7 @@ A rejected approval does not necessarily end the workflow — reject destination | `complete` | Step finished (user input completed, notification sent, feed processed…) | | `approved` / `rejected` | Approval step outcome | | `revert` | Approval reverted to a user-input step for changes | +| `cancelled` | Step was skipped (workflow-send-to-step) or the workflow was cancelled while it was active | ## Per-assignee statuses From e622d9357f239540af47ae7f7c3cdd193048dcf7 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 11:36:10 -0700 Subject: [PATCH 03/21] feat(skills): exact entries-update call shape in reassignment recipe --- skills/gravity-flow-abilities/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 7c91e14..1668895 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -109,8 +109,8 @@ For filters `status-search` doesn't offer, use `gravityforms/entries-search` wit There is no reassign ability. For steps whose assignees resolve from an entry field (assignee field, user field, email field), reassignment is exactly two calls: -1. `gravityforms/entries-update` — set the entry's assignee field to the new user/email -2. `gravityflow/steps-restart { entry_id }` — re-resolves assignees from the updated field and re-notifies +1. `gravityforms/entries-update { "entry": { "id": 787, "3": "pat@newco.com" } }` — the entry ID goes INSIDE the entry object; set the assignee field (here field 3) to the new user/email +2. `gravityflow/steps-restart { entry_id }` — re-resolves assignees from the updated field and re-sends notifications (email assignees get a fresh token link) This is the ONE sanctioned exception to "don't edit workflow fields mid-step," and it only works for field-resolved assignees. For steps with fixed user/role assignees, the step configuration itself must change — not yet possible via MCP; direct the user to the step settings screen. From 48e12f0592d56e1c179178d8cfd58d33f55d0c6b Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 11:39:09 -0700 Subject: [PATCH 04/21] feat(skills): approval_policy guidance for multi-assignee confusion --- skills/gravity-flow-abilities/references/status-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md index 893c7a9..c1a862b 100644 --- a/skills/gravity-flow-abilities/references/status-reference.md +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -27,7 +27,7 @@ A rejected approval does not necessarily end the workflow — reject destination ## Per-assignee statuses -Within a pending step, each assignee has their own status (`pending`, `approved`, `rejected`, `complete`). Multi-assignee approval steps combine them by policy (all must approve / any one approves) — one assignee's `approved` with the step still `pending` means the policy is "all." `workflow-status-get` returns the live list: `assignees[] { key, display_name, status }`. +Within a pending step, each assignee has their own status (`pending`, `approved`, `rejected`, `complete`). Multi-assignee approval steps combine them by policy — `workflow-status-get`'s `current_step.approval_policy` states it directly: `"all"` (unanimous — every assignee must act; one approval with the step still pending is normal, name who is still pending) or `"any"` (first decision wins). The live list is `assignees[] { key, display_name, status }`. ## Assignee key format From b0f8e99acea7c96e5e14ca6b0f268e7e0096f5a9 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 12:36:18 -0700 Subject: [PATCH 05/21] feat(skills): document step CRUD build workflows and guardrails --- skills/gravity-flow-abilities/SKILL.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 1668895..f83fa65 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -12,7 +12,7 @@ metadata: ## Ability Routing -17 abilities. Select based on intent: +21 abilities. Select based on intent: **Orient** → `system-info` (Flow version + workflow-enabled forms with step counts and pending-entry counts — call this first), `system-step-types` (registered step types with `processable` flags) @@ -28,11 +28,13 @@ metadata: **Step configuration (read)** → `steps-list` (a form's steps, summary shape; optional `entry_id` adds per-entry statuses), `steps-get` (one step's sanitized settings) +**Build (write)** → `system-step-type-schema` (settings schema for one type — call FIRST), `steps-create`, `steps-update` (partial merge), `steps-delete` + **Analyze** → `reports-get` (aggregate completion counts and durations across 7 scopes), `activity-list` (recent workflow event feed — the audit view) All ability names use format `gravityflow/{area}-{action}`. On the dedicated endpoint, tool names are hyphenated: `gravityflow-inbox-list`. -**Not yet available:** step configuration writes (`steps-create`, `steps-update`, `steps-delete`, `system-step-type-schema`) are planned but not shipped. Never attempt to create or modify workflow steps via `gravityforms/feeds-*` — steps are Flow-owned feeds and generic feed tools bypass Flow's validation. Report the limitation instead. +**Step writes are allowlisted:** only `approval`, `user_input`, and `notification` steps can be created or modified programmatically; other types (feed add-on steps, routing, etc.) return an error naming the allowlist — configure those in the Flow admin UI. `workflow_start` / `workflow_complete` are auto-managed sentinels and can never be touched. Never attempt to create or modify workflow steps via `gravityforms/feeds-*` — Flow actively refuses generic feed tools targeting its steps and redirects you to `gravityflow/steps-*`. ## Division of Labor: Flow vs Forms @@ -114,6 +116,16 @@ There is no reassign ability. For steps whose assignees resolve from an entry fi This is the ONE sanctioned exception to "don't edit workflow fields mid-step," and it only works for field-resolved assignees. For steps with fixed user/role assignees, the step configuration itself must change — not yet possible via MCP; direct the user to the step settings screen. +### Building workflow steps + +1. `system-step-type-schema { step_type, form_id }` — the settings vocabulary for the type. **Unknown settings keys are rejected on create/update (nothing saves), so never guess keys.** +2. `steps-create { form_id, step_type, step_name, assignees, destination_complete, condition, settings }` — first-class inputs cover the common cases; everything else goes in `settings`. Assignees use `type|id` keys; the internal assignee-source discriminator is handled for you. New steps append to the end of the workflow. Conditions are GF conditional-logic objects (`{actionType, logicType, rules}`) gating whether the step runs. +3. Verify with `steps-list` — check order and destinations. +4. `steps-update { step_id, ... }` — partial merge; a step's type is immutable. **When entries are in flight on the step, the update is blocked until you echo `UPDATE STEP {id} AFFECTING {n} ENTRIES`** — and changing assignee settings reroutes ALL of those entries (assignees re-resolved and re-notified automatically). Confirm blast radius with the user first. +5. `steps-delete { step_id, confirmation }` — always needs `DELETE STEP {id} FROM FORM {form_id}`; if entries sit on the step the error escalates to a count-bearing phrase (`DELETE STEP {id} AFFECTING {n} ENTRIES`). Prefer moving in-flight entries with `workflow-send-to-step` first. Deletes are blocked while other steps' destinations route to the step — update those destinations first (the error names them). + +Every validation error names the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. + ### Analytics - `reports-get { scope }` — scopes and required params: `all_forms` (per-form), `form` (per-month, needs `form_id`), `form_by_step` (needs `form_id`), `step_by_assignee` (needs `step_id`), `form_by_assignee` (needs `form_id`), `all_forms_by_assignee`, `assignee_by_month` (needs `assignee_key`). Defaults to the last 6 months. Rows carry `count` and `avg_duration_secs` plus scope-specific identifiers; durations are **seconds** — convert to human units when reporting. @@ -156,7 +168,10 @@ Full vocabulary, per-assignee meta, and the entries-search escape hatch: [refere | Sorting inbox by `date_created` | Oldest *submission* ≠ longest-*waiting* task | Sort by `workflow_timestamp` | | Treating `status-search`'s thin results as "no entries" | Degraded mode shows own submissions only | Report the missing view-all permission instead | | Tool absent from discovery | Admin hasn't enabled that tool (or MCP is off) | Point to Workflow → Settings → MCP; no workarounds | -| Building/modifying steps via `gravityforms/feeds-*` | Bypasses Flow validation; refused | Step CRUD via MCP is not yet available — say so | +| Building/modifying steps via `gravityforms/feeds-*` | Actively refused by Flow | Use `gravityflow/steps-create` / `steps-update` / `steps-delete` | +| Guessing settings keys on steps-create/update | Unknown keys rejected, nothing saved | Call `system-step-type-schema` first | +| Updating assignees on a step with in-flight entries without warning the user | Every in-flight entry is rerouted and re-notified | Echo the count-bearing confirmation only after the user confirms the blast radius | +| Deleting a step other steps route to | Blocked — dangling destinations | Update the referencing steps' destinations first (the error names them) | For steps-process details and error codes: [references/processing.md](references/processing.md). For status vocabulary, meta keys, and search escape hatches: [references/status-reference.md](references/status-reference.md). From 3af56e78ccf5605943efd391dc41c49a8b71c32b Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 12:58:43 -0700 Subject: [PATCH 06/21] fix(skills): entries-search uses form_ids in escape-hatch examples --- .../gravity-flow-abilities/references/status-reference.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md index c1a862b..68191f1 100644 --- a/skills/gravity-flow-abilities/references/status-reference.md +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -61,7 +61,7 @@ Example — all entries stuck on step 41 in form 3: ```json { - "form_id": 3, + "form_ids": [3], "search_criteria": { "field_filters": [ { "key": "workflow_step", "value": "41" } @@ -70,11 +70,13 @@ Example — all entries stuck on step 41 in form 3: } ``` +Note: GF's `entries-search` takes `form_ids` (an array), not `form_id`. + Example — everything user 7 has ever approved on form 3: ```json { - "form_id": 3, + "form_ids": [3], "search_criteria": { "field_filters": [ { "key": "workflow_user_id_7", "value": "approved" } From e306d4e2f61bc495983c3085c4959a6cbb267ff1 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 12:59:43 -0700 Subject: [PATCH 07/21] feat(skills): add feasibility facts reference for consultation scenarios --- skills/gravity-flow-abilities/SKILL.md | 1 + .../references/feasibility.md | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 skills/gravity-flow-abilities/references/feasibility.md diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index f83fa65..32b9f65 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -173,5 +173,6 @@ Full vocabulary, per-assignee meta, and the entries-search escape hatch: [refere | Updating assignees on a step with in-flight entries without warning the user | Every in-flight entry is rerouted and re-notified | Echo the count-bearing confirmation only after the user confirms the blast radius | | Deleting a step other steps route to | Blocked — dangling destinations | Update the referencing steps' destinations first (the error names them) | +For "can Flow do X?" feasibility consultations: [references/feasibility.md](references/feasibility.md). For steps-process details and error codes: [references/processing.md](references/processing.md). For status vocabulary, meta keys, and search escape hatches: [references/status-reference.md](references/status-reference.md). diff --git a/skills/gravity-flow-abilities/references/feasibility.md b/skills/gravity-flow-abilities/references/feasibility.md new file mode 100644 index 0000000..a1ba6eb --- /dev/null +++ b/skills/gravity-flow-abilities/references/feasibility.md @@ -0,0 +1,32 @@ +# Feasibility Facts (for "can Gravity Flow do X?" consultations) + +Customers often ask whether a workflow design is possible before building. These are verified product facts — confirm, then point at the relevant abilities or admin screens. Never invent capabilities; anything not listed here should be verified against docs.gravityflow.io before claiming it. + +## Core workflow capabilities + +- **Approval chains**: unlimited sequential approval steps; per-step assignees (users, roles, emails, field-resolved); `"all"` (unanimous) or `"any"` approval policy per step; conditions on any step (GF conditional logic on field values) enable branch-per-department/amount/type designs. +- **Approval loops / send-back**: approval steps can **revert** to a user-input step (revise-and-resubmit loops) and reject-destinations can point at any earlier step — multi-round review loops are native. +- **User input steps**: assignees edit a configured subset of fields (`editable_fields`), can save progress (`in_progress`) before completing; display-fields config controls what each step's assignees see (reviewer-privacy patterns). +- **Two-actor patterns** (candidate submits, referent completes hidden fields): initial submission + a user_input step whose editable fields are the referent-only fields. Field-level conditional logic on steps requires the step's "field conditional logic" setting. +- **External participants without WordPress accounts**: email assignees act via signed token links from notification emails — supported for approval and user input steps. They cannot use MCP. +- **Scheduling/delays**: steps can be scheduled (delay after previous step, or date/date-field based). Expiration with destinations handles "no response by X" routing; resend settings handle reminders. +- **Notifications**: per-step assignee notifications plus approval/rejection/complete notifications, all templatable with merge tags; `{workflow_entry_link}` is the assignee's action link — REQUIRED for email assignees to act. + +## Integrations (verify the add-on is installed before promising) + +- **Payment-then-review** (e.g. paid application → manual approval): payment via a GF payment add-on (e.g. Stripe) on submission, then Flow approval steps. "Non-refundable payment, then review" = payment collected at submission, approval gates only the post-payment outcome. +- **Role change / user registration on approval**: GF User Registration add-on feeds run as workflow steps — approve → the registration/role-update feed step fires. Rejection routes around it. +- **Signatures**: the GF Signature add-on's field works inside workflows; approval steps can display and collect signature fields (signature on approval requires the field on the form and step display/editable config). +- **PDF generation**: Gravity Flow PDF add-on generates PDFs from entries with merge tags (including signatures) as a workflow step. +- **Feed add-on steps**: most GF feed add-ons (webhooks, email marketing, sheets connectors…) can run as workflow steps, i.e. "after approval, push to X". +- **Form-to-form**: Form Connector add-on creates/updates entries in other forms as steps (intake → per-team forms patterns). + +## Scope expectations + +For large multi-workflow asks (e.g. "eight country-specific incorporation workflows"), set expectations: design one workflow as the template, validate it end-to-end, then replicate per variant — and ask the narrowing questions first (which steps differ per country/region? who acts at each step? what does 'done' mean?). Do not attempt to build the entire estate in one pass. + +## Currently NOT possible via abilities + +- Creating WordPress pages/posts (inbox blocks, dashboards) — hand off to the human with exact instructions. +- Creating/modifying step types outside the allowlist (approval, user_input, notification) — feed-addon and routing steps are configured in the Flow admin UI. +- Acting as an email assignee (no credential exists) — only delegated processing by a workflow admin. From eca0fc8218c89b3044c8cff44c2fcb89eaa84ee1 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 13:59:13 -0700 Subject: [PATCH 08/21] feat(skills): verified payment-then-review patterns in feasibility facts --- skills/gravity-flow-abilities/references/feasibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/gravity-flow-abilities/references/feasibility.md b/skills/gravity-flow-abilities/references/feasibility.md index a1ba6eb..c0247e2 100644 --- a/skills/gravity-flow-abilities/references/feasibility.md +++ b/skills/gravity-flow-abilities/references/feasibility.md @@ -14,7 +14,7 @@ Customers often ask whether a workflow design is possible before building. These ## Integrations (verify the add-on is installed before promising) -- **Payment-then-review** (e.g. paid application → manual approval): payment via a GF payment add-on (e.g. Stripe) on submission, then Flow approval steps. "Non-refundable payment, then review" = payment collected at submission, approval gates only the post-payment outcome. +- **Payment-then-review** (e.g. paid application → manual approval): two verified patterns — payment at submission via a GF payment add-on feed, or a mid-workflow `stripe_checkout` step (Flow Stripe extension) where the assignee pays through Stripe-hosted Checkout from their workflow detail page. Payment itself always happens on Stripe's hosted page — never through MCP; agents triage/diagnose payment steps and verify outcomes (payment_status, transaction id, timeline "Processed" event). Capture/refund/cancel steps automate post-payment actions. - **Role change / user registration on approval**: GF User Registration add-on feeds run as workflow steps — approve → the registration/role-update feed step fires. Rejection routes around it. - **Signatures**: the GF Signature add-on's field works inside workflows; approval steps can display and collect signature fields (signature on approval requires the field on the form and step display/editable config). - **PDF generation**: Gravity Flow PDF add-on generates PDFs from entries with merge tags (including signatures) as a workflow step. From 9e40d93379cb15e04c7049c01cf4b14954f08a67 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 14:49:38 -0700 Subject: [PATCH 09/21] =?UTF-8?q?feat(skills):=20exit-criteria=20run-1=20f?= =?UTF-8?q?indings=20=E2=80=94=20recovery,=20handoffs,=20advisory=20discip?= =?UTF-8?q?line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/gravity-flow-abilities/SKILL.md | 12 ++++++++++-- .../gravity-flow-abilities/references/feasibility.md | 11 +++++++++++ .../references/status-reference.md | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 32b9f65..062b9ee 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -59,7 +59,9 @@ Flow's MCP surface is gated in **Workflow → Settings → MCP**, independently **Write & destructive group** (each opt-in): `timeline-note-add`, `steps-process`, `steps-restart`, `workflow-send-to-step`, `workflow-restart`, `workflow-cancel` -If a tool is absent from discovery or a call is permission-denied, the admin has not enabled that specific tool (or MCP itself is off). Do not attempt workarounds — tell the user to enable the exact tool in Workflow → Settings → MCP. +If a tool is absent from discovery or a call is permission-denied, the admin has not enabled that specific tool (or MCP itself is off), or your user lacks the capability. Do not attempt workarounds — tell the user to enable the exact tool in Workflow → Settings → MCP, or which capability is missing. + +**Admin-screen handoff URLs** (when a change needs the Flow admin UI, give the user a direct link): step settings live at `{site}/wp-admin/admin.php?page=gf_edit_forms&view=settings&subview=gravityflow&id={form_id}&fid={step_id}` — build these from `steps-list` output when recommending manual configuration. ### Endpoint @@ -91,6 +93,8 @@ Sort/prioritize by `workflow_timestamp` (when the task started waiting), not `da 2. `timeline-get { entry_id }` → what already happened, in whose hands it stalled **Where admins get step IDs:** `steps-list` requires the step-builder permission, which workflow admins often lack. The per-step map in `workflow-status-get` (`steps[] { id, name, type, status }`) is the intended step-ID source for send-to-step targets. Skipped steps show status `cancelled` there. +**When some tools are permission-denied:** keep diagnosing with what you have. Entry-level reads blocked? Use config-level reads (`steps-list`/`steps-get` need the step-builder permission). Config reads blocked? Use entry-level state (`workflow-status-get` on entries you can see). Never stop at the first denial while relevant accessible tools remain — and name the exact missing permission in your summary rather than guessing at causes. + 3. Escalate mildest-first (all admin-gated): - `steps-restart` — reset the current step's assignee statuses and re-send notifications. Use when the assignee missed/lost the notification. - `workflow-send-to-step { step_id }` — jump to a specific step; the current step's pending assignees are discarded. Use to skip a broken/unnecessary step. @@ -126,6 +130,10 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a Every validation error names the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. +### Feasibility and design questions + +When the user is asking whether something is possible, or how they would build it — **answer first, mutate nothing.** Do not create forms, steps, or entries to "demonstrate"; unrequested writes on a consultation are a failure. Read state where it helps (system-info, steps-list), confirm capabilities from [references/feasibility.md](references/feasibility.md), cite documentation links, ask the narrowing questions, and set scope expectations for large builds. + ### Analytics - `reports-get { scope }` — scopes and required params: `all_forms` (per-form), `form` (per-month, needs `form_id`), `form_by_step` (needs `form_id`), `step_by_assignee` (needs `step_id`), `form_by_assignee` (needs `form_id`), `all_forms_by_assignee`, `assignee_by_month` (needs `assignee_key`). Defaults to the last 6 months. Rows carry `count` and `avg_duration_secs` plus scope-specific identifiers; durations are **seconds** — convert to human units when reporting. @@ -138,7 +146,7 @@ Full detail in [references/processing.md](references/processing.md). The essenti - **Only `approval` and `user_input` steps are processable.** Anything else (notification, webhook, feed add-on steps…) returns `operation_not_supported` — those steps complete on their own; if one is stuck, use the unstick ladder instead. - **Valid `status` by step type:** approval → `approved` | `rejected` | `revert` (revert only if the step has reverting enabled); user_input → `complete` | `in_progress` (save progress without completing). -- **Note requirements:** approval steps can be configured to REQUIRE a note on reject/revert. On that validation error, retry the same call with a `note`. Delegated calls always require a note. +- **Note requirements:** approval steps can be configured to REQUIRE a note on reject/revert. On that validation error, retry the same call with a `note` — COMPOSE the note yourself from the user's stated rationale (policy reference, reason, context); if they gave none, a brief professional note stating the action and who requested it is correct. Do not stall the action to ask for note wording. Delegated calls always require a note. - **`field_values`** (user_input steps only): keys are `input_{field_id}` / `input_{field_id}.{sub}`. Only the step's *editable fields* are writable — anything else fails closed with an error naming the editable field IDs. Approval steps do not accept field values. - **Double-submit** → `assignee_already_processed`, no state change. Not an error to retry — the work is done. diff --git a/skills/gravity-flow-abilities/references/feasibility.md b/skills/gravity-flow-abilities/references/feasibility.md index c0247e2..e04bec7 100644 --- a/skills/gravity-flow-abilities/references/feasibility.md +++ b/skills/gravity-flow-abilities/references/feasibility.md @@ -21,6 +21,17 @@ Customers often ask whether a workflow design is possible before building. These - **Feed add-on steps**: most GF feed add-ons (webhooks, email marketing, sheets connectors…) can run as workflow steps, i.e. "after approval, push to X". - **Form-to-form**: Form Connector add-on creates/updates entries in other forms as steps (intake → per-team forms patterns). +## Documentation links (verified) + +Cite these when answering feasibility/design questions — deep-link by searching within them rather than guessing article URLs: + +- Gravity Flow docs home: https://docs.gravityflow.io/ +- Flow fundamentals (steps, assignees, notifications, conditions): https://docs.gravityflow.io/category/flow-fundamentals/ +- User guides (worked workflow examples): https://docs.gravityflow.io/category/user-guides/ +- Extensions (PDF, Form Connector, Stripe, Checklists…): https://docs.gravityflow.io/category/extensions/ +- Developer reference (hooks, REST): https://docs.gravityflow.io/category/developers/ +- Gravity Forms docs (fields, conditional logic, feeds, merge tags): https://docs.gravityforms.com/ + ## Scope expectations For large multi-workflow asks (e.g. "eight country-specific incorporation workflows"), set expectations: design one workflow as the template, validate it end-to-end, then replicate per variant — and ask the narrowing questions first (which steps differ per country/region? who acts at each step? what does 'done' mean?). Do not attempt to build the entire estate in one pass. diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md index 68191f1..1eab505 100644 --- a/skills/gravity-flow-abilities/references/status-reference.md +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -43,6 +43,8 @@ Step *configurations* may also use field-resolved types (`assignee_field`, `assi **Email assignees** act through signed token links embedded in notification emails. They never authenticate to WordPress, so no MCP credential corresponds to them. An agent cannot act *as* an email assignee — only a workflow admin can act *on behalf of* one via delegated `steps-process` with `assignee_key: "email|..."`. +**"Their link doesn't work" playbook** — always cover all three parts: (1) explain the model — email assignees act via the signed link in their notification, not by logging in, and links are entry+step+assignee-specific and can expire or be superseded; (2) verify with `workflow-status-get` that the step is still pending and they are still a listed assignee (if the step moved on, the old link is dead by design); (3) remediate with `steps-restart` (admin) to re-send a fresh token link — or delegated `steps-process` if the admin should act for them. Also check the step's notification message actually contains `{workflow_entry_link}` (use `{workflow_entry_link:page_id=N}` to point the link at a specific front-end inbox page). + ## Workflow entry meta keys (escape hatch) When `status-search` filters aren't expressive enough, these meta keys work as `field_filters` keys in `gravityforms/entries-search`: From 21fa3892425dd0a798bb2ed5352093119194ed81 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 15:36:48 -0700 Subject: [PATCH 10/21] feat(skills): doc-link requirement and request-as-authorization nuance --- skills/gravity-flow-abilities/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 062b9ee..8f640f3 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -125,14 +125,14 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a 1. `system-step-type-schema { step_type, form_id }` — the settings vocabulary for the type. **Unknown settings keys are rejected on create/update (nothing saves), so never guess keys.** 2. `steps-create { form_id, step_type, step_name, assignees, destination_complete, condition, settings }` — first-class inputs cover the common cases; everything else goes in `settings`. Assignees use `type|id` keys; the internal assignee-source discriminator is handled for you. New steps append to the end of the workflow. Conditions are GF conditional-logic objects (`{actionType, logicType, rules}`) gating whether the step runs. 3. Verify with `steps-list` — check order and destinations. -4. `steps-update { step_id, ... }` — partial merge; a step's type is immutable. **When entries are in flight on the step, the update is blocked until you echo `UPDATE STEP {id} AFFECTING {n} ENTRIES`** — and changing assignee settings reroutes ALL of those entries (assignees re-resolved and re-notified automatically). Confirm blast radius with the user first. +4. `steps-update { step_id, ... }` — partial merge; a step's type is immutable. **When entries are in flight on the step, the update is blocked until you echo `UPDATE STEP {id} AFFECTING {n} ENTRIES`** — and changing assignee settings reroutes ALL of those entries (assignees re-resolved and re-notified automatically). If the user's request itself already specifies the change ("assign it to the finance role"), that IS the authorization — echo the count-bearing confirmation and proceed, reporting the blast radius in your summary. Only stop to confirm when the request leaves the decision open or the in-flight impact plainly exceeds what the user described. 5. `steps-delete { step_id, confirmation }` — always needs `DELETE STEP {id} FROM FORM {form_id}`; if entries sit on the step the error escalates to a count-bearing phrase (`DELETE STEP {id} AFFECTING {n} ENTRIES`). Prefer moving in-flight entries with `workflow-send-to-step` first. Deletes are blocked while other steps' destinations route to the step — update those destinations first (the error names them). Every validation error names the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. ### Feasibility and design questions -When the user is asking whether something is possible, or how they would build it — **answer first, mutate nothing.** Do not create forms, steps, or entries to "demonstrate"; unrequested writes on a consultation are a failure. Read state where it helps (system-info, steps-list), confirm capabilities from [references/feasibility.md](references/feasibility.md), cite documentation links, ask the narrowing questions, and set scope expectations for large builds. +When the user is asking whether something is possible, or how they would build it — **answer first, mutate nothing.** Do not create forms, steps, or entries to "demonstrate"; unrequested writes on a consultation are a failure. Read state where it helps (system-info, steps-list), confirm capabilities from [references/feasibility.md](references/feasibility.md), **always include the relevant documentation links from that reference's verified list** (feature claims without a doc link are incomplete answers), ask the narrowing questions, and set scope expectations for large builds. ### Analytics From 5a169a339db835230383c4b67e7795de693c0f4a Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 27 Jul 2026 22:13:29 -0700 Subject: [PATCH 11/21] feat(skills): destructive-safety, permission fallback, no-assignee recovery, deep doc links --- skills/gravity-flow-abilities/SKILL.md | 12 ++++++- .../references/feasibility.md | 35 ++++++++++++++----- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 8f640f3..46b4ebc 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -93,7 +93,11 @@ Sort/prioritize by `workflow_timestamp` (when the task started waiting), not `da 2. `timeline-get { entry_id }` → what already happened, in whose hands it stalled **Where admins get step IDs:** `steps-list` requires the step-builder permission, which workflow admins often lack. The per-step map in `workflow-status-get` (`steps[] { id, name, type, status }`) is the intended step-ID source for send-to-step targets. Skipped steps show status `cancelled` there. -**When some tools are permission-denied:** keep diagnosing with what you have. Entry-level reads blocked? Use config-level reads (`steps-list`/`steps-get` need the step-builder permission). Config reads blocked? Use entry-level state (`workflow-status-get` on entries you can see). Never stop at the first denial while relevant accessible tools remain — and name the exact missing permission in your summary rather than guessing at causes. +**When some tools are permission-denied, fall back — do not give up.** The denial of ONE tool is never the end of diagnosis; there is almost always another route: +- `inbox-list` denied → `status-search` (find pending entries by form/step/assignee) → `workflow-status-get` on a specific entry. +- `steps-list`/`steps-get` denied (step-builder permission) → `workflow-status-get` gives the per-step map, current step, assignees, and approval policy for any visible entry; `system-step-types` gives type capabilities. +- Can't find the form by guessing IDs → `system-info` lists every workflow-enabled form with its title; match the user's description against those titles, then `steps-list`/`status-search` on the match. +Only conclude "not discoverable" after the accessible routes are exhausted, and name the exact missing permission rather than inventing a cause. Never diagnose the MCP integration as "disabled" — if a tool call fails to connect, that is a transient client issue; retry. 3. Escalate mildest-first (all admin-gated): - `steps-restart` — reset the current step's assignee statuses and re-send notifications. Use when the assignee missed/lost the notification. @@ -101,10 +105,16 @@ Sort/prioritize by `workflow_timestamp` (when the task started waiting), not `da - `workflow-restart` — reset EVERY step status and reprocess from the top. Nuclear; requires confirmation phrase. 4. Etiquette: after any admin intervention, `timeline-note-add` explaining what was done and why. +**Recovering entries that skipped a step (no-assignee auto-complete).** When a step shows "No assignees" in the timeline and entries sailed past it (common after an assignee user is deleted or a role empties), workflow-restart is the WRONG tool — it resets everything and, if the step still has no valid assignee, the entries skip it again. The correct recovery: (1) fix the step's assignees first (via the step settings UI, or `steps-update` if the type is allowlisted) so the step has a real assignee; (2) THEN `workflow-send-to-step` each affected entry back to that step. Identify the affected set precisely (e.g. filter by the field that gated the step) rather than sweeping every entry. This needs a human decision on WHO the new assignee should be — stop and ask for that if it is not given. + **Cancel vs restart:** `workflow-cancel` ends the workflow (assignees purged, entry kept, `final_status: cancelled`); `workflow-restart` starts it over. Cancelling is not deleting — the entry survives and can be restarted later. **Confirmation echo-back:** `workflow-cancel` requires `confirmation: "CANCEL WORKFLOW {entry_id}"`; `workflow-restart` requires `confirmation: "RESTART WORKFLOW {entry_id}"`. On a mismatch the error returns the exact expected phrase — echo it back precisely. Always confirm with the human before these operations. +**Confirmation phrases are NOT self-authorization.** The echo-back exists so a human's "yes" gates the action — it is not a formality you satisfy on your own. Never construct and send a destructive confirmation in the same breath as discovering the operation; the user must have explicitly asked for THIS specific destructive action first. If they have not, stop and state exactly what you would do and what confirmation you need. This applies to every destructive tool: `workflow-cancel`, `workflow-restart`, `steps-delete`, and `steps-process` rejections/reverts. + +**Never use writes to discover.** Do not reject, delete, cancel, or restart anything to "find out" how a step is configured or what a validation rule is — that mutates real workflows and real entries. Discovery is always read-only: `steps-list`, `steps-get`, `system-step-type-schema`, `workflow-status-get`, `status-search`. If a config read is permission-denied, report that; do not substitute a destructive probe. + ### Search `status-search` filters: `form_id`, `status` (workflow final status), `step_id`, `assignee_key`, date range, paging. With view-all permission it spans all entries; without it, results are silently limited to the user's **own submissions** — if results look incomplete, that's why (tell the user which permission is missing rather than retrying). diff --git a/skills/gravity-flow-abilities/references/feasibility.md b/skills/gravity-flow-abilities/references/feasibility.md index e04bec7..3192741 100644 --- a/skills/gravity-flow-abilities/references/feasibility.md +++ b/skills/gravity-flow-abilities/references/feasibility.md @@ -7,6 +7,7 @@ Customers often ask whether a workflow design is possible before building. These - **Approval chains**: unlimited sequential approval steps; per-step assignees (users, roles, emails, field-resolved); `"all"` (unanimous) or `"any"` approval policy per step; conditions on any step (GF conditional logic on field values) enable branch-per-department/amount/type designs. - **Approval loops / send-back**: approval steps can **revert** to a user-input step (revise-and-resubmit loops) and reject-destinations can point at any earlier step — multi-round review loops are native. - **User input steps**: assignees edit a configured subset of fields (`editable_fields`), can save progress (`in_progress`) before completing; display-fields config controls what each step's assignees see (reviewer-privacy patterns). +- **Consent capture**: use the native Gravity Forms **Consent field** (a required GDPR checkbox with descriptive text) — not a plain checkbox. Signature capture needs the Signature add-on. - **Two-actor patterns** (candidate submits, referent completes hidden fields): initial submission + a user_input step whose editable fields are the referent-only fields. Field-level conditional logic on steps requires the step's "field conditional logic" setting. - **External participants without WordPress accounts**: email assignees act via signed token links from notification emails — supported for approval and user input steps. They cannot use MCP. - **Scheduling/delays**: steps can be scheduled (delay after previous step, or date/date-field based). Expiration with destinations handles "no response by X" routing; resend settings handle reminders. @@ -23,14 +24,32 @@ Customers often ask whether a workflow design is possible before building. These ## Documentation links (verified) -Cite these when answering feasibility/design questions — deep-link by searching within them rather than guessing article URLs: - -- Gravity Flow docs home: https://docs.gravityflow.io/ -- Flow fundamentals (steps, assignees, notifications, conditions): https://docs.gravityflow.io/category/flow-fundamentals/ -- User guides (worked workflow examples): https://docs.gravityflow.io/category/user-guides/ -- Extensions (PDF, Form Connector, Stripe, Checklists…): https://docs.gravityflow.io/category/extensions/ -- Developer reference (hooks, REST): https://docs.gravityflow.io/category/developers/ -- Gravity Forms docs (fields, conditional logic, feeds, merge tags): https://docs.gravityforms.com/ +Cite the SPECIFIC article for each capability you mention — a feasibility answer without deep links is incomplete. Verified URLs: + +**Gravity Flow — steps** +- Approval step: https://docs.gravityflow.io/approval-step-type/ +- User Input step: https://docs.gravityflow.io/user-input-step-type/ +- Notification step: https://docs.gravityflow.io/notification-step-type/ +- Choice Routing step: https://docs.gravityflow.io/choice-routing-step/ +- Outgoing Webhook step: https://docs.gravityflow.io/outgoing-webhook-step-type/ +- Update User step: https://docs.gravityflow.io/update-user-step/ +- Types of steps and where they come from: https://docs.gravityflow.io/types-of-steps-and-where-they-come-from/ +- Step condition: https://docs.gravityflow.io/condition/ +- Next Step / routing: https://docs.gravityflow.io/next-step/ +- Schedule / delay: https://docs.gravityflow.io/schedule/ + +**Gravity Flow — categories** (deep-link within when the topic isn't listed above) +- Flow fundamentals: https://docs.gravityflow.io/category/flow-fundamentals/ +- Extensions (PDF, Form Connector, Stripe…): https://docs.gravityflow.io/category/extensions/ +- Developer reference: https://docs.gravityflow.io/category/developers/ + +**Gravity Forms — fields & logic** +- Consent field (GDPR checkbox): https://docs.gravityforms.com/consent/ +- Signature field (needs the Signature add-on): https://docs.gravityforms.com/signature/ +- Enabling conditional logic for fields: https://docs.gravityforms.com/enable-conditional-logic/ +- Conditional logic for notifications: https://docs.gravityforms.com/enable-conditional-logic-for-notifications/ +- Conditional logic for feeds: https://docs.gravityforms.com/enabling-conditional-logic-for-feeds/ +- Merge tags: https://docs.gravityforms.com/category/user-guides/merge-tags-getting-started/ ## Scope expectations From 2f8148384b565c87511bad2fd9f1c3edeba67a6b Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Tue, 28 Jul 2026 13:06:36 -0700 Subject: [PATCH 12/21] docs(gravity-flow): update for unified GF MCP host + deactivate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flow contributes its tools to Gravity Forms' single MCP endpoint (no separate Flow endpoint). Gating and skill download move to Forms → Settings → MCP, in the Gravity Flow metabox. Also document steps-update is_active as the reversible deactivate — the safe alternative to permanent steps-delete. --- skills/gravity-flow-abilities/SKILL.md | 27 +++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 46b4ebc..fe9aab1 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -2,7 +2,7 @@ name: gravity-flow-abilities description: Workflow guidance for AI agents using Gravity Flow abilities via the WordPress Abilities API (MCP). Load this skill when interacting with any gravityflow/* MCP tools — triaging the workflow inbox, approving/rejecting entries, submitting user input, diagnosing stuck workflows, admin actions (cancel/restart/send-to-step), or workflow reports and activity. Provides status vocabulary, assignee-key semantics, sequencing rules, and pitfall avoidance that tool schemas alone cannot express. license: GPL-2.0+ -compatibility: Requires a WordPress site with Gravity Flow 3.2+ (MCP enabled under Workflow → Settings → MCP) and Gravity Forms 2.9+ +compatibility: Requires Gravity Forms as the MCP host (MCP enabled under Forms → Settings → MCP, with Gravity Flow's tools enabled in its metabox) and Gravity Flow 3.2+ contributing its abilities to that endpoint metadata: author: gravityforms version: "0.1.0" @@ -28,17 +28,17 @@ metadata: **Step configuration (read)** → `steps-list` (a form's steps, summary shape; optional `entry_id` adds per-entry statuses), `steps-get` (one step's sanitized settings) -**Build (write)** → `system-step-type-schema` (settings schema for one type — call FIRST), `steps-create`, `steps-update` (partial merge), `steps-delete` +**Build (write)** → `system-step-type-schema` (settings schema for one type — call FIRST), `steps-create`, `steps-update` (partial merge; also toggles `is_active` to deactivate/reactivate a step — the reversible alternative to delete), `steps-delete` **Analyze** → `reports-get` (aggregate completion counts and durations across 7 scopes), `activity-list` (recent workflow event feed — the audit view) -All ability names use format `gravityflow/{area}-{action}`. On the dedicated endpoint, tool names are hyphenated: `gravityflow-inbox-list`. +All ability names use format `gravityflow/{area}-{action}`; as MCP tools they appear hyphenated (`gravityflow-inbox-list`) on Gravity Forms' endpoint, alongside the `gravityforms-*` tools. **Step writes are allowlisted:** only `approval`, `user_input`, and `notification` steps can be created or modified programmatically; other types (feed add-on steps, routing, etc.) return an error naming the allowlist — configure those in the Flow admin UI. `workflow_start` / `workflow_complete` are auto-managed sentinels and can never be touched. Never attempt to create or modify workflow steps via `gravityforms/feeds-*` — Flow actively refuses generic feed tools targeting its steps and redirects you to `gravityflow/steps-*`. ## Division of Labor: Flow vs Forms -Gravity Flow abilities return **entry IDs and workflow state — never entry field values**. Hydrate entry data with `gravityforms/entries-get`. The two servers are complementary and usually both connected: +Gravity Flow abilities return **entry IDs and workflow state — never entry field values**. Hydrate entry data with `gravityforms/entries-get`. Both toolsets are served by Gravity Forms' single MCP endpoint, so they are always available together: - `gravityflow/*` — workflow: inbox, statuses, assignees, timeline, processing, admin actions, reports - `gravityforms/*` — data: entry field values, form structure, submissions, notes @@ -50,22 +50,22 @@ Gravity Flow abilities return **entry IDs and workflow state — never entry fie ## Access Modes -Flow's MCP surface is gated in **Workflow → Settings → MCP**, independently of Gravity Forms' MCP settings: +Gravity Forms is the MCP host; Flow contributes its tools to GF's endpoint. Everything is gated in **Forms → Settings → MCP**, in the **Gravity Flow** metabox below Gravity Forms' own tools: -1. **Enable MCP** (default: off) — master switch. When off, no `gravityflow/*` ability is exposed or callable. -2. **Per-tool checkboxes** (default: every tool off) — grouped into **Read-only Tools** and **Write & Destructive Tools**. A tool is exposed and callable only when its box is checked. There is no blanket "read access." +1. **Enable MCP** (Gravity Forms' master switch, default off) — gates the whole surface. When off, no `gravityflow/*` (or `gravityforms/*`) ability is exposed or callable. +2. **Per-tool checkboxes** (default: every tool off) — Flow's metabox splits its tools into **Read-only** and **Write** columns; a tool is exposed and callable only when its box is checked. There is no blanket "read access." -**Read-only group** (each opt-in): `inbox-list`, `inbox-count`, `status-search`, `workflow-status-get`, `timeline-get`, `steps-list`, `steps-get`, `system-info`, `system-step-types`, `reports-get`, `activity-list` +**Read-only group** (each opt-in): `inbox-list`, `inbox-count`, `status-search`, `workflow-status-get`, `timeline-get`, `steps-list`, `steps-get`, `system-info`, `system-step-types`, `system-step-type-schema`, `reports-get`, `activity-list` -**Write & destructive group** (each opt-in): `timeline-note-add`, `steps-process`, `steps-restart`, `workflow-send-to-step`, `workflow-restart`, `workflow-cancel` +**Write & destructive group** (each opt-in): `timeline-note-add`, `steps-process`, `steps-restart`, `workflow-send-to-step`, `workflow-restart`, `workflow-cancel`, `steps-create`, `steps-update`, `steps-delete` -If a tool is absent from discovery or a call is permission-denied, the admin has not enabled that specific tool (or MCP itself is off), or your user lacks the capability. Do not attempt workarounds — tell the user to enable the exact tool in Workflow → Settings → MCP, or which capability is missing. +If a tool is absent from discovery or a call is permission-denied, the admin has not enabled that specific tool (or Gravity Forms' MCP is off), or your user lacks the capability. Do not attempt workarounds — tell the user to enable the exact tool in Forms → Settings → MCP (the Gravity Flow metabox), or which capability is missing. **Admin-screen handoff URLs** (when a change needs the Flow admin UI, give the user a direct link): step settings live at `{site}/wp-admin/admin.php?page=gf_edit_forms&view=settings&subview=gravityflow&id={form_id}&fid={step_id}` — build these from `steps-list` output when recommending manual configuration. ### Endpoint -The dedicated server lives at `/wp-json/mcp/gravityflow` and works regardless of Gravity Forms' MCP toggle. Tool names there are direct (`gravityflow-steps-process`) with full JSON Schemas. When the site-wide WordPress MCP server exists (GF site mode), Flow abilities also appear through its `discover-abilities` / `execute-ability` meta-tools. Same abilities either way; only transport differs. +Flow's tools ride Gravity Forms' MCP endpoint — there is no separate Flow endpoint. On GF's dedicated endpoint (`/wp-json/mcp/gravityforms`) each tool is listed directly (`gravityflow-steps-process`) with full JSON Schemas, alongside the `gravityforms-*` tools. In GF site-endpoint mode the same abilities appear through the site MCP server's `discover-abilities` / `execute-ability` meta-tools. Same abilities either way; only transport differs. ## Acting User Model @@ -136,7 +136,8 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a 2. `steps-create { form_id, step_type, step_name, assignees, destination_complete, condition, settings }` — first-class inputs cover the common cases; everything else goes in `settings`. Assignees use `type|id` keys; the internal assignee-source discriminator is handled for you. New steps append to the end of the workflow. Conditions are GF conditional-logic objects (`{actionType, logicType, rules}`) gating whether the step runs. 3. Verify with `steps-list` — check order and destinations. 4. `steps-update { step_id, ... }` — partial merge; a step's type is immutable. **When entries are in flight on the step, the update is blocked until you echo `UPDATE STEP {id} AFFECTING {n} ENTRIES`** — and changing assignee settings reroutes ALL of those entries (assignees re-resolved and re-notified automatically). If the user's request itself already specifies the change ("assign it to the finance role"), that IS the authorization — echo the count-bearing confirmation and proceed, reporting the blast radius in your summary. Only stop to confirm when the request leaves the decision open or the in-flight impact plainly exceeds what the user described. -5. `steps-delete { step_id, confirmation }` — always needs `DELETE STEP {id} FROM FORM {form_id}`; if entries sit on the step the error escalates to a count-bearing phrase (`DELETE STEP {id} AFFECTING {n} ENTRIES`). Prefer moving in-flight entries with `workflow-send-to-step` first. Deletes are blocked while other steps' destinations route to the step — update those destinations first (the error names them). +5. **Deactivate instead of delete when the intent is "stop this step from running."** `steps-update { step_id, is_active: false }` pauses a step reversibly — its config and history are kept and it can be switched back on with `is_active: true`. A bare toggle needs no confirmation phrase. This is the safe, undoable alternative; reach for delete only when a step must be permanently removed. +6. `steps-delete { step_id, confirmation }` — **permanent, no undo.** Always needs `DELETE STEP {id} FROM FORM {form_id}`; if entries sit on the step the error escalates to a count-bearing phrase (`DELETE STEP {id} AFFECTING {n} ENTRIES`). Prefer moving in-flight entries with `workflow-send-to-step` first, or deactivating (above) rather than deleting. Deletes are blocked while other steps' destinations route to the step — update those destinations first (the error names them). Every validation error names the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. @@ -185,7 +186,7 @@ Full vocabulary, per-assignee meta, and the entries-search escape hatch: [refere | `field_values` on an approval step, or on non-editable fields | Rejected fail-closed | user_input steps only; respect the editable-field list in the error | | Sorting inbox by `date_created` | Oldest *submission* ≠ longest-*waiting* task | Sort by `workflow_timestamp` | | Treating `status-search`'s thin results as "no entries" | Degraded mode shows own submissions only | Report the missing view-all permission instead | -| Tool absent from discovery | Admin hasn't enabled that tool (or MCP is off) | Point to Workflow → Settings → MCP; no workarounds | +| Tool absent from discovery | Admin hasn't enabled that tool (or GF's MCP is off) | Point to Forms → Settings → MCP (Gravity Flow metabox); no workarounds | | Building/modifying steps via `gravityforms/feeds-*` | Actively refused by Flow | Use `gravityflow/steps-create` / `steps-update` / `steps-delete` | | Guessing settings keys on steps-create/update | Unknown keys rejected, nothing saved | Call `system-step-type-schema` first | | Updating assignees on a step with in-flight entries without warning the user | Every in-flight entry is rerouted and re-notified | Echo the count-bearing confirmation only after the user confirms the blast radius | From aae385b20cb311a6532fdb80310e8bf3f82c20db Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Tue, 28 Jul 2026 13:46:44 -0700 Subject: [PATCH 13/21] docs(gravity-flow): verify absence before reporting it; unanimous is configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two gaps surfaced by the phase-c validation pass: - Never conclude a form/workflow/step doesn't exist from a single system-info read — confirm with steps-list/status-search first (system-info is a summary). - The unanimous vs any approval policy is a configurable step setting; when a workflow is stuck on it, steps-update the policy is the real remedy, not just restart/re-notify. --- skills/gravity-flow-abilities/SKILL.md | 1 + skills/gravity-flow-abilities/references/status-reference.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index fe9aab1..15a3090 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -75,6 +75,7 @@ MCP authenticates as a real WordPress user (application password). Every ability - **Processing as self** requires the authenticated user to be a *pending assignee* on the entry's current step — the same rule the web inbox enforces. - **Delegated processing** (`steps-process` with `assignee_key`) acts on behalf of another pending assignee. It requires workflow-admin permission AND a `note` explaining why. The action is dual-attributed: the timeline note names both identities (`[On behalf of X (key)] …`) and the activity feed records a `delegated` event. A delegated action is never indistinguishable from the assignee acting themselves. - **Entry visibility:** entry-scoped abilities (`workflow-status-get`, `timeline-get`, `timeline-note-add`, `steps-list` with `entry_id`) succeed only when the user has view-all permission, is the entry's submitter, or is a current/past assignee. An invisible entry returns the same error as a nonexistent one — do not retry or enumerate; verify existence via `status-search` or ask the user. +- **Never report absence without verifying.** `system-info`'s workflow-form list is a *summary* — a form not obviously matching the goal there is NOT proof it lacks a workflow or doesn't exist. Before telling the user "no such form/workflow/step," confirm: call `steps-list { form_id }` on the candidate form (workflow-enabled forms have steps) or `status-search`. Concluding absence from a single `system-info` read is a failure mode; the target is usually discoverable with one more read. ## Core Workflows diff --git a/skills/gravity-flow-abilities/references/status-reference.md b/skills/gravity-flow-abilities/references/status-reference.md index 1eab505..f1e851d 100644 --- a/skills/gravity-flow-abilities/references/status-reference.md +++ b/skills/gravity-flow-abilities/references/status-reference.md @@ -29,6 +29,8 @@ A rejected approval does not necessarily end the workflow — reject destination Within a pending step, each assignee has their own status (`pending`, `approved`, `rejected`, `complete`). Multi-assignee approval steps combine them by policy — `workflow-status-get`'s `current_step.approval_policy` states it directly: `"all"` (unanimous — every assignee must act; one approval with the step still pending is normal, name who is still pending) or `"any"` (first decision wins). The live list is `assignees[] { key, display_name, status }`. +This policy is a **configurable step setting**, not a fixed law. If a workflow is stuck because it demands unanimous approval and the user wants any-one-approves instead (or vice versa), that is a `steps-update` on the step's approval settings — surface it as the real remedy (or a decision point to confirm), rather than only restarting/re-notifying to chase the missing approvals. + ## Assignee key format `type|id`: From 4b742c447a36fc6e606b3e3053f561bd2f5a84f1 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Tue, 28 Jul 2026 16:39:33 -0700 Subject: [PATCH 14/21] docs(gravity-forms): document notification + confirmation write abilities Add the 7 new tools to routing + access-mode groups (catalog 25->32), a Notifications & Confirmations workflow section (read-before-write, per-type required fields, default-confirmation-protected, conditional routing), and correct the stale 'via forms-update' guidance to the dedicated targeted tools. --- skills/gravity-forms-abilities/SKILL.md | 27 +++++++++++++++---- .../references/conditional-logic.md | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/skills/gravity-forms-abilities/SKILL.md b/skills/gravity-forms-abilities/SKILL.md index 0deeb00..bceacb5 100644 --- a/skills/gravity-forms-abilities/SKILL.md +++ b/skills/gravity-forms-abilities/SKILL.md @@ -12,7 +12,7 @@ metadata: ## Ability Routing -25 abilities across 7 categories. Select based on intent: +32 abilities across 8 categories. Select based on intent: **Discover** → `system-field-types` (field types), `system-info` (site identity, version, license, add-ons — includes `site_url` and `site_name` for multi-site disambiguation), `forms-list` (all forms as summaries: id, title, is_active, date_created, field_count), `forms-get` (single form with full field detail) @@ -26,7 +26,9 @@ metadata: **Integrations** → `feeds-list`, `feeds-create`, `feeds-update`, `feeds-delete` -**Notifications** → `notifications-list`, `notifications-send` (re-send for existing entry) +**Notifications** → `notifications-list`, `notifications-create` / `notifications-update` / `notifications-delete` (targeted config edits — the canonical path, NOT forms-update), `notifications-send` (re-send for an existing entry) + +**Confirmations** → `confirmations-list`, `confirmations-create` / `confirmations-update` / `confirmations-delete` (the message / page / redirect a submitter sees after submitting) **Audit** → `notes-list`, `notes-add` (annotate entries) @@ -41,9 +43,9 @@ The MCP settings page (GF Settings → MCP) gates access with a master switch pl So an ability is available only when **Enable MCP is on AND that specific tool is checked**. A freshly enabled integration exposes nothing until tools are opted in. -**Read-only group** (each opt-in): `forms-get`, `forms-list`, `forms-analyze-logic`, `entries-get`, `entries-search`, `entries-count`, `submissions-validate`, `feeds-list`, `notifications-list`, `notes-list`, `system-info`, `system-field-types` +**Read-only group** (each opt-in): `forms-get`, `forms-list`, `forms-analyze-logic`, `entries-get`, `entries-search`, `entries-count`, `submissions-validate`, `feeds-list`, `notifications-list`, `confirmations-list`, `notes-list`, `system-info`, `system-field-types` -**Write & destructive group** (each opt-in): `forms-create`, `forms-update`, `forms-delete`, `forms-duplicate`, `entries-create`, `entries-update`, `entries-delete`, `feeds-create`, `feeds-update`, `feeds-delete`, `submissions-submit`, `notifications-send`, `notes-add` +**Write & destructive group** (each opt-in): `forms-create`, `forms-update`, `forms-delete`, `forms-duplicate`, `entries-create`, `entries-update`, `entries-delete`, `feeds-create`, `feeds-update`, `feeds-delete`, `submissions-submit`, `notifications-send`, `notifications-create`, `notifications-update`, `notifications-delete`, `confirmations-create`, `confirmations-update`, `confirmations-delete`, `notes-add` If an ability is not available (absent from discovery, or a direct call is permission-denied), the admin has not enabled that specific tool (or MCP itself is off). Do not attempt workarounds — tell the user to enable the exact tool, and Enable MCP, in GF Settings → MCP. @@ -106,7 +108,22 @@ Use `{admin_email}` for site admin, or a specific address. `{all_fields}` render 2. Modify the returned fields array (add/remove/change) 3. Call `forms-update` with the complete form object -`notifications` and `confirmations` merge by key — safe for partial updates. `fields` does NOT merge. +`notifications` and `confirmations` passed to `forms-update` merge by key — handy when standing up a whole form at once. For **targeted edits to a single notification or confirmation — including deletion — use the dedicated tools below**, not `forms-update` (they don't round-trip the whole form, so they can't drop fields). `fields` does NOT merge. + +### Notifications & Confirmations + +Notifications (emails) and confirmations (the message / page / redirect shown after submit) are keyed objects on the form. Edit them with the dedicated tools — the canonical path. + +**Always read before you write.** Call `notifications-list` / `confirmations-list` first to get the `id` of the item to change (the list carries the full objects; there is no separate "get one"). + +- **Create** → `notifications-create` / `confirmations-create` with a settings object; the tool generates and returns the id. + - A notification needs `name`, `event` (e.g. `form_submission`), `to` (an email, or a field id / routing per `toType`), `subject`, and `message` (merge tags + HTML OK). + - A confirmation needs `name` and a `type`: `message` (needs `message`), `page` (needs `pageId`), or `redirect` (needs `url`). +- **Update** → `notifications-update` / `confirmations-update` with the `notification_id` / `confirmation_id` and only the keys to change (partial merge; the id is immutable). The result must still satisfy the required fields above. +- **Delete** → `notifications-delete` / `confirmations-delete` by id. The form's **default confirmation cannot be deleted** (edit it instead) — every form keeps one fallback. +- **Route by condition** → add a `conditionalLogic` object (`{actionType, logicType, rules}`) so a notification sends — or a confirmation shows — only on matching submissions. For notifications `actionType: "show"` means "send when matched"; the default confirmation shows when none match. See [references/conditional-logic.md](references/conditional-logic.md). + +Common shapes: route different emails to clinicians vs patients (two notifications, each with `conditionalLogic` on a role/type field); show a tailored thank-you page per department (conditional confirmations + the default as fallback). ### Submitting a Form diff --git a/skills/gravity-forms-abilities/references/conditional-logic.md b/skills/gravity-forms-abilities/references/conditional-logic.md index 1229c1d..6532a61 100644 --- a/skills/gravity-forms-abilities/references/conditional-logic.md +++ b/skills/gravity-forms-abilities/references/conditional-logic.md @@ -176,7 +176,7 @@ CL is set as a property on individual fields during `forms-update`: ### Send notification only for specific selections -Set on the notification object (via `forms-update` on the `notifications` property): +Set the `conditionalLogic` on the notification via `notifications-update` (the canonical targeted path; `notifications-create` accepts it too): ```json { From 729a53d62033e2e046fa5e6bfaf51387e77231ed Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Tue, 28 Jul 2026 17:41:19 -0700 Subject: [PATCH 15/21] docs(gravity-flow): build it, don't just describe it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the dominant residual failure across the scenario suite (agents producing a sound design but creating nothing, over-blocking a whole build on a single hand-off-able detail like an assignee identity). For build requests: create the structure with a defensible placeholder and hand off ONLY the unknown detail with the exact steps-update to finalize — a plan with nothing created fails a build request; distinct from pure feasibility questions. --- skills/gravity-flow-abilities/SKILL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index 15a3090..fe50373 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -133,6 +133,8 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a ### Building workflow steps +**Build it — don't just describe it.** When asked to set up a workflow, actually create the steps; do not stop at a design and ask permission to proceed. If a detail you genuinely can't know blocks *one* setting — the exact user/role to assign, a real URL — still build the full structure with a defensible placeholder (the closest matching `role|…`, or a clearly-named stand-in) and hand off ONLY that specific detail, with the exact `steps-update` call that finalizes it. Producing a plan while creating nothing fails a build request. (This is different from a pure *feasibility* question — see below — where you answer and mutate nothing.) + 1. `system-step-type-schema { step_type, form_id }` — the settings vocabulary for the type. **Unknown settings keys are rejected on create/update (nothing saves), so never guess keys.** 2. `steps-create { form_id, step_type, step_name, assignees, destination_complete, condition, settings }` — first-class inputs cover the common cases; everything else goes in `settings`. Assignees use `type|id` keys; the internal assignee-source discriminator is handled for you. New steps append to the end of the workflow. Conditions are GF conditional-logic objects (`{actionType, logicType, rules}`) gating whether the step runs. 3. Verify with `steps-list` — check order and destinations. From 02af4a6cbfc7627bb540ddf7c34ab5e4a3c93523 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Wed, 29 Jul 2026 15:21:41 -0700 Subject: [PATCH 16/21] docs(gravity-flow): name view-all cap, fix submitter notify, sweep steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the validation loop: name the gravityflow_status_view_all capability where view-all is discussed (S-076); warn that notification recipients must be valid assignee keys — no 'submitter' token, and bad recipients save silently and email no one (S-063/S-067); and add a principle to enumerate steps for any workflow- content edit rather than stopping at form-level settings (S-032/S-069). --- skills/gravity-flow-abilities/SKILL.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/skills/gravity-flow-abilities/SKILL.md b/skills/gravity-flow-abilities/SKILL.md index fe50373..a5def79 100644 --- a/skills/gravity-flow-abilities/SKILL.md +++ b/skills/gravity-flow-abilities/SKILL.md @@ -74,8 +74,9 @@ MCP authenticates as a real WordPress user (application password). Every ability - **Inbox is personal.** `inbox-list` returns tasks assigned to the authenticated user (directly, via role, or via resolved fields). Different credentials = different inbox. - **Processing as self** requires the authenticated user to be a *pending assignee* on the entry's current step — the same rule the web inbox enforces. - **Delegated processing** (`steps-process` with `assignee_key`) acts on behalf of another pending assignee. It requires workflow-admin permission AND a `note` explaining why. The action is dual-attributed: the timeline note names both identities (`[On behalf of X (key)] …`) and the activity feed records a `delegated` event. A delegated action is never indistinguishable from the assignee acting themselves. -- **Entry visibility:** entry-scoped abilities (`workflow-status-get`, `timeline-get`, `timeline-note-add`, `steps-list` with `entry_id`) succeed only when the user has view-all permission, is the entry's submitter, or is a current/past assignee. An invisible entry returns the same error as a nonexistent one — do not retry or enumerate; verify existence via `status-search` or ask the user. +- **Entry visibility:** entry-scoped abilities (`workflow-status-get`, `timeline-get`, `timeline-note-add`, `steps-list` with `entry_id`) succeed only when the user holds the **`gravityflow_status_view_all`** capability, is the entry's submitter, or is a current/past assignee. An invisible entry returns the same error as a nonexistent one — do not retry or enumerate; verify existence via `status-search` or ask the user. To let someone (e.g. a manager) see *other* users' entries without making them a WordPress administrator, the answer is to grant `gravityflow_status_view_all` to their role — that exact capability, never a broader role. (The MCP surface exposes no role/capability directory, so you cannot enumerate which roles already hold it; name the capability and how to grant it.) - **Never report absence without verifying.** `system-info`'s workflow-form list is a *summary* — a form not obviously matching the goal there is NOT proof it lacks a workflow or doesn't exist. Before telling the user "no such form/workflow/step," confirm: call `steps-list { form_id }` on the candidate form (workflow-enabled forms have steps) or `status-search`. Concluding absence from a single `system-info` read is a failure mode; the target is usually discoverable with one more read. +- **Workflow content lives in the steps, not just the form.** When a request concerns instructions, notifications, assignees, due dates, or any per-step content ("update the dates", "add instructions", "who is assigned", "which notifications go out"), enumerate the workflow with `steps-list` then `steps-get` on each step — form-level settings (title, form schedule, form notifications) are only part of the surface. Editing the form and stopping there, then claiming the request is complete, is a failure mode: the step layer (step instructions, assignee/reject notifications, step schedules and due dates) usually holds most of what the request targets. Sweep every step before reporting done. ## Core Workflows @@ -118,7 +119,7 @@ Only conclude "not discoverable" after the accessible routes are exhausted, and ### Search -`status-search` filters: `form_id`, `status` (workflow final status), `step_id`, `assignee_key`, date range, paging. With view-all permission it spans all entries; without it, results are silently limited to the user's **own submissions** — if results look incomplete, that's why (tell the user which permission is missing rather than retrying). +`status-search` filters: `form_id`, `status` (workflow final status), `step_id`, `assignee_key`, date range, paging. With the `gravityflow_status_view_all` capability it spans all entries; without it, results are silently limited to the user's **own submissions** — if results look incomplete, that's why (tell the user which permission is missing rather than retrying). For filters `status-search` doesn't offer, use `gravityforms/entries-search` with workflow meta keys as `field_filters` — key table in [references/status-reference.md](references/status-reference.md). @@ -142,7 +143,9 @@ This is the ONE sanctioned exception to "don't edit workflow fields mid-step," a 5. **Deactivate instead of delete when the intent is "stop this step from running."** `steps-update { step_id, is_active: false }` pauses a step reversibly — its config and history are kept and it can be switched back on with `is_active: true`. A bare toggle needs no confirmation phrase. This is the safe, undoable alternative; reach for delete only when a step must be permanently removed. 6. `steps-delete { step_id, confirmation }` — **permanent, no undo.** Always needs `DELETE STEP {id} FROM FORM {form_id}`; if entries sit on the step the error escalates to a count-bearing phrase (`DELETE STEP {id} AFFECTING {n} ENTRIES`). Prefer moving in-flight entries with `workflow-send-to-step` first, or deactivating (above) rather than deleting. Deletes are blocked while other steps' destinations route to the step — update those destinations first (the error names them). -Every validation error names the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. +Most validation errors name the concrete problem (unknown keys, invalid assignee, bad destination, invalid enum value) — fix exactly what it names and retry. + +**Notification recipients must be valid assignee keys — there is no `submitter` recipient.** To email the person who submitted the entry (e.g. a reject notification telling an applicant the outcome), route the notification to the submission's **email field** (`email_field|{id}`) or a literal `email|{address}` — never a bare string like `"submitter"`. Unlike settings keys, notification **recipients are not always validated on save**: an invalid recipient such as `submitter` (or a non-array recipient value) can be stored *without error* and then silently send to no one. So after configuring any step or reject/revert notification, confirm the recipient resolves to a real `type|id` / `email_field` target, and never report "the submitter will be notified" unless the recipient is a valid resolvable key. When the submitter's email isn't captured in a field, that's a genuine gap to hand back to the user — not something to paper over with a `submitter` placeholder. ### Feasibility and design questions @@ -192,6 +195,7 @@ Full vocabulary, per-assignee meta, and the entries-search escape hatch: [refere | Tool absent from discovery | Admin hasn't enabled that tool (or GF's MCP is off) | Point to Forms → Settings → MCP (Gravity Flow metabox); no workarounds | | Building/modifying steps via `gravityforms/feeds-*` | Actively refused by Flow | Use `gravityflow/steps-create` / `steps-update` / `steps-delete` | | Guessing settings keys on steps-create/update | Unknown keys rejected, nothing saved | Call `system-step-type-schema` first | +| Using `submitter` (or a bare string) as a notification recipient | Silently stored, emails no one — recipients aren't always validated | Route to the submission's `email_field|{id}` / `email|{address}`; verify the recipient resolves | | Updating assignees on a step with in-flight entries without warning the user | Every in-flight entry is rerouted and re-notified | Echo the count-bearing confirmation only after the user confirms the blast radius | | Deleting a step other steps route to | Blocked — dangling destinations | Update the referencing steps' destinations first (the error names them) | From 2c7566b5762538480892c677bfe34fbede74f326 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Wed, 29 Jul 2026 17:06:07 -0700 Subject: [PATCH 17/21] chore: ignore local Claude settings file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 028d6ba..33b58d2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .DS_Store .env dist/ +.claude/settings.local.json From 28211c4e4c39e7e1fd413ccf34b1a6d0ea1c964f Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Wed, 29 Jul 2026 18:04:12 -0700 Subject: [PATCH 18/21] docs(gravity-forms): note the GF 3.0 international phone field value shape The phone field's phoneFormat:'formatted' mode stores its value as a JSON object (country/national/formatted/e164), not a plain string. Document how that affects creating the field and reading/writing/submitting entry values via the abilities. --- skills/gravity-forms-abilities/SKILL.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/skills/gravity-forms-abilities/SKILL.md b/skills/gravity-forms-abilities/SKILL.md index bceacb5..c4d8f02 100644 --- a/skills/gravity-forms-abilities/SKILL.md +++ b/skills/gravity-forms-abilities/SKILL.md @@ -80,6 +80,11 @@ The agent does not need to know which mode is active — the MCP client handles Never guess field types. `system-field-types` returns `supports_choices`, `has_inputs`, `default_inputs`, and support flags for each type. +**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting: `"standard"` (a plain-string value) or `"formatted"` (international, paired with a `defaultCountry` like `"us"`). A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it: +- **Creating the field** (`forms-create` / `forms-update`): set `"phoneFormat": "formatted"` (plus `defaultCountry`) for international; omit it for a standard phone. +- **Reading** (`entries-get` / `entries-search`): a formatted phone comes back as that JSON object, not a plain number — parse it (use `e164` for the canonical number), don't treat it as a string. +- **Writing / submitting** (`entries-update`, `submissions-submit`, `submissions-validate`): supply the JSON object with a valid `e164`, not a bare number, or validation fails. (`entries-create` is a raw insert, so match the same shape to keep the value usable.) + **Default admin notification template:** ```json { From 717166853da460a62fb8995a7d5bda957fdc1d15 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 3 Aug 2026 12:00:26 -0700 Subject: [PATCH 19/21] fix(gravity-forms-abilities): require explicit phoneFormat and document all valid values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The phone reference was missing the 'formatted' value and SKILL.md told agents to omit phoneFormat for standard phones — but API-created fields skip the editor's normalization, so an omitted or invented format broke form rendering. Document the three valid values (plus defaultCountry and showCountryCode) and instruct agents to always set phoneFormat explicitly. --- skills/gravity-forms-abilities/SKILL.md | 4 ++-- .../references/field-config.md | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/skills/gravity-forms-abilities/SKILL.md b/skills/gravity-forms-abilities/SKILL.md index c4d8f02..b8cbf76 100644 --- a/skills/gravity-forms-abilities/SKILL.md +++ b/skills/gravity-forms-abilities/SKILL.md @@ -80,8 +80,8 @@ The agent does not need to know which mode is active — the MCP client handles Never guess field types. `system-field-types` returns `supports_choices`, `has_inputs`, `default_inputs`, and support flags for each type. -**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting: `"standard"` (a plain-string value) or `"formatted"` (international, paired with a `defaultCountry` like `"us"`). A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it: -- **Creating the field** (`forms-create` / `forms-update`): set `"phoneFormat": "formatted"` (plus `defaultCountry`) for international; omit it for a standard phone. +**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting with exactly three valid values: `"standard"` (US-masked plain string), `"international"` (unformatted plain string), or `"formatted"` (international UI, paired with a `defaultCountry` like `"us"`). Never invent or abbreviate other values — an unknown `phoneFormat` breaks form rendering. A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it: +- **Creating the field** (`forms-create` / `forms-update`): ALWAYS set `phoneFormat` explicitly — `"standard"` for a US phone, `"formatted"` (plus `defaultCountry`) for international. Do not omit it: fields created through the API skip the editor's normalization, so an omitted format is not backfilled. - **Reading** (`entries-get` / `entries-search`): a formatted phone comes back as that JSON object, not a plain number — parse it (use `e164` for the canonical number), don't treat it as a string. - **Writing / submitting** (`entries-update`, `submissions-submit`, `submissions-validate`): supply the JSON object with a valid `e164`, not a bare number, or validation fails. (`entries-create` is a raw insert, so match the same shape to keep the value usable.) diff --git a/skills/gravity-forms-abilities/references/field-config.md b/skills/gravity-forms-abilities/references/field-config.md index d826886..069c6d9 100644 --- a/skills/gravity-forms-abilities/references/field-config.md +++ b/skills/gravity-forms-abilities/references/field-config.md @@ -203,7 +203,16 @@ Example — multi-file with limit: | Property | Type | Description | |---|---|---| -| `phoneFormat` | string | `"standard"` (US: (###) ###-####) or `"international"` (free-form) | +| `phoneFormat` | string | **Always set explicitly.** Exactly one of: `"standard"` (US: (###) ###-####), `"international"` (plain unformatted input), or `"formatted"` (international UI with country selector; stores JSON — see SKILL.md). Never invent other values — an unknown format breaks form rendering. `system-field-types` reports the site's valid values in `format_options` (sites can add custom formats via filter). | +| `defaultCountry` | string | `"formatted"` only. Two-letter lowercase country code preselected in the country dropdown (e.g. `"us"`, `"gb"`). | +| `showCountryCode` | boolean | `"formatted"` only. Whether the country calling code is shown in the input. Defaults to `true`. | + +```json +{ "type": "phone", "label": "Phone", "phoneFormat": "standard" } +``` +```json +{ "type": "phone", "label": "Mobile", "phoneFormat": "formatted", "defaultCountry": "us" } +``` ### Consent (`type: consent`) From ec9498b6a157692ae6d2f2afb1887c7d87ed8391 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Mon, 3 Aug 2026 12:34:58 -0700 Subject: [PATCH 20/21] docs(gravity-forms-abilities): phone pitfall row, workflow step, format_options, and MCP endpoint corrections Add the observed phone failure to the Key Pitfalls table and the form-creation workflow steps, point agents at the format_options key system-field-types now returns, soften the backfill claim to older GF versions (3.0.x now normalizes server-side), and correct the shared MCP endpoint path and meta-tool names to the mcp-adapter-* forms observed live. Bump skill to 1.0.3 with changelog. --- change_log.txt | 5 +++++ skills/gravity-forms-abilities/SKILL.md | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/change_log.txt b/change_log.txt index fd8151e..9498b73 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,3 +1,8 @@ +### 1.0.3 | 2026-08-03 +- gravity-forms-abilities: Require an explicit phoneFormat on phone fields — document all three valid values plus defaultCountry/showCountryCode, and surface the format_options discovery key from system-field-types. +- gravity-forms-abilities: Add a phone pitfall row and a phone step to the form-creation workflow; an omitted phoneFormat breaks rendering on older Gravity Forms versions. +- gravity-forms-abilities: Correct the shared MCP endpoint path (/wp-json/mcp/mcp-adapter-default-server) and the meta-tool names (mcp-adapter-discover-abilities, mcp-adapter-execute-ability, mcp-adapter-get-ability-info). + ### 1.0.2 | 2026-06-18 - gravity-forms-abilities: Add a warning against deprecated Ready Classes in favor of the layout grid. - gravity-forms-abilities: Add documentation for the 100-entry bulk-delete cap and the count/delete loop. diff --git a/skills/gravity-forms-abilities/SKILL.md b/skills/gravity-forms-abilities/SKILL.md index b8cbf76..49077c8 100644 --- a/skills/gravity-forms-abilities/SKILL.md +++ b/skills/gravity-forms-abilities/SKILL.md @@ -5,7 +5,7 @@ license: GPL-2.0+ compatibility: Requires a WordPress site with Gravity Forms 2.9+ and the MCP endpoint enabled (GF Settings → MCP) metadata: author: gravityforms - version: "1.0.0" + version: "1.0.3" --- # Gravity Forms Abilities — Agent Skill @@ -53,18 +53,18 @@ If an ability is not available (absent from discovery, or a direct call is permi The MCP settings page also controls how GF abilities are exposed: -- **Site MCP** (default) — GF abilities are registered on the shared WordPress MCP endpoint (`/wp-json/mcp/v1`). They appear alongside abilities from other plugins and are accessed through the default server's meta-tools (`discover-abilities`, `execute-ability`, `get-ability-schema`). +- **Site MCP** (default) — GF abilities are registered on the shared WordPress MCP endpoint (`/wp-json/mcp/mcp-adapter-default-server`). They appear alongside abilities from other plugins and are accessed through the default server's meta-tools (`mcp-adapter-discover-abilities`, `mcp-adapter-execute-ability`, `mcp-adapter-get-ability-info`). - **Dedicated Endpoint** — GF registers its own MCP server at `/wp-json/mcp/gravityforms`. Each ability becomes a direct MCP tool (e.g., `gravityforms-forms-get` instead of going through `execute-ability`). GF abilities are hidden from the default server. **When using dedicated endpoint mode:** - Tool names use hyphen format: `gravityforms-forms-get`, `gravityforms-entries-search`, etc. (the `/` in ability names is converted to `-`) -- Each tool has its own full JSON Schema — no need to call `get-ability-schema` first +- Each tool has its own full JSON Schema — no need to call `mcp-adapter-get-ability-info` first - The MCP client must be configured to connect to the GF server endpoint separately - Both servers can coexist — the default server serves other plugins' abilities while GF serves its own **When using site MCP mode (default):** -- All abilities accessed through the shared endpoint's `execute-ability` meta-tool -- Tool name passed as a parameter: `execute-ability` with `{"ability": "gravityforms/forms-get", ...}` +- All abilities accessed through the shared endpoint's `mcp-adapter-execute-ability` meta-tool +- Tool name passed as a parameter: `mcp-adapter-execute-ability` with `{"ability": "gravityforms/forms-get", ...}` - This is the simpler setup — one MCP connection covers all plugins The agent does not need to know which mode is active — the MCP client handles routing. The same abilities are available in both modes; only the transport differs. @@ -74,14 +74,14 @@ The agent does not need to know which mode is active — the MCP client handles ### Creating a Form 1. Call `system-field-types` — discover available types and capabilities -2. Build form object with `title` and `fields` array +2. Build form object with `title` and `fields` array — for `phone` fields, ALWAYS set `phoneFormat` explicitly (see the phone section below) 3. **Include a `notifications` object** — `forms-create` does NOT auto-create a default admin notification (unlike the GF admin UI). Without one, submissions are saved but no email is sent. 4. Call `forms-create` — returns `form_id` and `edit_url` Never guess field types. `system-field-types` returns `supports_choices`, `has_inputs`, `default_inputs`, and support flags for each type. -**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting with exactly three valid values: `"standard"` (US-masked plain string), `"international"` (unformatted plain string), or `"formatted"` (international UI, paired with a `defaultCountry` like `"us"`). Never invent or abbreviate other values — an unknown `phoneFormat` breaks form rendering. A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it: -- **Creating the field** (`forms-create` / `forms-update`): ALWAYS set `phoneFormat` explicitly — `"standard"` for a US phone, `"formatted"` (plus `defaultCountry`) for international. Do not omit it: fields created through the API skip the editor's normalization, so an omitted format is not backfilled. +**International phone field (Gravity Forms 3.0).** The `phone` field is still one type, but it has a `phoneFormat` setting with exactly three valid values: `"standard"` (US-masked plain string), `"international"` (unformatted plain string), or `"formatted"` (international UI, paired with a `defaultCountry` like `"us"`). The `system-field-types` phone entry reports the site's valid values as `format_options` — use those exact strings and never invent or abbreviate others; an unknown `phoneFormat` breaks form rendering. A **formatted** phone does NOT store a plain string — its value is a JSON object with the keys `country`, `national`, `formatted`, and `e164` (the `e164` value is validated against the E.164 standard). This changes how every entry-facing ability handles it: +- **Creating the field** (`forms-create` / `forms-update`): ALWAYS set `phoneFormat` explicitly — `"standard"` for a US phone, `"formatted"` (plus `defaultCountry`) for international. Do not omit it: older Gravity Forms versions do not backfill an omitted format on API-created fields, which breaks form rendering. - **Reading** (`entries-get` / `entries-search`): a formatted phone comes back as that JSON object, not a plain number — parse it (use `e164` for the canonical number), don't treat it as a string. - **Writing / submitting** (`entries-update`, `submissions-submit`, `submissions-validate`): supply the JSON object with a valid `e164`, not a bare number, or validation fails. (`entries-create` is a raw insert, so match the same shape to keep the value usable.) @@ -350,6 +350,7 @@ For CL structure details, operators, and common patterns, see [references/condit | Fileupload field without `allowedExtensions`/`maxFileSize` | Accepts any file type/size — security risk | Always set `allowedExtensions` and `maxFileSize` — see field-config reference | | Creating a form without `notifications` | Submissions saved but no email sent — admin never notified | Always include a notification object — see "Creating a Form" workflow | | Creating a name field without `nameFormat: "advanced"` | First/Last sub-inputs render stacked vertically instead of side-by-side | Always set `"nameFormat": "advanced"` and `"size": "large"` on name fields — see field-config reference | +| Creating a phone field without an explicit `phoneFormat` (or with an invented value) | On older GF versions the field fatals when rendered (form editor and frontend); format behavior is undefined | ALWAYS set `phoneFormat` explicitly to one of the values `system-field-types` reports in `format_options` (`"standard"`, `"international"`, `"formatted"`) — see field-config reference | | Passing multiselect values as comma-separated string | Data loss when values contain commas (e.g., "Atlanta, GA") | Always pass multiselect values as an **array**: `"input_1": ["Red", "Blue"]` — see field-config reference | | User asks to "create a form and add it to a page" | Cannot create WordPress pages/posts — only GF abilities exist | Create the form, then tell the user the shortcode `[gravityform id="X" title="true"]` to embed manually. Page/post creation is not currently available via the Abilities API. | | Ability not found (e.g., `forms-create`) | Site admin has not checked that specific tool (or Enable MCP is off) in MCP settings | Tell the user to enable the exact tool — and Enable MCP — in GF Settings → MCP. There is no blanket "write access" toggle; each tool is opted in individually. Do not attempt workarounds. | From 6717c4e064d9d91e84d25ec87b30b868e219a500 Mon Sep 17 00:00:00 2001 From: Samuel Estok Date: Tue, 4 Aug 2026 13:04:50 -0700 Subject: [PATCH 21/21] docs: complete 1.1.0 changelog for gravity-flow skill and forms updates --- change_log.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/change_log.txt b/change_log.txt index 9498b73..1a3f603 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,7 +1,11 @@ -### 1.0.3 | 2026-08-03 -- gravity-forms-abilities: Require an explicit phoneFormat on phone fields — document all three valid values plus defaultCountry/showCountryCode, and surface the format_options discovery key from system-field-types. +### 1.1.0 | 2026-08-04 +- gravity-flow-abilities: Add the skill — workflow guidance for Gravity Flow abilities via the WordPress Abilities API (MCP), covering step CRUD, approval policies, reassignment, and feasibility consultation. - gravity-forms-abilities: Add a phone pitfall row and a phone step to the form-creation workflow; an omitted phoneFormat breaks rendering on older Gravity Forms versions. +- gravity-forms-abilities: Add documentation for the notification and confirmation write abilities. - gravity-forms-abilities: Correct the shared MCP endpoint path (/wp-json/mcp/mcp-adapter-default-server) and the meta-tool names (mcp-adapter-discover-abilities, mcp-adapter-execute-ability, mcp-adapter-get-ability-info). +- gravity-forms-abilities: Document the Gravity Forms 3.0 international phone field value shape. +- gravity-forms-abilities: Fix the entries-search escape-hatch examples to use form_ids. +- gravity-forms-abilities: Require an explicit phoneFormat on phone fields — document all three valid values plus defaultCountry/showCountryCode, and surface the format_options discovery key from system-field-types. ### 1.0.2 | 2026-06-18 - gravity-forms-abilities: Add a warning against deprecated Ready Classes in favor of the layout grid.