Imported from Dkalds/TenderFlow (
AGENTS.md). Install upstream withnpx skills add Dkalds/TenderFlow. Copyright stays with the author.
AGENTS.md
Fuente canónica de instrucciones para todos los agentes. CLAUDE.md y
.github/copilot-instructions.md solo añaden adaptaciones de plataforma. Si
editás una regla común, editála aquí.
Este archivo contiene únicamente reglas siempre relevantes. El detalle operativo vive en docs/AGENT_PLAYBOOK.md, el estado calculado en docs/STATUS.md, el trabajo priorizado en docs/IMPROVEMENT_BACKLOG.md y el diagnóstico UX/UI del frontend con su roadmap por olas en docs/UX_AUDIT.md.
0. Alcance y prioridades
Congelamiento de superficie — levantado el 2026-08-10. Estuvo vigente desde el 2026-08-07 mientras corría docs/archive/plans/2026-08-plan-saneamiento.md; sus dos olas están cerradas, así que la restricción ya no aplica y las features nuevas están permitidas. (Su última frase se contradecía con las anteriores, y un agente que lo leyera hoy no sabría cuál de las dos obedecer.)
Lo que sí sigue vigente es el motivo por el que se declaró: el producto acumuló 154 endpoints y 13 espacios de consola en doce días, y varias de esas superficies mostraban datos fabricados o placeholder. Antes de añadir una superficie nueva, comprobá que ninguna existente cubre ya el caso, y que la que añadís muestra datos reales desde el primer commit.
Salvo que el usuario priorice otra cosa, seguí el orden de docs/IMPROVEMENT_BACKLOG.md. Los ítems cerrados se archivan en docs/archive/IMPROVEMENT_BACKLOG_CERRADOS.md en vez de acumularse en el backlog: un backlog que nadie puede leer entero no prioriza nada.
No escribas aquí hechos calculables (conteos de tests, coverage, tamaño de
ratchets, jobs o endpoints): pertenecen a docs/STATUS.md, que
genera make status. Los RFCs de features ya existentes se implementan sin
crear otro RFC; las excepciones están en la política de §5.
1. Cómo navegar el código (graphify-first)
Usá este orden para preguntas de arquitectura y relaciones cross-file:
- Si el ejecutable
graphifyestá disponible, uságraphify query,graphify pathographify explain. Es una herramienta local del mantenedor: no está en PyPI ni npm y no debe instalarse. - Si el CLI no está pero existe
graphify-out/graph.json, consultá los artefactos commiteados engraphify-out/(wiki/,graph.jsony, como último recurso,GRAPH_REPORT.md). - Si tampoco hay artefactos, usá búsqueda textual y el mapa de docs/AGENT_PLAYBOOK.md. Seguí con la tarea: la ausencia del CLI no es un error.
Que graphify-out/ esté dirty tras hooks o actualizaciones incrementales es
normal. Leé archivos raw cuando vayas a modificar o depurar código concreto, o
cuando el grafo no tenga el detalle necesario.
Peso de graphify-out/: se conserva commiteado (decisión del 2026-09-06,
C3.7). Medido ese día: 17 MB, de los que 16 MB son graph.json; cuatro
ficheros versionados, sobre un repositorio empaquetado de 23 MB.
Se evaluaron las tres opciones y las dos alternativas rompen el paso 2 de la lista de arriba:
- Artefacto de CI. El grafo dejaría de estar en el checkout, que es precisamente donde lo lee un agente sin el CLI — el caso de todas las sesiones remotas. Cambiaría un fallback que funciona por uno que exige descargar un artefacto y autenticarse.
- Git LFS. Añade un requisito de instalación a cualquiera que clone, y un
clonesin LFS deja punteros en vez del grafo: el mismo fallo, en silencio. - Conservarlo. 17 MB en un repositorio de 23 MB es caro en proporción y barato en absoluto, y mantiene el fallback documentado.
Disparador de revisión: si graphify-out/ supera los 50 MB, se vuelve a
decidir. El número no es un límite técnico; es el punto en que el coste del
clon deja de ser despreciable frente a la comodidad que compra.
2. Mapa de áreas
El mapa detallado, entry points y documentación por paquete viven en docs/AGENT_PLAYBOOK.md. Límites que hay que conservar:
db/posee la persistencia y todo SQL.services/contiene reglas y transformaciones de dominio; no es una frontera obligatoria para CRUD simple.api/expone HTTP yweb/solo consume contratos tipados de esa API.scraper/ingiere y clasifica;scheduler/orquesta su ejecución.
3. Invariantes que nunca romper
-
Typing strict en todo el código de producción:
pyproject.tomldeclarastrict = trueglobal. Los únicos overrides sontests.*/scripts.*y dependencias de terceros sin stubs. Si tocás cualquier.pyde producción, sale strict o no sale. No añadas# type: ignoreniAnysin un comentario inline que explique el motivo, y nunca abras un override nuevo de módulo enpyproject.toml. -
Upsert idempotente: cualquier escritura desde scraper debe poder re-ejecutarse sin duplicar (ver
db/upsert.py). -
Migraciones append-only: nunca modificar migraciones alembic ya commiteadas. Siempre nueva revisión.
-
Auto-marking de tests:
tests/conftest.pyinfiereunit,integration,e2e,propertyyloadpor nombre, y además marcaintegrationtodo test cuyo cierre de fixtures abra Postgres (tmp_db/api_db, directo o transitivo víaclient/api_key/auth) —unitsignifica "sin BD" de verdad, y también sin red: un testunitque resuelva o conecte fuera de la máquina falla (guard detests/conftest.py; si necesita una respuesta DNS, parcheasocket.getaddrinfo).loadcasa como palabra del nombre del módulo, ytests/test_markers_automarking.pylista por nombre los módulos que quedan fuera demake check. No introduzcas markers manuales de categoría: renombrá el test (la categoría por BD sale sola del fixture). Las excepciones históricas están congeladas porscripts/check_agent_docs.py;slowsí puede marcarse explícitamente.make checkcorreunit or integration;make test-unites el bucle rápido sin BD. -
DTOs Pydantic v2 son el contrato API↔web (
shared/dto.py). Cambios a campos requieren migración consciente. Una ruta nueva nace tipada:dict[str, Any]genera{ [key: string]: unknown }en el cliente y obliga al frontend a duplicar la forma a mano.scripts/check_openapi_contract.pyes un ratchet con allowlist decreciente — no se le añaden entradas. -
HMAC-signed CSRF + argon2/bcrypt para auth (
shared/auth_core.py). No reemplazar por algo más débil. -
Pre-commit obligatorio: ruff + mypy + bandit + codespell + gitleaks + detect-secrets +
check-agent-docscorren en cada commit (.pre-commit-config.yamles la lista vigente). No bypassear con--no-verify. -
Frontend siempre vía API:
web/no accede adb.*ni a la capa Python de servicios de forma directa; consumeapi/mediante HTTP/OpenAPI y contratos tipados. Código nuevo debe respetar este invariante. -
Un solo plano de orquestación por entorno: los planos GitHub Actions y APScheduler nunca corren activos contra la misma BD (ADR-012). Variable
SCHEDULER_PLANEdeclara el dueño. -
Todo el SQL vive en
db/(ADR-022).db/repositories/*(clases) ydb/*.py(funciones de módulo) son el mismo estrato; ninguno se migra al otro por estilo. SQL nuevo fuera dedb/está prohibido. El ratchet TID251 mantiene congeladas las violaciones legacy enservices/,api/,scheduler/,scraper/yscripts/: su whitelist solo puede encoger. También prohíbe abrirdb.connection.connect/connect_reado sus alias endb.databasefuera de esa whitelist. Excepción acotada:services/sql_fragments.pyexpone fragmentos constantes pero no los ejecuta. El conteo vigente está en docs/STATUS.md.Quién puede llamar a
db/(ADR-024): este invariante gobierna dónde vive el SQL, no qué capa invoca persistencia.services/es biblioteca de dominio, no frontera obligatoria. La regla vigente es CRUD simple →db.*directo (incluido desdeapi/routes/); regla de negocio o transformación de dominio →services/. No refactorices un passthrough (leer por id, listar paginado, log de auditoría, crear/revocar sesión) para meterle una capa de servicio que no transforma nada.
4. Validación y comandos
El Makefile es la fuente de comandos; el catálogo y la matriz de prerrequisitos están en docs/AGENT_PLAYBOOK.md.
- Para cambios Python:
make lint,make typecheckymake test-unit.make checkes el atajo fail-fast que ejecuta esos tres en orden. /checkes un workflow de Claude que ejecuta los mismos controles de forma independiente para reportar todos los resultados; no es un alias de shell demake check.- Para cambios frontend:
make web-lint,make web-typechecky los tests relevantes. Los cambios analíticos también requierenmake check-frontend-invariants. - Para contratos API:
make check-api-contract. Para customizaciones de agentes:make check-agent-docs. - Gates que exigen BD sembrada y por eso no entran en
make check:make fuzz-api(ninguna operación puede devolver 5xx; ratchetKNOWN_5XXque solo encoge) y los E2E de Playwright, que en CI corren contra Postgres + API + build de producción y bloquean el merge.make audit-truth-checkmide la verdad del dato contra una BD real y su versión programada avisa por email.make mutation-samplees informe periódico, no gate.
Los tests usan exclusivamente Postgres y requieren TEST_DATABASE_URL. Si
faltan dependencias, Postgres o el CLI de Graphify, ejecutá solo los controles
disponibles y reportá explícitamente cuáles no se ejecutaron y por qué. Un
control omitido no cuenta como verde y no se sustituye por otro motor.
docs/STATUS.md se regenera con make status; no lo edites a
mano.
5. Workflow estándar
Pre-flight (siempre):
- Seguí el orden Graphify CLI → artefactos commiteados → búsqueda textual de §1.
- Lee docs/AGENT_PLAYBOOK.md si vas a tocar un área que no conocés.
- Revisa docs/IMPROVEMENT_BACKLOG.md si te pidieron "encuentra una mejora".
Durante:
- Respetá los invariantes (sección 3).
- Todo módulo Python de producción debe seguir pasando strict.
Post-flight (siempre tras editar .py):
- Corré
/checko los tres controles Python de §4. Si faltan prerrequisitos, reportá los controles no ejecutados. - Si el CLI está disponible, corré
graphify update .; usá--forcepara módulos nuevos, renames o moves. Si no está, omitilo. - Si el cambio contradice una decisión registrada en
docs/adr/o un invariante de la sección 3, abrí una nueva revisión ADR endocs/adr/antes de mergear. - Si el cambio resuelve (total o parcialmente) un ítem de docs/IMPROVEMENT_BACKLOG.md, movélo a Cerrados (o anotá progreso parcial) en ese mismo momento. No dejarlo para después.
- Si tocaste customizaciones de agentes, corré
make check-agent-docs.
Regla general de las instrucciones: describen el estado real del repo, no la intención. Si encontrás una que el código desmiente, arreglá la instrucción en el mismo cambio (o anotala en el backlog si el arreglo es grande) — una instrucción falsa cuesta más que ninguna.
Política de RFCs
Un RFC formal (docs/rfc/) se requiere solo para:
- Cambios de schema/persistencia irreversibles (ej. migración de motor de BD).
- Cambios breaking al contrato API público (campos eliminados, semantica cambiada).
- Decisiones de seguridad/auth (nuevos mecanismos, rotación de secretos masiva).
- Borrado irreversible de datos de producción.
Para todo lo demás: backlog en docs/IMPROVEMENT_BACKLOG.md + PR directo. Los RFCs de UX/features existentes no generan nuevos RFCs.
6. Cuándo pedir confirmación al humano
Estas acciones requieren OK explícito antes de ejecutar:
-
Tocar
db/alembic/(migraciones de schema). -
Modificar secrets,
.env*,.gitleaks.toml,.secrets.baseline. -
Editar workflows
.github/workflows/(CI/CD, scrape, release). -
Cambiar dependencias en
pyproject.toml,requirements*.in,requirements*.txt. -
Borrar tests existentes o relajar markers strict en
pyproject.toml. -
git reset --hard, force-push, ramas borradas, reescritura de historia. -
git push, excepto cuando el usuario o el harness ya asignaron una rama de trabajo para la tarea (típico en Claude Code web / sesiones remotas): a esa rama se pushea sin volver a preguntar.Sobre
master: el camino por defecto sigue siendo rama + PR, y amasterno se pushea directo por iniciativa propia. Pero si el usuario lo pide nombrando ese destino ("push a master", "mergea esto a master"), esa es la decisión: se ejecuta sin volver a preguntar y sin reproponer el PR — basta con decir qué se va a hacer antes de hacerlo, y avisar de lo que el push arrastra (rebase pendiente, controles no ejecutados, cambios que no son tuyos). Un "subilo" o un "hacé push" a secas no es esa autorización: ahí aplica el camino por defecto. -
Crear/cerrar PRs e issues en GitHub (incluso desde una rama ya autorizada).
-
Ejecutar la capacidad real de un skill con efectos externos (deploy, automatización de navegador contra un sitio real, CLI de infraestructura contra recursos reales — ej.
deploy-to-vercel,agent-browser,upstash-cli) sin que el usuario haya pedido esa acción puntual. Esto aplica sin importar eltrustdel skill:trust: first-partyclasifica la confianza en el origen (el vendor que lo publica), no si la acción en sí es segura de ejecutar sin permiso. El skill puede estar instalado y usarse para consulta/lectura de su documentación libremente; lo que requiere OK explícito es invocar la acción que cambia estado fuera del repo.
Si durante la ejecución de una tarea descubrís que completarla requiere una acción de esta lista, detené la tarea, describí qué acción necesitás y por qué, y esperá confirmación explícita antes de continuar.
Para todo lo demás (editar código de feature, añadir tests, refactor local), procedé sin pedir confirmación a menos que el cambio sea irreversible.
7. Referencias
- docs/AGENT_PLAYBOOK.md: paquetes, workflows, comandos, prerrequisitos y glosario.
- docs/c4-architecture.md, docs/database-schema.md y docs/api-design.md: arquitectura y contratos.
- docs/adr/, docs/runbooks/, docs/sli-slo.md y docs/SECURITY.md: decisiones y operación.
- docs/COSTES.md: coste mensual por proveedor, umbral de alerta y fecha de medición.
- docs/legal/registro-tratamientos.md y docs/legal/anexo-encargo-tratamiento.md: registro del art. 30 RGPD y anexo de encargo para clientes B2B. Ambos tienen casillas que solo puede rellenar el propietario (identidad societaria, DPA de cada encargado); están marcadas como tales y no se inventan.
- Decisiones estructurales vigentes que aún no tienen implementación completa:
ADR-027 (outbox sobre
domain_events), ADR-028 (cola y worker; acota ADR-012), ADR-029 (almacén de objetos; revoca «texto por página, sin blob store»), ADR-030 (user_idcomo identidad interna, OIDC, y quién posee el dato personal frente al corporativo), ADR-031 (tablafollows) y ADR-032 (semántica del importe y maestro de órganos). Leelos antes de tocar eventos, notificaciones, documentos binarios, identidad, seguimiento o importes: fijan la forma que esos cambios deben tener.
AGENTS.md es la fuente común; CLAUDE.md y
.github/copilot-instructions.md solo adaptan plataformas. Los commands viven
en .claude/commands/, sus copias portables en .agents/skills/source-command-*
y los skills instalados en skills-lock.json.
make check-agent-docs valida targets y rutas citadas, slash-commands, paridad
recursiva de skills, copias exactas de commands, equivalencia de hooks y plugins
OpenCode.
Cada skill de skills-lock.json declara trust (first-party: org del vendor
de la herramienta que documenta — anthropics, vercel-labs, upstash,
supabase; community: cualquier otro mantenedor). Ver
scripts/classify_skill_trust.py. Tabla completa (nombre, trust, source,
descripción) en docs/skills-inventory.md,
generada con make skills-inventory (scripts/gen_skills_inventory.py) — no
se edita a mano.
Asimetría de enforcement entre clientes: solo .claude/settings.json
declara un permissions.allow (allow-list de comandos Bash auto-aprobados).
.codex/hooks.json y .opencode/opencode.json no tienen un mecanismo
equivalente en este repo — Codex y OpenCode no auto-aprueban ni restringen
comandos a nivel de repo, así que la sección 6 de este archivo (y no la config
de Claude) es la única barrera real para esos dos clientes. No asumas que
restringir .claude/settings.json alcanza para todos los clientes.
