# API de configuración de ejecución

> Leer y cambiar las doce configuraciones de ejecución que gobiernan el marcado, la escritura en runs cerrados y los valores predeterminados de creación de runs, a nivel de proyecto y de organización.

Cada proyecto tiene doce **configuraciones de ejecución** que gobiernan el comportamiento del
marcado durante la ejecución de pruebas: si un run cerrado sigue aceptando escrituras de
resultado, si aplica un bloqueo por responsable asignado, qué ocurre después de registrar un
resultado, y más. Once de las doce **heredan** a través de una cadena de tres niveles,
`proyecto → organización → valor por defecto del código`; la duodécima,
`defaultAssigneeUserId`, es **exclusiva del proyecto** y no hereda en absoluto.

`{projectId}` es el **código** del proyecto (por ejemplo `ACME`) en todo lo siguiente; `{orgUlid}`
es el ULID de la organización del solicitante.

## Vocabulario y valores por defecto del código

| Clave                            | Tipo                                                          | Valor por defecto       | ¿Hereda?                        |
| -------------------------------- | ------------------------------------------------------------- | ----------------------- | ------------------------------- |
| `autoCompleteRun`                | `boolean`                                                     | `true`                  | Sí                              |
| `allowResultsInClosedRuns`       | `boolean`                                                     | `true`                  | Sí                              |
| `fastPass`                       | `boolean`                                                     | `true`                  | Sí                              |
| `defectToggleDefault`            | `boolean`                                                     | `true`                  | Sí                              |
| `autoAssignOnOpen`               | `boolean`                                                     | `true`                  | Sí                              |
| `failCaseOnStepFail`             | `boolean`                                                     | `false`                 | Sí                              |
| `autoPassWhenAllStepsPass`       | `boolean`                                                     | `false`                 | Sí                              |
| `assigneeResultLock`             | `boolean`                                                     | `false`                 | Sí                              |
| `requireCommentOnNegativeResult` | `boolean`                                                     | `false`                 | Sí                              |
| `afterAddingResult`              | `"stay" \| "next_pending_by_number" \| "next_pending_visual"` | `"next_pending_visual"` | Sí                              |
| `timeTracking`                   | `"off" \| "optional" \| "required"`                           | `"optional"`            | Sí                              |
| `defaultAssigneeUserId`          | ULID de miembro `\| null`                                     | `null`                  | **No — exclusiva del proyecto** |

Un proyecto y una organización que no almacenan ningún valor para ninguna clave se comportan
exactamente como el producto lo hacía antes de que existiera esta capacidad — cada valor por
defecto del código anterior reproduce el comportamiento observable de hoy, con una excepción
deliberada: `allowResultsInClosedRuns` tiene por defecto `true`, lo que formaliza un
comportamiento de relajación del congelamiento que ya estaba vigente en la ruta de marcado
individual antes de que esta capacidad se publicara. Una organización que prefiera el
comportamiento anterior, más estricto, puede fijarlo en `false`.

### Qué gobierna cada configuración

- **`autoCompleteRun`** — si un run se cierra automáticamente en cuanto todos sus casos tienen un
  resultado. Cuando es `false`, el run permanece abierto hasta que un miembro lo cierra
  explícitamente.
- **`allowResultsInClosedRuns`** — si un run **cerrado y no abortado** sigue aceptando escrituras
  de resultado (marcados, marcados de paso, ediciones de notas de resultado, adjuntos de
  resultado). Un run **abortado** permanece congelado sin importar el valor de esta
  configuración, siempre. Consulta [Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/) para el
  comportamiento exacto del `409`.
- **`fastPass` / `defectToggleDefault` / `requireCommentOnNegativeResult`** — experiencia de
  ejecución exclusiva del cliente web: si marcar `passed`/`skipped` omite el modal de resultado,
  el valor inicial del interruptor de defecto en el modal, y si un resultado negativo exige un
  comentario antes de enviarlo. Consulta la
  [limitación conocida](#limitación-conocida-requirecommentonnegativeresult-se-aplica-solo-en-el-cliente)
  más abajo para la única configuración de este grupo sin contraparte en el servidor.
- **`autoAssignOnOpen`** — si abrir un caso sin asignar autoasigna al usuario que actúa.
- **`failCaseOnStepFail` / `autoPassWhenAllStepsPass`** — si un resultado a nivel de paso deriva
  automáticamente el estado del caso padre. Precedencia cuando ambas aplican: un paso fallido
  gana sobre un paso bloqueado, que gana sobre un conjunto de pasos completamente aprobado.
- **`assigneeResultLock`** — si solo el tester asignado (o un solicitante exento) puede registrar
  un resultado para un caso asignado. Consulta [Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/)
  para el comportamiento del `403`.
- **`afterAddingResult`** — qué caso enfoca el tablero de ejecución después de un marcado,
  únicamente en el cliente web. No es observable a través de la API.
- **`timeTracking`** — si `elapsedMs` es opcional u obligatorio en un marcado individual con
  estado terminal. Consulta [Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/) para el
  comportamiento del `422`.
- **`defaultAssigneeUserId`** — precompleta el responsable predeterminado de un run nuevo.
  Consulta [Precedencia en la creación de runs](#precedencia-en-la-creación-de-runs) más abajo.

## Cadena de resolución y `source`

Cada una de las once claves heredables se resuelve en este orden: un override explícito a nivel
de **proyecto** gana; si no, un valor por defecto explícito a nivel de **organización** gana; si
no, aplica el **valor por defecto del código**. Un `null` almacenado en cualquier nivel significa
"sin override en ese nivel" — nunca se trata como el valor `false` o una cadena vacía.

La respuesta de cada clave heredable incluye:

| Campo                   | Significado                                                                                  |
| ----------------------- | -------------------------------------------------------------------------------------------- |
| `value`                 | El valor efectivo resuelto — lo que realmente gobierna el comportamiento ahora mismo         |
| `source`                | `"project"`, `"organization"` o `"default"` — qué nivel produjo `value`                      |
| `projectValue`          | El override crudo almacenado a nivel de proyecto, o `null`                                   |
| `organizationValue`     | El override crudo almacenado a nivel de organización, o `null`                               |
| `organizationEffective` | El valor efectivo propio de la organización (su override, o el valor por defecto del código) |

`source` es lo que un cliente debe usar para mostrar el indicador de "sobrescrito" / "heredado de
la organización" / "por defecto" — nunca comparar `value` contra `organizationEffective`. Un
valor de proyecto que coincide con el valor efectivo de la organización sigue siendo un
**override almacenado**: debe seguir mostrándose como tal y seguir siendo posible borrarlo, o un
cambio posterior del valor por defecto de la organización afectaría silenciosamente a un
proyecto que el usuario creía fijo.

`defaultAssigneeUserId` está **exenta de la cadena** — no tiene nivel de organización en
absoluto, así que su entrada solo incluye `value` y `source` (`"project"` o `"default"`), nunca
`projectValue`, `organizationValue` ni `organizationEffective`.

## Configuración del proyecto

### Leer la configuración del proyecto

`GET /api/v1/projects/{projectId}/run-settings`

Abierto a cualquier lector del proyecto (`viewer` o superior). Devuelve `200` con las doce
entradas.

```json
{
  "settings": {
    "autoCompleteRun": {
      "value": true,
      "source": "default",
      "projectValue": null,
      "organizationValue": null,
      "organizationEffective": true
    },
    "fastPass": {
      "value": false,
      "source": "project",
      "projectValue": false,
      "organizationValue": null,
      "organizationEffective": true
    },
    "timeTracking": {
      "value": "required",
      "source": "organization",
      "projectValue": null,
      "organizationValue": "required",
      "organizationEffective": "required"
    },
    "defaultAssigneeUserId": {
      "value": "01J...MEMBER",
      "source": "project"
    }
  }
}
```

(Abreviado — la respuesta real incluye las doce claves.)

### Actualizar la configuración del proyecto

`PATCH /api/v1/projects/{projectId}/run-settings`

Cada clave es **opcional** — una clave **ausente** se deja sin cambios, y un **`null`**
explícito restablece esa clave a heredar (para `defaultAssigneeUserId`, de vuelta a ningún
precompletado). Se requiere al menos una clave; un cuerpo vacío se rechaza con
`422 validation_failed`. Las claves desconocidas se rechazan de la misma manera (`.strict()`).

```json
{ "fastPass": false, "requireCommentOnNegativeResult": null }
```

`defaultAssigneeUserId` acepta el **ULID** de un miembro, nunca el id entero interno, resuelto
contra la membresía activa de la organización del proyecto — un ULID desconocido o ajeno se
rechaza con `404 not_found`, y no se persiste nada.

Devuelve `200` con la respuesta completa recién resuelta (misma forma que `GET`).

#### Autorización

Requiere el permiso `projects.manage` (que tienen **`owner`**/**`admin`**), o el
**propietario actual** del proyecto. Un miembro que solo puede leer el proyecto
(`viewer`/`member`) recibe `403 forbidden`.

#### Ejemplo

```bash
curl -sS -X PATCH \
  -H "Authorization: Bearer $PROBARA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fastPass": false, "assigneeResultLock": true}' \
  "https://app.probara.net/api/v1/projects/ACME/run-settings"
```

## Valores predeterminados de la organización

### Leer los valores predeterminados de la organización

`GET /api/v1/orgs/{orgUlid}/run-settings`

Abierto a cualquier miembro de la organización. Devuelve `200` con las **once** claves
heredables únicamente — `defaultAssigneeUserId` nunca aparece aquí, porque no tiene nivel de
organización.

```json
{
  "settings": {
    "autoCompleteRun": { "value": true, "source": "default", "organizationValue": null },
    "fastPass": { "value": true, "source": "organization", "organizationValue": true }
  }
}
```

(Abreviado — la respuesta real incluye las once claves.)

### Actualizar los valores predeterminados de la organización

`PATCH /api/v1/orgs/{orgUlid}/run-settings`

Misma semántica de "ausente = sin cambios" / "`null` explícito restablece al valor por defecto
del código" que el endpoint de proyecto, restringida a las once claves heredables. Enviar
`defaultAssigneeUserId` aquí se rechaza con `422 validation_failed` como **clave desconocida** —
nunca como un no-op silencioso, ya que el esquema solo declara las once claves heredables.

```json
{ "timeTracking": "required" }
```

#### Autorización

Requiere el permiso `org-settings.manage`.

#### Ejemplo

```bash
curl -sS -X PATCH \
  -H "Authorization: Bearer $PROBARA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"autoCompleteRun": false}' \
  "https://app.probara.net/api/v1/orgs/01J...ORG/run-settings"
```

### «Fijar como valor predeterminado de la organización»

Cambiar un valor predeterminado de la organización mueve el valor **efectivo** de cada proyecto
que no almacena un override propio para esa clave — un proyecto que ya tiene su propio override
con `source: "project"` no se ve afectado. El panel de configuración de proyecto de la
aplicación web ofrece una acción «Fijar como valor predeterminado de la organización» en el menú
de la fila de cualquier clave sobrescrita a nivel de proyecto (visible solo para quien tiene
`org-settings.manage`), que es exactamente un `PATCH` al endpoint de organización con el valor
de esa única clave a nivel de proyecto — no existe una superficie de API separada para ello.

## Limitación conocida: `requireCommentOnNegativeResult` se aplica solo en el cliente

Esta única configuración **no tiene contraparte en el servidor**. El texto del comentario llega
en una solicitud separada de la del propio marcado
(`PATCH /api/v1/runs/{runUlid}/results/{resultUlid}`), así que ninguna solicitud individual al
servidor puede observar "este caso se marcó como fallido sin comentario" — aplicarlo en el
servidor implicaría ampliar la solicitud de marcado con un cambio disruptivo desproporcionado
para una única configuración de interfaz, o un barrido posterior sin disparador natural. **Un
cliente de API o de reportes puede marcar un caso como `failed` o `blocked` sin comentario
mientras esta configuración está activa** — el modal de resultado de la aplicación web es el
único lugar donde se aplica esta regla. Cada una de las demás configuraciones de este documento
hereda hacia un comportamiento del lado del servidor o tiene una brecha explícita y documentada
por separado (consulta [Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/) para la aplicación en
el servidor de `timeTracking`, que **sí** es real).

## Dónde aplica y dónde no `timeTracking: "required"`

El rechazo `422 elapsed_ms_required` de `timeTracking: "required"` (consulta
[Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/)) solo se activa en una ruta de código que
realmente invoca la lógica de marcado individual. Esta tabla es la auditoría completa de la
superficie:

| Superficie                                                                                       | ¿Llega al control?                                                                          | Aplica o está exenta                                                                              |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Marcado individual — `PATCH /api/v1/runs/{runUlid}/cases/{runCaseUlid}`                          | Sí, directamente                                                                            | **Aplica** el `422`                                                                               |
| Herramienta MCP `mark_run_case`                                                                  | Sí — despacha al mismo endpoint textualmente                                                | **Aplica** — sin manejo especial en la herramienta, hereda el `422` del endpoint                  |
| Marcado en bloque — `POST .../cases/bulk-mark` (y MCP `bulk_mark_run_cases`)                     | No — el esquema de la solicitud no lleva ningún campo de duración por caso                  | **Exenta** a nivel de esquema — no existe forma de construir una solicitud que lo viole           |
| Envío de resultados en bloque — `POST .../cases/bulk-submit-result` (y MCP `bulk_submit_result`) | No — pasa por un servicio distinto, nunca por la ruta de marcado individual                 | **Exenta** por diseño — el envío en bloque queda explícitamente fuera del alcance de este rechazo |
| Enriquecimiento de notas de resultado — `PATCH /api/v1/runs/{runUlid}/results/{resultUlid}`      | No — modifica una fila de resultado ya agregada (`notes`); nunca genera un cambio de estado | **Exenta** — fuera de alcance estructuralmente, no lleva `status`                                 |
| Ruta automatizada de ingesta de resultados                                                       | N/D — esta API no tiene hoy una superficie de ingesta de resultados por CSV/webhook/CI      | No aplica                                                                                         |

## Precedencia en la creación de runs

`defaultAssigneeUserId` solo decide con qué empieza un run **recién creado** — cambiar la
configuración del proyecto nunca reescribe el responsable ya almacenado de un run existente.
`POST /api/v1/projects/{projectId}/runs` resuelve el responsable predeterminado del run en este
orden:

1. `defaultAssigneeUlid` **presente** en el cuerpo de la solicitud de creación del run
   (incluido un `null` explícito, que significa "deliberadamente ninguno") — siempre gana.
2. Si no, la configuración `defaultAssigneeUserId` del proyecto, cuando está definida.
3. Si no, `null` — el comportamiento de hoy, sin cambios.

## Relacionados

- [Casos de ejecución](https://docs.probara.net/es/developers/api/run-cases/) — los endpoints de marcado que gobiernan
  estas configuraciones, incluidos los comportamientos `403 run_case_assignee_locked` y
  `422 elapsed_ms_required`
- [Resultados reales de los pasos](https://docs.probara.net/es/developers/api/run-result-attachments/) —
  comportamiento de escritura en runs cerrados para resultados de paso y notas de resultado
- [Descripción general de la referencia de la API](https://docs.probara.net/es/developers/api-reference/) — paginación,
  errores, autenticación
- [Referencia interactiva v1](https://docs.probara.net/es/developers/api-reference/v1/) — esquemas completos
