> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kodexa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gating actions on completeness

> Configure task template actions to stay disabled until reviewers have actually visited every tab, expanded every panel, and resolved every exception the form binds — the Form Completeness Gate.

[Gating actions on exceptions](/guides/task-templates/gating-actions-on-exceptions) stops a reviewer approving data that is *broken*. The **Form Completeness Gate** stops a reviewer approving data they have not *looked at* — a half-read form where two tabs were never opened and the instruction panel was never expanded.

The gate is a per-form checklist of "you must do X before you can finish" requirements. It is fully opt-in: existing forms and actions are unchanged until you add the props described below.

## The problem

A review form has three tabs — line items, charges, and a panel of reviewer instructions tucked at the bottom. A reviewer who only ever looks at the first tab can still click **Approve**: the document had no open exceptions, the button was enabled, and the task transitions to `approved`. Nobody actually read the charges, and the instruction panel that explained how to handle a multi-stop shipment route was never even expanded.

The gate closes that hole. The reviewer cannot finish until they have visited the data you flagged as mandatory.

## The pattern

A single action property turns the gate on:

| Property              | Type    | Effect                                                                                                                                                                                                                                               |
| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `gatedByCompleteness` | boolean | When `true`, the action stays **disabled** while the form's completeness gate has any outstanding item. An amber info popover next to the button lists what is still outstanding. Default is off — omit it and the action behaves exactly as before. |

It lives under `metadata.actions[].properties` in the task template YAML, alongside the existing exception gates. The gate is evaluated entirely in the UI whenever a requirement is satisfied — there is no round trip to the server.

When the gate blocks an action, an amber info icon appears next to the button. Clicking (or keyboard-focusing) it opens a popover headed **Before continuing:** that lists the outstanding items as bullets. It shows up to six items; if more remain it appends a `…and N more` line.

<Tip>
  `gatedByCompleteness` combines with the exception gates from [Gating actions on exceptions](/guides/task-templates/gating-actions-on-exceptions). All the disable rules are OR-ed together — any one of them is enough to keep the button disabled.
</Tip>

### What counts as outstanding

The gate collects requirements from two places: the layout components on the form, and the open data exceptions on the paths the form binds.

| Source         | Raised by                              | Outstanding until…                           | Item label                                               |
| -------------- | -------------------------------------- | -------------------------------------------- | -------------------------------------------------------- |
| `tab`          | a `v2:tabs` `mustView` index           | the reviewer clicks into that tab            | `Review <tab> tab`                                       |
| `panel-expand` | a `v2:panel` `mustExpand`              | the reviewer expands the panel at least once | `Expand <panel>`                                         |
| `panel-scroll` | a `v2:panel` `mustScroll`              | the reviewer scrolls the panel to its end    | `Scroll to the end of <panel>`                           |
| `exception`    | an open data exception on a bound path | the exception is resolved or overridden      | the exception's message (falls back to `Resolve <type>`) |

### Opting fields in

The mandatory requirements come from props on the layout components themselves, not from the task template. Add them where the field is rendered in the data form schema. See [Layout Components](/guides/data-forms/layout-components) for the full prop tables — the gate-specific props are:

| Component  | Prop         | Type       | Notes                                                                                                                                                                                                                              |
| ---------- | ------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v2:tabs`  | `mustView`   | `number[]` | Zero-based indices of tabs the reviewer must open. The first-rendered tab counts as already viewed, so index `0` is normally pre-satisfied — list the tabs *after* the first. Out-of-range indices are ignored with a dev warning. |
| `v2:panel` | `mustExpand` | `boolean`  | Requires `collapsible: true`. The panel then starts collapsed and must be expanded at least once. If `collapsible` is `false` the prop is ignored (with a dev warning), because a non-collapsible panel is always open.            |
| `v2:panel` | `mustScroll` | `boolean`  | A bottom sentinel must scroll into view at least once — useful for long instruction blocks. Can be combined with `mustExpand` on the same panel.                                                                                   |

<Note>
  `mustExpand` only has an effect on a collapsible panel. Set `collapsible: true` on the same `v2:panel` or the requirement is dropped and a warning is logged in dev.
</Note>

### Exception scoping

The non-exception requirements (`mustView`, `mustExpand`, `mustScroll`) always apply to a `gatedByCompleteness` action. Exception-source requirements are scoped:

* If the action also declares `onlyEnabledIfNoOpenExceptionsForPaths`, only exceptions on those listed paths fold into the gate. (See [Gating actions on exceptions](/guides/task-templates/gating-actions-on-exceptions).)
* Otherwise, every open exception on a path the form binds applies.

A form's bound paths are the `tagPath` / `groupTaxon` values of every component it renders. Exceptions on paths the form does not render never fold into its gate, and matching is hierarchical — an exception on a child attribute is in scope for a panel bound at its parent group. An exception blocks the gate while it is open and has no closing comment; resolving it (or adding a closing comment) clears it from the gate.

### Example

An invoice review form with two tabs after the first, a collapsible instruction panel the reviewer must open, and an `Approve` action gated on completeness:

```yaml theme={null}
# Data form schema — layout components opt the fields in
- component: v2:tabs
  properties:
    mustView: [1, 2]          # "Line Items" and "Charges" — first tab is pre-viewed
  children:
    - component: v2:panel      # index 0 — first tab, counts as viewed
      properties: { title: "Header" }
    - component: v2:panel      # index 1
      properties: { title: "Line Items" }
    - component: v2:panel      # index 2
      properties: { title: "Charges" }

- component: v2:panel
  properties:
    title: "Reviewer Instructions"
    collapsible: true          # required for mustExpand
    mustExpand: true
```

```yaml theme={null}
# Task template — the action opts into the gate
metadata:
  actions:
    - uuid: approve-action
      type: approve
      label: "Approve"
      properties:
        targetStatus: approved
        statusSlug: approved
        icon: check
        color: green
        keybind: "a"
        gatedByCompleteness: true
```

Until the reviewer opens the **Line Items** and **Charges** tabs, expands **Reviewer Instructions**, and clears any open exception on a bound path, **Approve** stays disabled and the popover lists what is left: `Review Line Items tab`, `Review Charges tab`, `Expand Reviewer Instructions`, and one bullet per outstanding exception.

<Note>
  The gate is in-memory and lives for one form view. Reloading the form starts the reviewer over — every tab, panel, and scroll requirement is outstanding again. Keep the list short enough that a re-read is not punishing.
</Note>

## Recipe summary

1. On each layout component, opt the mandatory fields in: `mustView` on a `v2:tabs`, `mustExpand` (with `collapsible: true`) and/or `mustScroll` on a `v2:panel`.
2. On the action in the task template, set `gatedByCompleteness: true`.
3. To restrict which exceptions count, add `onlyEnabledIfNoOpenExceptionsForPaths` to the same action.
4. Keep a reject or escape action ungated so a reviewer is never trapped.
5. Test by leaving one flagged tab unopened — the action must stay disabled and the popover must name it.

## Next: requiring comments

The completeness gate makes sure a reviewer has *seen* the data. The next recipe — [Requiring comments on actions](/guides/task-templates/requiring-comments-on-actions) — makes sure they *explain* a decision before it is recorded.
