---
name: propose_capability_groups
kind: pipeline
lang: bash
domain: pipelines
version: "1.0.0"
purity: impure
signature: "propose_capability_groups([--min-count N], [--max-domains M], [--json], [--apply tag]) -> report"
description: "Analiza tags candidatos a capability group (issue 0086). Filtra via blocklist + cap de dominios. Lista candidatos o promociona con --apply (llama generate_capability_doc + actualiza INDEX.md)."
tags: ["capability-groups", "doctor", "audit", "pipeline"]
uses_functions:
  - generate_capability_doc_bash_pipelines
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
params:
  - name: --min-count
    desc: "minimo de funciones con el tag para considerarlo candidato (default 3)"
  - name: --max-domains
    desc: "maximo de dominios distintos entre funciones del tag (default 3, filtra tags genericos que cruzan muchos dominios)"
  - name: --json
    desc: "salida JSON estructurada en vez de texto (util para agentes)"
  - name: --apply
    desc: "promociona el tag dado a capability group: genera doc con generate_capability_doc + actualiza docs/capabilities/INDEX.md (idempotente)"
output: "tabla de candidatos a stdout (tag, count, domains, already_group, samples), o efectos en disco si --apply (nuevo .md + fila en INDEX.md)"
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/pipelines/propose_capability_groups.sh"
---

## Ejemplo

```bash
# Listar candidatos con al menos 10 funciones
bash bash/functions/pipelines/propose_capability_groups.sh --min-count 10

# Listar con salida JSON (para agentes)
bash bash/functions/pipelines/propose_capability_groups.sh --min-count 5 --json

# Promocionar un tag a capability group
bash bash/functions/pipelines/propose_capability_groups.sh --apply metabase

# Via fn run
./fn run propose_capability_groups --min-count 10
```

## Logica

### Modo listar (sin --apply)

1. Resuelve la raiz del registry (walk-up hasta `registry.db`).
2. Filtra tags via **blocklist** hardcodeada (idiomas, dominios, CRUD generico, verbos super-genericos, primitivas, estados).
3. Query SQL con `json_each(f.tags)` + `GROUP BY` + `HAVING cnt >= N AND domains <= M`.
4. Marca cada candidato con `already_group: yes/no` parseando los links `[tag](tag.md)` de `docs/capabilities/INDEX.md`.
5. Imprime tabla formateada con maximo 3 IDs de muestra por candidato.
6. Resumen: total candidatos, ya-grupos, nuevos.

### Modo --apply

1. Valida que el tag no esta en blocklist y pasa el filtro de count/domains.
2. Llama a `bash/functions/pipelines/generate_capability_doc.sh <tag>`.
3. Inserta fila en `docs/capabilities/INDEX.md` (idempotente — no duplica si ya existe).
4. Imprime checklist de edicion manual para el usuario.

## Blocklist

Tags en la blocklist nunca son candidatos (caso exacto):

- **Idioma**: `go py bash ps ts python cpp`
- **Dominio**: `core infra finance datascience cybersecurity shell tui pipelines browser`
- **Kind/purity**: `function pipeline component pure impure`
- **CRUD generico**: `add create delete list update get set remove insert`
- **Verbo super-generico**: `compose convert combine append empty exists check find format parse render`
- **Estructural**: `generic helper utility wrapper test`
- **Primitivas**: `string number int float array slice map dict value key`
- **Estados**: `pending-usar pendiente-usar`

## Notas

- Usa `sqlite3` directo para el JOIN custom `functions + json_each(tags)` (autorizado por la regla registry_calls.md — JOINs custom no expuestos por el MCP).
- La insercion en INDEX.md usa `python3` embebido para manejar la posicion relativa a la cabecera de tabla de forma portable.
- El flag `--max-domains` es la heuristica clave: un tag como `add` aparece en 8 dominios → filtrado; `metabase` aparece en 2 → candidato valido.
