⌘J
En esta página 9

Engineering / Project tracking

Project tracking

Cómo trackeamos el trabajo en Axel, un sistema de 3 niveles sobre GitHub Projects, del roadmap ejecutivo al sprint.

Todo el trabajo de Axel se trackea en GitHub Projects (org heyaxel), en un sistema de 3 niveles. La verdad técnica vive en el repo platform; los Projects la agregan y le dan visibilidad por audiencia.

Los 3 niveles

1 · Portfolio / ejecutivo

Axel · MVP: un Gantt de los ~90 días con un item por hito/sub-proyecto, fechas (Start/Target) y Owner (CTO/Legal/CEO). Es la vista para el CTO y el CEO, sin detalle técnico.

2 · Sub-proyecto / ejecución

Un Project por sub-proyecto (#1 Foundation#5 Notifications), mismo schema (18 campos), con fases propias derivadas de su design doc, no una F0–F7 universal.

3 · Sprint

El campo Sprint (iteración semanal) dentro de cada sub-proyecto.

Los Projects son a nivel de organización, no de repo. Los issues viven en platform; el Project los agrega. Cada Project se linkea al repo para que aparezca en su pestaña Projects.

heyaxel (organización)
Repos
platform — issues + verdad técnica
handbook — esta base
Projects (nivel org)
Axel · MVP — Gantt ejecutivo (nivel 1)
#1 · Foundation — issues por fase (nivel 2)
#2 · Domain
#3 · … — un Project por sub-proyecto

Anatomía de un Project de sub-proyecto

  • Milestone = fase. Los milestones agrupan por fase (F0 · Bootstrap, …). No se crean labels de fase: sería redundante.
  • Campos: Status · Phase · Priority · Estimate · Owner · Sprint.
  • Vistas:
Backlog

Tabla agrupada por Phase: el roadmap maestro, para planificar.

Board

Kanban por Status: el trabajo día a día.

AFK-ready

La frontera grabbable: Status = Todo y 0 blockers abiertos. La cola de los agentes. Ver dependencias, abajo.

Dependencias nativas → la cola AFK

Status = Todo solo no alcanza para la cola de agentes: muestra todo el backlog. Un agente vería 46 issues sin saber cuál puede tomar.

La cola AFK gatea por las relaciones Blocked by nativas de GitHub (REST /issues/{n}/dependencies/blocked_by), no por el texto del issue. Un issue es grabbable cuando tiene Status = Todo y 0 blockers abiertos.

  • Las deps se derivan del desglose por fase: cada gate de fase bloquea por su trabajo; F0-01 (crear el repo) es el único punto de entrada real.
  • Snapshot inicial: los issues bloqueados se ponen en Status = Blocked, así la vista AFK-ready muestra solo la frontera. (Caveat: al cerrarse issues, los que se desbloquean hay que re-pasarlos a Todo, no hay automación nativa para “0 blockers → Todo”.)

Auto-add + repo compartido: como los 5 sub-proyectos comparten el repo platform, un auto-add global metería todos los issues en todos los boards. Los PRs de tooling se ponen en In Review para no ensuciar la cola grabbable.

Convención de labels

Todos los labels custom llevan prefijo de namespace:

type: status: area: meta:
type: / status:

type: qué tipo de trabajo (feature, chore, infra, test, docs), 1 por issue. status: estado del workflow (needs-decision, blocked, in-progress), 1 por issue.

area: / meta:

area: dónde en el codebase (backend, frontend, schema, tenancy…), 1+ por issue. meta: cross-cutting (gate, edge-case), 0+ por issue.

Automatización nativa

Workflows built-in que mantienen el board al día sin trabajo manual:

  • Auto-add: todo issue/PR nuevo de platform entra al board.
  • Item closed → Done · PR merged: el estado se mueve solo.

Regla de idioma: títulos, milestones, labels y campos en inglés; los detalles de los issues (cuerpos) en español. Estructura en inglés, contenido en español.

Cuerpo de un issue bien formado

Current State · Goal · Scope · Technical Spec (archivos, contracts, deps) · Out of Scope · Acceptance Criteria (checkboxes ejecutables) · Edge cases · Dependencies/Blocks · Manual Verification · Definition of Done.

Los Acceptance Criteria + Edge cases son la Definition of Ready: es el gate más importante del workflow AI-native; un issue bien groomeado ahorra más que cualquier review posterior.

Flujo de una fase (commit local → push batch)

Los issues de una fase (ej. F0-01…F0-06) no se pushean uno por uno. Se acumulan como commits locales en una rama de fase, y el primer push de toda la fase ocurre recién al pasar su gate (el issue meta: gate, ej. F0-00).

Por qué diferir el push:

  • Presupuesto de Actions. Una rama sin PR abierto no gasta CI. Abrir un PR por cada issue de la fase dispararía el claude-review (el workflow caro) N veces. Agrupando la fase en un solo momento de push → un run, no N.
  • El gate valida la fase completa. El sentido de un issue gate es verificar que los entregables de la fase encajan juntos (entorno local + CI verde). Eso solo se puede correr cuando todos los issues previos existen, no antes.

Diferir el push no es diferir el commit. Cada issue se commitea al terminarlo (Conventional Commit, Refs #N, no Closes, que solo dispara al mergear). Lo que se difiere es subir la rama al remoto. El trabajo está guardado en git desde el minuto uno; simplemente no viaja a GitHub hasta el gate.

Mantener la rama al día: si main avanza mientras la fase está en curso (otros PRs mergean), rebasar la rama de fase sobre main antes de seguir: así el trabajo se integra contra el estado real y los conflictos se resuelven temprano, de a poco, en vez de todos juntos en el push final.

Estados del Project durante una fase

EstadoCuándo
TodoIssue grabbable (0 blockers), aún no tomado.
In ProgressSe está trabajando o ya está commiteado local, pero sin PR todavía. Es el estado correcto para trabajo hecho-pero-no-entregado.
In ReviewHay un PR abierto esperando revisión. No usar antes del push: sin PR no hay nada que revisar.
DonePR mergeado. Para issues de una fase con push diferido, esto ocurre en bloque al cerrar la fase.

Un issue con commit local pero sin push se queda en In Progress, no In Review. La transición a In Review y luego Done pasa cuando la fase abre su(s) PR(s) en el gate.

El gate de merge (modelo AFK)

El ruleset protect-main de cada repo: require PR, bloquea force-push y borrado de main. Requiere plan Team (en Free la protección de privados da 403).

  • approvals = 1: un agente no puede mergear su propio PR (GitHub no deja auto-aprobar). El humano aprueba; el CTO, como org admin, mergea sus propios PRs con bypass (--admin).
  • El review de Claude es advisory, no bloquea. No lo hicimos required status check: pasa con solo correr (no por hallazgos), y sin synchronize dejaba el check pending en cada push. El muro real es approvals=1; el gate de seguridad de código es CI-tests-verde, que se agrega como required_status_checks cuando aterriza ci.yml.

Ver Automatización & agentes AFK para el detalle.