Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions docs/decisiones/0043-posicionamiento-agent-native-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# 0043 — bib2graph implementa deliberadamente el contrato CLI-para-agentes: las relajaciones de trust son por bajo blast radius; tres grietas son roadmap explícito

- **Estado:** Aceptada
- **Fecha:** 2026-06-30
- **Decidido por:** mixto. Las decisiones de fondo —**(1)** ratificar que el CLI `b2g`
**implementa deliberadamente** el contrato CLI-para-agentes (postura de diseño, no accidente),
**(2)** declarar que las relajaciones de los principios de *trust & safety* (4–7) son **decisión
por bajo blast radius**, no huecos, y **(3)** nombrar tres grietas como **roadmap reconocido** sin
resolverlas en este ADR— son **decisiones del Product Owner humano**. El **encuadre** que las
ordena —la auditoría del CLI contra el *Rubric for Agent-Native CLI Design* (nueve principios),
separando *hueco* de *decisión de diseño*— es **síntesis de la IA (architect) validada por el PO**.
- **Extiende [0021](0021-cli-agente-native-contrato.md)** (Contrato del CLI agente-native). 0021 fijó
el **contrato concreto** (set de subcomandos, envelope `schema="1"`, exit codes 0–5 por tipo de
excepción, estado en el archivo y no en la sesión). Este ADR **no cambia ese contrato**: lo
**ratifica como postura de diseño explícita** y le agrega dos capas que 0021 no declaraba — el
*porqué* de las relajaciones de trust y la *deuda de roadmap* nombrada. Es un ADR-paraguas de
**posicionamiento** anclado en 0021, no una enmienda que toque el envelope, los exit codes ni la FSM.
- **Relacionada con:** [0010](0010-agente-native-columna.md) (la CLI agente-native como **columna
primaria** — este ADR explicita la postura que 0010 fijó como principio),
[0037](0037-superficie-cli-10-verbos-ciclo.md)/[0038](0038-destino-verbos-huerfanos-0037.md) (la
superficie de **10 verbos** del ciclo; la grieta (9) abajo se resuelve, si se aborda, como **comando
meta fuera de los 10** —patrón de [0039](0039-skill-comando-meta-distribucion.md)—, no como 11° verbo
del ciclo), [0039](0039-skill-comando-meta-distribucion.md) (precedente de comando meta auto-descriptivo
fuera del ciclo), [0041](0041-documentacion-por-superficie-de-entrega.md) (`docs/API.md` y el `llms.txt`
**publican** el mismo contrato que este ADR posiciona; la grieta (9) reduciría el *hop* a la doc en prosa).
- **No introduce IA** (coherente con [0022](0022-producto-sin-ia-generativa.md)): es **postura de
diseño y deuda declarada** sobre un CLI determinista. bib2graph no embebe ni invoca ningún modelo;
el "para agentes" describe al **consumidor** del contrato (un LLM por subprocess + JSON), no una
capacidad generativa del motor.
- **No cambia ningún contrato público.** El envelope `schema="1"`, los exit codes 0–5 y la FSM del
ciclo ([0021](0021-cli-agente-native-contrato.md)/[0016](0016-maquina-estados-lazo.md)) quedan
**intactos**. Las tres grietas, si se abordan, lo hacen de forma **aditiva** (campos nuevos en `data`,
un `error.subcode`, un comando meta) — cada una **exige su propio ADR/issue** antes de tocar `API.md`.
- **Origen:** unificación de **dos auditorías independientes** (PO + un agente) del CLI `b2g` contra
el *Rubric for Agent-Native CLI Design*, reconciliadas **contra el código** (verificadas as-built
con `archivo:línea`). Encuadre empírico de uso real: un agente que, ante un error de red opaco,
**abandonó la CLI y pegó directo contra OpenAlex con `urllib`**, perdiendo la procedencia que es la
razón de ser de la herramienta. *(Las notas de auditoría son trabajo local; este ADR se sostiene
solo: las afirmaciones falsables van ancladas con `archivo:línea`.)*

## Contexto

El ADR [0010](0010-agente-native-columna.md) fijó el **principio** ("la CLI agente-native es
superficie primaria") y el [0021](0021-cli-agente-native-contrato.md) fijó el **contrato concreto**.
Lo que **ninguno de los dos registró** es una cosa que en la práctica ya está decidida y conviene
volver inmutable: que ser *agent-native* es una **postura de diseño deliberada y verificable**, no un
subproducto del estilo de implementación — y, sobre todo, **dónde el contrato deliberadamente se
relaja y por qué**.

Una auditoría del CLi contra los nueve principios de un rubric de diseño agent-native (legibilidad ·
trust & safety · composabilidad/estabilidad) arrojó, verificado as-built:

- **Legibilidad (1–3): sólida.** Salida estructurada por un único envelope versionado
(`{schema, ok, command, exit_code, data, warnings, error}`, `ENVELOPE_SCHEMA_VERSION = "1"`,
`service/envelope.py:26`), una sola flag de salida estructurada (`--json`, `cli/_options.py:85`; o
`B2G_JSON` por entorno), y exit codes 0–5 mapeados por tipo de excepción con una función pura
(`service/errors.py:71-97`). **Caveat:** dentro de `NETWORK_ERROR`/exit 4, no se distingue un `429`
reintentable de un `504` no-reintentable (`sources/openalex.py:150`, `cli/_errors.py:140-145`).
- **Trust & safety (4–7): relajada.** No hay `--dry-run` universal, ni *tagging* de canal de confianza
en los inputs (`seed.py:215`), ni *grading* legible de blast radius por comando (no hay `--force` ni
confirmación). El extremo destructivo del espectro —borrado de recursos— **directamente no existe**.
- **Composabilidad/estabilidad (8–9): sólida con dos caveats.** Cada comando es invocable en frío y el
canal entre invocaciones es **el archivo** (`library.duckdb` del workspace), no memoria de sesión —
la propiedad que hace a `b2g` sobrevivir la compactación de contexto. *Caveat (8):* **cuál** workspace
se resuelve por ambiente (precedencia `--workspace` > `B2G_WORKSPACE` > walk-up del cwd,
`workspace.py:246-286`); un agente que cambia de cwd sin flag puede operar en silencio sobre otra
investigación. *Caveat (9):* la **salida** está versionada, pero **no hay auto-descripción por el mismo
canal de invocación** (no existe `b2g schema`/introspección); el agente en frío debe ir a `docs/API.md`
en prosa — el *hop* débil.

El rubric pide **no sumar** los nueve en un puntaje, sino mirar dónde **dos grietas juntas** crean
riesgo. La pregunta que este ADR cierra no es "¿cuántos principios cumple?", sino **qué es decisión y
qué es deuda** — para que la próxima auditoría (humana o de agente) no reabra lo ya decidido ni pierda
de vista lo que sigue abierto.

## Decisión

### (1) bib2graph implementa deliberadamente el contrato CLI-para-agentes

El CLI `b2g` **implementa, por postura de diseño y no por accidente**, el contrato CLI-para-agentes
que [0021](0021-cli-agente-native-contrato.md) fijó: **envelope JSON versionado** (`schema="1"`),
**exit codes 0–5 tipados** por clase de error, **comandos self-contained** (estado en el archivo, no
en la sesión) e **identidad de recurso explícita** (papers por `--id` con resolución
`id > doi > source_id`, `read.py:205`, ADR [0036](0036-identidad-source-id-agnostica-doi-ancla.md)).
Esto **ratifica y se apoya en 0021**: aquel ADR decidió *qué* es el contrato; éste registra que
**sostenerlo es un objetivo de diseño de primera clase**, falsable con `archivo:línea`, y por tanto un
criterio contra el cual se evalúan cambios futuros de superficie. Un PR que erosione la legibilidad
estructurada (p. ej. reintroducir prosa en stdout en modo `--json`, o partir el contrato de salida en
variantes `--output`/`--pretty`) **contradice este ADR** y exige justificarse.

### (2) Las relajaciones de los principios 4–7 son decisión por bajo blast radius, no huecos

Las relajaciones de *trust & safety* (reversibilidad parcial, sin *tagging* de canal, riesgo plano sin
*grading* legible) son **decisión deliberada**, coherente con el *fail-open advisory* de 0021. El
fundamento es el **bajo blast radius** de esta herramienta, verificable: es un CLI de **analítica
local** sobre un store **DuckDB local single-writer** ([0009](0009-biblioteca-viva-duckdb.md)/[0019](0019-concurrencia-diferida.md)),
**sin borrado de recursos** (no hay `delete/reset/purge`), **sin estado multi-tenant ni credenciales
compartidas**, y con **daño recuperable por snapshot** (`snapshot restore` como red). El propio rubric
lo dice en su *worked example*: un CLI de analítica local no necesita la misma postura de trust que uno
que borra producción.

**No se adopta fricción de confirmación interactiva** (`--force`, prompts, gates) porque **mataría la
autonomía del agente** —el `b2g` está pensado para correrse por subprocess sin humano en el loop— y el
daño que evitaría es local y reversible. Esta es la decisión explícita: la planicie de riesgo es
**benigna por construcción**, no un descuido a corregir. Quien proponga sumar fricción de confirmación
a un verbo del ciclo está **reabriendo esta decisión** y debe traer un ADR.

> **Frontera de la decisión (2):** "relajado por diseño" cubre el espectro **actual** del CLI (lectura
> · escritura idempotente que transiciona el FSM · curación transversal persistente · `restore`
> irreversible-pero-local). **No** es licencia para introducir un blast radius nuevo (borrado remoto,
> multi-tenant, mutación destructiva no recuperable) sin reevaluar la postura de trust. Si el espectro
> cambia, este ADR no lo ampara.

### (3) Tres grietas se declaran roadmap explícito (deuda reconocida, no resuelta acá)

Las tres se nombran como **deuda reconocida**; **ninguna se resuelve en este ADR** y cada una, al
tocarse, **exige su propio ADR/issue** porque toca `docs/API.md`. Caben las tres **sin romper** el
contrato de 0021 ni la superficie de 10 verbos de [0037](0037-superficie-cli-10-verbos-ciclo.md):

- **(3a) Subcódigo de error de red — `RATE_LIMITED` (429) vs `UPSTREAM_TIMEOUT`/`QUERY_TOO_COMPLEX`
(504).** Hoy todo cae en `NETWORK_ERROR`/exit 4 (`cli/_errors.py:140-145`), aunque el status code se
distingue **internamente** (`openalex.py:150`, `_RETRY_STATUS_CODES` en `openalex.py:70`). Un *retry
loop* del agente no puede separar transitorio (reintentable con backoff) de permanente (hay que
simplificar la query). Es la **única grieta con daño documentado en uso real**: el desenlace fue un
agente ruteando **alrededor** del motor (directo a OpenAlex con `urllib`), perdiendo la trazabilidad.
Fix aditivo (un `error.subcode` en el envelope; la información ya existe puertas adentro), efecto grande.
- **(3b) Volver legible el workspace resuelto.** La identidad del *recurso* es explícita, pero **cuál**
workspace se eligió por ambiente es implícito (`workspace.py:246-286`). Recomendación: **eco del
workspace en el envelope** (p. ej. en `data`) o **warning** cuando se resolvió por walk-up del cwd
(no por flag ni env explícitos), para que un agente que cambió de cwd no opere en silencio sobre otra
investigación. Aditivo; no cambia la precedencia.
- **(3c) `b2g schema` — introspección versionada de la superficie.** Un comando **meta** que vuelque
comandos, flags por comando y el JSON-schema del envelope con su `ENVELOPE_SCHEMA_VERSION`, por el
**mismo canal de invocación** que el agente ya usa para actuar — cerrando el *hop* a `docs/API.md` en
prosa. Debe modelarse como **comando meta fuera de los 10 verbos del ciclo** (no transiciona FSM, no
toca workspace), siguiendo el precedente de `skill` ([0039](0039-skill-comando-meta-distribucion.md)):
conteo "10 + `skill` + `schema`", no "11 verbos del ciclo".

## Consecuencias

**Lo que se gana**

- **La postura agent-native deja de ser folclore.** Pasa a ser **decisión registrada e inmutable**: la
próxima auditoría parte de "esto ya está decidido" en vez de re-litigar si `b2g` "es de verdad"
agent-native. El criterio para evaluar cambios de superficie queda explícito.
- **Las relajaciones de trust dejan de leerse como huecos.** Cualquier revisor (o agente) que mida `b2g`
contra un rubric genérico y reporte "fallan 4–7" tiene la respuesta registrada: **es por blast
radius**, con la frontera de cuándo deja de valer. Se evita el churn de "arreglar" no-problemas.
- **La deuda real queda nombrada y acotada.** Tres grietas con *archivo:línea*, ordenadas por palanca,
cada una aditiva y sin romper contrato — listas para abrir issues sin re-descubrir el análisis.

**Lo que cuesta**

- **Disciplina de coherencia en cada cambio de superficie.** Este ADR es ahora un criterio: un PR que
erosione legibilidad estructurada o sume fricción de confirmación a un verbo del ciclo lo **contradice**
y debe justificarse con ADR. Es trabajo de revisión que antes no existía explícitamente.
- **Las tres grietas siguen abiertas hasta que alguien las tome.** Declararlas roadmap **no** las cierra;
en particular (3a) tiene daño documentado y cada ronda que pase sin subcódigo de red mantiene el riesgo
de que un agente vuelva a rutear alrededor del motor.
- **Cada grieta abierta arrastra su propio ADR/issue.** Por tocar `docs/API.md`, ninguna se mergea "de
paso": el costo de cerrar (3a)/(3b)/(3c) incluye su registro de decisión, aunque el código sea chico.

## Alternativas

- **Dejarlo como nota-mapa, sin graduar a ADR.** **Descartada:** las notas de auditoría son trabajo
local que no se commitea; la postura y la separación hueco/decisión se perderían y la próxima auditoría
re-litigaría lo ya decidido. El valor está precisamente en volver la decisión **inmutable y citable**.
- **Enmendar 0021 en vez de un ADR nuevo.** **Descartada:** 0021 fija el **contrato** (envelope/exit/FSM);
este ADR no lo cambia, agrega una capa de **posicionamiento + deuda**. Meterlo como enmienda de 0021
mezclaría "qué es el contrato" con "por qué la postura y qué falta", y 0021 ya carga varias enmiendas
de superficie. Un ADR-paraguas que **extiende** 0021 mantiene cada decisión legible por separado.
- **Resolver las tres grietas en este mismo ADR.** **Descartada:** cada una toca `docs/API.md` (contrato
público) y por regla del repo exige su propio ADR/issue con su DoD y tests. Mezclarlas acá produciría un
ADR que decide cinco cosas a la vez y un PR imposible de revisar. Este ADR **nombra** la deuda; no la salda.
- **Adoptar fricción de confirmación (`--force`/prompts) para "cumplir" trust 4–7.** **Descartada:** es
exactamente lo que (2) rechaza — mataría la autonomía del agente para prevenir un daño local y reversible.
El rubric mismo desaconseja imponer postura de producción a una herramienta de analítica local.
- **Cerrar el caveat de red (3a) re-exponiendo `--api-key` o tocando el retry interno.** Fuera de alcance:
la grieta es de **legibilidad del outcome** (subcódigo en el envelope), no del retry —que ya existe
interno (`openalex.py:70`)— ni de la credencial —que ya entra solo por env `OPENALEX_API_KEY`
(`openalex.py:413`); el único `--api-key` literal vive en el alias deprecado `enrich` que se retira en
0.11.0 ([0038](0038-destino-verbos-huerfanos-0037.md)). El footgun de la credencial **se cierra solo**
con esa deprecación; la acción mínima es **no re-introducir** `--api-key` en los verbos vivos.
1 change: 1 addition & 0 deletions docs/decisiones/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ Qué se vuelve posible/fácil y qué se vuelve costoso/imposible. Trade-offs hon
| [0039](0039-skill-comando-meta-distribucion.md) | Distribución de la skill de Claude Code end-user: `b2g skill add [--user\|--project] [--force]` (comando **meta**; **fuera** del set de 10, no compite con `status`); skill vendoreada en el wheel (`src/bib2graph/skill/`, fuente commiteada incluida por `packages` — **no** `force-include`) → **version-lock skill==cli**; envelope `--json` sin FSM, sin workspace. **Descarta** el extra `[skill]` (no escribe en `~/.claude/skills/`) y plugin+marketplace como primario (desacopla versionado; ruta futura) | **Aceptada** (2026-06-28). **Enmienda [0038](0038-destino-verbos-huerfanos-0037.md)** (excepción meta); relacionada 0037/0028/0010/0021/0029; no introduce IA (0022). Epic [#188](https://github.com/complexluise/bib2graph/issues/188). **Nota (2026-06-28):** el [0040](0040-retiro-gui-local.md) retira `gui`, así que `skill` queda como **única** excepción meta (conteo "10 + skill") |
| [0040](0040-retiro-gui-local.md) | Retirar la GUI local de la librería (BREAKING): se eliminan `b2g gui`, la API local FastAPI (`api/`), la SPA `frontend/`, el extra `[gui]` y el vendoreo del frontend en el wheel; el core es CLI/agente-native sobre la biblioteca viva. **La capa `service/` (incl. `reads.py`) se conserva** (la usa el CLI). Conteo: "10 verbos + `skill`" | **Aceptada** (2026-06-28). **Supersede [0027](0027-pivote-posicionamiento-gui-local.md)/[0028](0028-arquitectura-gui-api-capa-servicios.md)**; enmienda [0038](0038-destino-verbos-huerfanos-0037.md) (retira la excepción `gui`); relacionada 0010/0021/0037/0039/0005/0019/0032/0033/0034; no introduce IA (0022). Issue [#190](https://github.com/complexluise/bib2graph/issues/190); limpieza profunda de docs en [#191](https://github.com/complexluise/bib2graph/issues/191) |
| [0041](0041-documentacion-por-superficie-de-entrega.md) | Organizar la documentación por **superficie de entrega**, no por tipo de lector: los tres usuarios = **dos superficies + un mediador** (agente dentro del repo → `AGENTS.md`/código; humano-y-agente navegando → sitio Diátaxis; IA externa → `llms.txt`). **No se forkea el sitio por audiencia**; se publica `llms.txt` (derivado de `AGENTS.md` + referencia CLI) en la raíz del sitio; persona = **veneer de navegación** (3 puertas por intención, 1 cuerpo); `AGENTS.md` sube al nav. La operación-por-IA es objetivo de 1ª clase | **Aceptada** (2026-06-29). Net-new (ningún ADR gobernaba la estructura de la doc); **no cambia contratos** (envelope/exit/FSM intactos). Relacionada 0039 (skill = doc operativa mediada por IA, fuera del sitio)/0010/0021/0037/0038/0040; no introduce IA (0022) |
| [0043](0043-posicionamiento-agent-native-cli.md) | bib2graph **implementa deliberadamente** el contrato CLI-para-agentes (envelope `schema="1"` versionado, exit codes 0–5 tipados, comandos self-contained, identidad de recurso explícita): postura de diseño, no accidente. Las **relajaciones de trust & safety (principios 4–7)** son **decisión por bajo blast radius** (analítica local, store DuckDB local, sin borrado, daño recuperable por snapshot), no huecos — *fail-open advisory*; **no** se adopta fricción de confirmación (mataría la autonomía del agente). Tres grietas = **roadmap explícito**: subcódigo de red (`RATE_LIMITED` 429 vs `UPSTREAM_TIMEOUT` 504), eco del workspace resuelto, `b2g schema` (introspección versionada) | **Aceptada** (2026-06-30). **Extiende [0021](0021-cli-agente-native-contrato.md)** (posiciona el contrato; **no** lo cambia: envelope/exit/FSM intactos). Relacionada 0010/0037/0038/0039 (`schema` = comando meta fuera de los 10)/0041; no introduce IA (0022). Origen: dos auditorías (PO + agente) contra el *Rubric for Agent-Native CLI Design* |
Loading