kcreate-scaffolding-gcp-script 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +370 -2
- package/bin/build.js +70 -0
- package/bin/cli.js +441 -0
- package/package.json +36 -4
- package/skill/SKILL.md +246 -0
- package/skill/references/lessons-learned.md +623 -0
- package/skill/scripts/01_init_config.sh +151 -0
- package/skill/scripts/02_print_config.sh +60 -0
- package/skill/scripts/10_build_components.sh +584 -0
- package/skill/scripts/20_validate_file_parity.sh +107 -0
- package/skill/scripts/21_validate_project_pinning.sh +105 -0
- package/skill/scripts/lib/common.sh +747 -0
- package/skill/scripts/run_tracking.sh +72 -0
- package/skill/templates/scaffolding.config.template +81 -0
- package/vendor/github-proyect-tracking/SKILL.md +171 -0
- package/vendor/github-proyect-tracking/references/api.md +163 -0
- package/vendor/github-proyect-tracking/references/architecture.md +171 -0
- package/vendor/github-proyect-tracking/scripts/tracking.py +196 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/__init__.py +1 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/commands.py +846 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/common.py +163 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/events.py +204 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/github.py +315 -0
- package/vendor/github-proyect-tracking/scripts/tracking_core/state.py +379 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Envoltura delgada sobre github-proyect-tracking para este skill. El agente
|
|
3
|
+
# nunca llama a tracking.py directamente — siempre a través de este script,
|
|
4
|
+
# para no tener que recordar flags ni rutas.
|
|
5
|
+
#
|
|
6
|
+
# Uso:
|
|
7
|
+
# run_tracking.sh init-plan <config_file> <plan.json>
|
|
8
|
+
# run_tracking.sh resume <config_file>
|
|
9
|
+
# run_tracking.sh step-start <config_file> "<nombre del paso>"
|
|
10
|
+
# run_tracking.sh step-done <config_file> "<nombre del paso>"
|
|
11
|
+
# run_tracking.sh step-error <config_file> "<nombre del paso>" "<detalle>"
|
|
12
|
+
# run_tracking.sh status <config_file>
|
|
13
|
+
# run_tracking.sh finalize <config_file>
|
|
14
|
+
#
|
|
15
|
+
# Si github-proyect-tracking no está instalado junto a este skill, todos los
|
|
16
|
+
# subcomandos salen con error claro (código 3) en vez de fallar a medias.
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
# shellcheck source=lib/common.sh
|
|
22
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
23
|
+
|
|
24
|
+
SUBCOMMAND="${1:-}"
|
|
25
|
+
CONFIG_FILE="${2:-}"
|
|
26
|
+
[[ -n "$SUBCOMMAND" && -n "$CONFIG_FILE" ]] || ksc_die \
|
|
27
|
+
"uso: run_tracking.sh <subcomando> <config_file> [args...]"
|
|
28
|
+
[[ -f "$CONFIG_FILE" ]] || ksc_die "no existe el archivo de configuración: $CONFIG_FILE"
|
|
29
|
+
|
|
30
|
+
REPO_ROOT="$(ksc_repo_root)"
|
|
31
|
+
TRACKING_PY="$REPO_ROOT/.claude/skills/github-proyect-tracking/scripts/tracking.py"
|
|
32
|
+
[[ -f "$TRACKING_PY" ]] || { ksc_err "github-proyect-tracking no está instalado en este repo ($TRACKING_PY no existe). Reinstala con install.sh de KTerragruntScaffoldingScripts (sin --skip-tracking)."; exit 3; }
|
|
33
|
+
|
|
34
|
+
# shellcheck disable=SC1090
|
|
35
|
+
source "$CONFIG_FILE"
|
|
36
|
+
[[ -n "${TRACKING_GITHUB_PROJECT_URL:-}" ]] || ksc_die \
|
|
37
|
+
"TRACKING_GITHUB_PROJECT_URL no está definido en $CONFIG_FILE"
|
|
38
|
+
|
|
39
|
+
TP() { python3 "$TRACKING_PY" "$@" --url "$TRACKING_GITHUB_PROJECT_URL"; }
|
|
40
|
+
|
|
41
|
+
case "$SUBCOMMAND" in
|
|
42
|
+
init-plan)
|
|
43
|
+
PLAN_FILE="${3:-}"
|
|
44
|
+
[[ -n "$PLAN_FILE" && -f "$PLAN_FILE" ]] || ksc_die "init-plan requiere un archivo plan.json existente"
|
|
45
|
+
TP init --plan-file "$PLAN_FILE"
|
|
46
|
+
;;
|
|
47
|
+
resume)
|
|
48
|
+
TP resume
|
|
49
|
+
;;
|
|
50
|
+
step-start)
|
|
51
|
+
NAME="${3:-}"; [[ -n "$NAME" ]] || ksc_die "step-start requiere el nombre del paso"
|
|
52
|
+
TP step-start --name "$NAME"
|
|
53
|
+
;;
|
|
54
|
+
step-done)
|
|
55
|
+
NAME="${3:-}"; [[ -n "$NAME" ]] || ksc_die "step-done requiere el nombre del paso"
|
|
56
|
+
TP step-done --name "$NAME"
|
|
57
|
+
;;
|
|
58
|
+
step-error)
|
|
59
|
+
NAME="${3:-}"; DETAILS="${4:-sin detalle}"
|
|
60
|
+
[[ -n "$NAME" ]] || ksc_die "step-error requiere el nombre del paso"
|
|
61
|
+
TP step-error --name "$NAME" --details "$DETAILS"
|
|
62
|
+
;;
|
|
63
|
+
status)
|
|
64
|
+
TP status --remote
|
|
65
|
+
;;
|
|
66
|
+
finalize)
|
|
67
|
+
TP finalize
|
|
68
|
+
;;
|
|
69
|
+
*)
|
|
70
|
+
ksc_die "subcomando desconocido: $SUBCOMMAND"
|
|
71
|
+
;;
|
|
72
|
+
esac
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Archivo de configuración de kcreate-scaffolding-gcp-script
|
|
2
|
+
# Generado automáticamente por 01_init_config.sh — no es necesario crearlo a
|
|
3
|
+
# mano. Los campos marcados __PENDIENTE__ no se pudieron inferir del
|
|
4
|
+
# entorno fuente y deben llenarse antes de continuar. Los demás ya vienen
|
|
5
|
+
# autodetectados del entorno fuente (SOURCE_ENV) — revísalos, pero no es
|
|
6
|
+
# obligatorio tocarlos salvo que alguno esté mal.
|
|
7
|
+
#
|
|
8
|
+
# Formato: KEY="value", una por línea. Este archivo se `source`-ea
|
|
9
|
+
# directamente en bash — no agregues nada que no sea una asignación simple
|
|
10
|
+
# o un comentario.
|
|
11
|
+
|
|
12
|
+
# --- Identidad de la ejecución (fijos, no editar tras la primera corrida) ---
|
|
13
|
+
SOURCE_ENV="__SOURCE_ENV__"
|
|
14
|
+
NEW_ENV="__NEW_ENV__"
|
|
15
|
+
|
|
16
|
+
# --- Requeridos: no se pueden inferir del entorno fuente ---
|
|
17
|
+
|
|
18
|
+
# Nombre legible para GCP (project_name). Inferido de NEW_ENV con la
|
|
19
|
+
# convención <App-Con-Mayúscula-Inicial>-<QA|Prod> — confírmalo o corrígelo
|
|
20
|
+
# si el nombre de la app tiene una capitalización distinta a la obvia
|
|
21
|
+
# (ej. siglas, nombres compuestos).
|
|
22
|
+
PROJECT_NAME="__PENDIENTE__"
|
|
23
|
+
|
|
24
|
+
# Repo(s) de GitHub (formato owner/repo) que hacen `push` de imágenes
|
|
25
|
+
# Docker al Artifact Registry de este proyecto nuevo. Se asume que ya
|
|
26
|
+
# existen — este skill NO valida contra GitHub. Uno solo en el caso común;
|
|
27
|
+
# si la app tiene varios repos (como boletera-qa, que maneja 5), sepáralos
|
|
28
|
+
# con ";" — se genera un binding de github_workload_identity por cada uno.
|
|
29
|
+
APP_GITHUB_REPO="__PENDIENTE__"
|
|
30
|
+
|
|
31
|
+
# URL del repo de GitHub (https://github.com/{owner}/{repo}) usado por
|
|
32
|
+
# github-proyect-tracking para registrar el avance de esta ejecución como
|
|
33
|
+
# Issues/comentarios. Necesario para poder reanudar si el proceso se
|
|
34
|
+
# interrumpe.
|
|
35
|
+
TRACKING_GITHUB_PROJECT_URL="__PENDIENTE__"
|
|
36
|
+
|
|
37
|
+
# --- Autodetectados de SOURCE_ENV (verificar, normalmente no hace falta tocar) ---
|
|
38
|
+
|
|
39
|
+
ORG_ID="__PENDIENTE__"
|
|
40
|
+
ORG_BILLING_ACCOUNT="__PENDIENTE__"
|
|
41
|
+
REGION="__PENDIENTE__"
|
|
42
|
+
ZONE="__PENDIENTE__"
|
|
43
|
+
LABEL_OWNER="__PENDIENTE__"
|
|
44
|
+
|
|
45
|
+
# Organización de GitHub usada en github_workload_identity (local.github_organization).
|
|
46
|
+
GITHUB_ORGANIZATION="__PENDIENTE__"
|
|
47
|
+
|
|
48
|
+
# Entradas fijas (no "dev-<usuario>") de sql_postgres.authorized_networks en
|
|
49
|
+
# SOURCE_ENV, separadas por ";" como "nombre=ip/32;nombre2=ip2/32". Se
|
|
50
|
+
# copian tal cual al entorno nuevo — son IPs de oficina/admin compartidas
|
|
51
|
+
# entre entornos, no específicas de SOURCE_ENV.
|
|
52
|
+
FIXED_ADMIN_NETWORKS="__PENDIENTE__"
|
|
53
|
+
|
|
54
|
+
# --- Opcionales, con default vacío (no bloquean CONFIG_READY) -----------
|
|
55
|
+
|
|
56
|
+
# Componentes a incluir ADEMÁS de los base (la intersección real de todos
|
|
57
|
+
# los entornos de live/ — ver ksc_base_components). Por defecto NO se
|
|
58
|
+
# incluyen opcionales como cloud_armor/identity_platform/monitoring_alerts
|
|
59
|
+
# /secrets aunque SOURCE_ENV los tenga, siguiendo la guía (§1.2: "no
|
|
60
|
+
# incluir salvo pedido explícito"). Si NEW_ENV sí los necesita, lístalos
|
|
61
|
+
# aquí separados por ";", ej. "secrets;cloud_armor".
|
|
62
|
+
EXTRA_COMPONENTS=""
|
|
63
|
+
|
|
64
|
+
# Si sql_postgres de SOURCE_ENV usa dns_domain_name, da aquí el dominio
|
|
65
|
+
# real que NEW_ENV usa (ej. "xoloshop.mx."). Déjalo vacío para que el
|
|
66
|
+
# skill genere un valor por patrón (<app>.ninja., copiando la convención
|
|
67
|
+
# de SOURCE_ENV) marcado con [WARN] para confirmación humana — el dominio
|
|
68
|
+
# real registrado puede no coincidir con ese patrón visual.
|
|
69
|
+
DNS_DOMAIN=""
|
|
70
|
+
|
|
71
|
+
# --- Copiados de SOURCE_ENV pero marcados para verificación humana/del ---
|
|
72
|
+
# --- agente: no se puede confirmar que sigan siendo la versión más ---
|
|
73
|
+
# --- reciente sin consultar documentación externa en el momento. ---
|
|
74
|
+
|
|
75
|
+
# Versión de GKE (canal Stable). 10_build_components.sh usa este valor tal
|
|
76
|
+
# cual — si quieres la versión estable más reciente real (no la que ya
|
|
77
|
+
# usaba SOURCE_ENV), verifícala y corrígela aquí antes de construir.
|
|
78
|
+
GKE_VERSION_NEEDS_VERIFICATION="__PENDIENTE__"
|
|
79
|
+
|
|
80
|
+
# Versión mayor de Cloud SQL Postgres. Mismo criterio que GKE_VERSION.
|
|
81
|
+
DATABASE_VERSION_NEEDS_VERIFICATION="__PENDIENTE__"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-proyect-tracking
|
|
3
|
+
description: Track process execution state in GitHub Issues with resume.
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
author: Rommel (rommelmg), Hermes Agent
|
|
6
|
+
license: MIT
|
|
7
|
+
platforms: [linux, macos, windows]
|
|
8
|
+
metadata:
|
|
9
|
+
hermes:
|
|
10
|
+
tags: [github, tracking, state-machine, resume, issues]
|
|
11
|
+
category: software-development
|
|
12
|
+
related_skills: [github]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# github-proyect-tracking Skill
|
|
16
|
+
|
|
17
|
+
Persiste el estado de ejecución de un proceso estructurado en un Issue de
|
|
18
|
+
GitHub (uno por ejecución) y lo reanuda tras interrupciones leyendo el
|
|
19
|
+
último comentario. No ejecuta los pasos del proceso: los instrumenta —
|
|
20
|
+
el agente hace el trabajo real y reporta cada transición con este skill.
|
|
21
|
+
Depende solo de Python 3.8+ (stdlib); `gh` es opcional (fuente de token).
|
|
22
|
+
|
|
23
|
+
**v0.4.0**: cada transición nueva lleva metadatos HTML con `transition_id`
|
|
24
|
+
UUID para reconciliar respuestas perdidas y 500/502/503/504 sin confundir
|
|
25
|
+
otro emisor.
|
|
26
|
+
El resultado CANCELADO se conserva en reintentos, `resume` y `adopt`; tras
|
|
27
|
+
ERROR se exige una reanudación explícita. El lock tiene heartbeat automático,
|
|
28
|
+
el estado usa esquema v2 con migración desde v1 y los instaladores restauran
|
|
29
|
+
el respaldo exacto ante fallos.
|
|
30
|
+
|
|
31
|
+
## When to Use
|
|
32
|
+
|
|
33
|
+
- El usuario pide rastrear/persistir la ejecución de un proceso con estados
|
|
34
|
+
verificables en GitHub (Issues como bitácora) y reanudar tras fallos.
|
|
35
|
+
- Un proceso se interrumpió (error, caída de sesión) y hay que continuar
|
|
36
|
+
exactamente donde quedó, usando el Issue como fuente de verdad.
|
|
37
|
+
- Contador: "trackea este proceso en GitHub", "reanuda donde quedó",
|
|
38
|
+
"registrando el avance del proceso".
|
|
39
|
+
|
|
40
|
+
Don't use for:
|
|
41
|
+
- Crear issues/PRs de mantenimiento sin seguimiento de proceso
|
|
42
|
+
(usa el skill `github`).
|
|
43
|
+
- Persistir secretos en el cuerpo de un Issue (los `Detalles` se
|
|
44
|
+
sanitizan por patrones, pero no guarantees absolutas: no metas
|
|
45
|
+
credenciales).
|
|
46
|
+
|
|
47
|
+
## Prerequisites
|
|
48
|
+
|
|
49
|
+
- `github_project_url` exacta: `https://github.com/{owner}/{repo}`
|
|
50
|
+
(obligatoria en `init`; owner/repo se normalizan a minúsculas).
|
|
51
|
+
- Token de GitHub con permisos de escritura sobre Issues (classic PAT
|
|
52
|
+
scope `repo`; fine-grained: Issues → Read and write). Resolución
|
|
53
|
+
automática: `--token` → `GITHUB_TRACKING_TOKEN` → `GITHUB_TOKEN` →
|
|
54
|
+
`GH_TOKEN` → `gh auth token` → archivo de estado (solo con
|
|
55
|
+
`--store-token`). Sin token → pídelo al usuario; nunca lo inventes.
|
|
56
|
+
- Python 3.8+ en PATH (`python3`).
|
|
57
|
+
|
|
58
|
+
## How to Run
|
|
59
|
+
|
|
60
|
+
Todo corre por `terminal` con un único JSON de respuesta por comando.
|
|
61
|
+
Ruta del script relativa a este skill: `scripts/tracking.py`:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
SKILL="$HOME/.hermes/profiles/<profile>/skills/software-development/github-proyect-tracking/scripts/tracking.py"
|
|
65
|
+
python3 "$SKILL" doctor --url https://github.com/OWNER/REPO --token "$GITHUB_TOKEN"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Reference
|
|
69
|
+
|
|
70
|
+
| Comando | Efecto |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `init --url URL [--plan JSON] [--store-token]` | Valida URL+token, crea el Issue, guarda `active_process_id` |
|
|
73
|
+
| `resume [--url URL]` | Lee el ÚLTIMO comentario y dicta cómo continuar |
|
|
74
|
+
| `step-start --name N` | Comenta `[STATUS: INICIANDO] - Paso: N` (valida orden del plan) |
|
|
75
|
+
| `step-done --name N` | Comenta `[STATUS: COMPLETADO] - Paso: N` (exige paso en vuelo) |
|
|
76
|
+
| `step-error --name N --details D` | Comenta `[STATUS: ERROR]` sanitizado y detiene |
|
|
77
|
+
| `finalize [--force]` | FINALIZADO, o CANCELADO con `--force`; cierra y limpia memoria |
|
|
78
|
+
| `status [--remote]` | Estado local (+ veredicto remoto) |
|
|
79
|
+
| `adopt --url URL --execution-id ID [--force]` | Recupera memoria desde un Issue remoto |
|
|
80
|
+
| `clear [--hard]` | Limpia `active_process_id` (o borra el archivo) |
|
|
81
|
+
| `doctor [--url URL]` | Diagnóstico de entorno y permisos (exit 0) |
|
|
82
|
+
|
|
83
|
+
Exit codes: `0` ok · `2` validación/transición inválida · `3` token sin
|
|
84
|
+
permiso · `4` sin token · `5` sin proceso activo · `6` Issue no
|
|
85
|
+
encontrado/ambiguo · `7` resume requerido · `8` conflicto con el estado
|
|
86
|
+
remoto (Issue cerrado externamente o finalize bloqueado). Excepciones
|
|
87
|
+
documentadas al contrato JSON: `--help`/`--version` (salida humana,
|
|
88
|
+
exit 0) y `KeyboardInterrupt` (130).
|
|
89
|
+
|
|
90
|
+
## Procedure
|
|
91
|
+
|
|
92
|
+
1. **Preparar.** `doctor --url URL`. Si `token` o `issues-write` falla,
|
|
93
|
+
pide el PAT al usuario. Criterio: JSON con `"ok": true` en checks
|
|
94
|
+
requeridos (gh es opcional y no rompe `ok`). Un rol demostrado de solo
|
|
95
|
+
lectura hace fallar `issues-write`; `UNVERIFIED` se reserva para permisos
|
|
96
|
+
granulares que la API no puede confirmar.
|
|
97
|
+
2. **Init.** `init --url URL --plan '["Paso 1: ...", ...]'` (plan
|
|
98
|
+
recomendado: nombres ÚNICOS; hace la reanudación determinista). Si
|
|
99
|
+
devuelve `RESUME_REQUIRED` (exit 7), NO crees otro Issue: ve al paso 3.
|
|
100
|
+
Criterio: `execution_id` e `issue_url` en la salida.
|
|
101
|
+
3. **Reanudar (si aplica).** `resume` y sigue el `verdict.action`
|
|
102
|
+
(`RERUN_STEP`, `NEXT_STEP`, `START_FIRST`, `ALL_STEPS_DONE`/
|
|
103
|
+
`FINISH_CLEANUP` → finalize, `ABORTED_EXTERNALLY` → pregunta al
|
|
104
|
+
usuario, `NEXT_STEP_AGENT_DEFINED`). `NONE` y `FINISH_CLEANUP` llevan
|
|
105
|
+
`outcome=FINALIZADO|CANCELADO`; conserva ese resultado.
|
|
106
|
+
4. **Ejecutar pasos.** Por cada paso, en orden estricto: `step-start`
|
|
107
|
+
ANTES de trabajar; si el trabajo falla, `step-error` (el proceso se
|
|
108
|
+
detiene; reanudar exige `resume` y solo permite reintentar ese paso);
|
|
109
|
+
si triunfa, `step-done`. Con plan, el CLI rechaza pasos fuera de orden,
|
|
110
|
+
desconocidos, duplicados o sin `step-start` previo (exit 2). Criterio:
|
|
111
|
+
`comment_url` por transición.
|
|
112
|
+
5. **Finalizar.** Con todos los pasos COMPLETADO: `finalize` — rechaza
|
|
113
|
+
cerrar procesos incompletos (exit 8 `FINALIZE_BLOCKED`) salvo
|
|
114
|
+
`--force` (que registra CANCELADO, no éxito), y nunca un Issue cerrado
|
|
115
|
+
externamente. Sin plan exige al menos un COMPLETADO para éxito; sin
|
|
116
|
+
trabajo, `--force` cancela. Criterio: `issue_state: "closed"`,
|
|
117
|
+
`active_process_id: null` y `outcome` coherente.
|
|
118
|
+
6. **Fallos transitorios de red.** GET/PATCH reintentan solos. Si un POST
|
|
119
|
+
pierde la respuesta o recibe 500/502/503/504, se reconcilia por el UUID
|
|
120
|
+
del intento antes de reintentar UNA vez; otro emisor con el mismo texto
|
|
121
|
+
no lo confirma. `init` también busca el Issue por título con paginación.
|
|
122
|
+
|
|
123
|
+
Memoria interna: `~/.github-proyect-tracking/{owner}__{repo}.json`
|
|
124
|
+
(owner/repo en minúsculas; esquema v2, migración automática desde v1 al
|
|
125
|
+
guardar, escritura atómica con tmp único + fsync y permisos 0600). El candado
|
|
126
|
+
por repositorio mantiene heartbeat durante toda la operación y comprueba su
|
|
127
|
+
propiedad antes de efectos remotos y escrituras. `active_process_id` (número
|
|
128
|
+
de Issue; null si no hay proceso) y `github_token` (SOLO con `--store-token`;
|
|
129
|
+
enmascarado en salidas). Overridable con `GITHUB_TRACKING_STATE`.
|
|
130
|
+
|
|
131
|
+
## Pitfalls
|
|
132
|
+
|
|
133
|
+
- **Título exacto del Issue:** `[Process Tracking] Execution ID: {ID}` —
|
|
134
|
+
no lo edites; `adopt` exige igualdad canónica (IDs parciales se
|
|
135
|
+
rechazan).
|
|
136
|
+
- **Contrato de comentarios:** los formatos visibles `[STATUS: ...]` son
|
|
137
|
+
exactos. La línea HTML de metadatos no cambia ese contrato. Un comentario
|
|
138
|
+
manual que imite el formato se interpretará como evento (el modelo de
|
|
139
|
+
confianza es "cualquiera con acceso al repo puede comentar").
|
|
140
|
+
- **Sanitización de secretos:** `Detalles` y nombres de paso se redactan
|
|
141
|
+
por patrones (tokens GitHub/AWS, Authorization/Bearer, passwords,
|
|
142
|
+
URLs con credenciales) ANTES de publicar — no es una garantía
|
|
143
|
+
absoluta: revisa lo que envías.
|
|
144
|
+
- **`init` con proceso inconcluso** falla con exit 7 a propósito:
|
|
145
|
+
reanuda con `resume` o descarta con `clear`.
|
|
146
|
+
- **`step-error` detiene el proceso** (estado `ERROR`). Continuar exige
|
|
147
|
+
`resume` → `RERUN_STEP` (no se puede hacer `step-done` directo).
|
|
148
|
+
- **`finalize` exige plan completo** (o `--force`); sin plan requiere un
|
|
149
|
+
COMPLETADO para éxito. Un Issue cerrado por un humano nunca se convierte
|
|
150
|
+
en éxito (`ABORTED_EXTERNALLY`, exit 8).
|
|
151
|
+
- **Planes legados duplicados** producen `PLAN_AMBIGUOUS`; asigna nombres
|
|
152
|
+
únicos en el cuerpo del Issue, sin alterar los eventos.
|
|
153
|
+
- **Contexto ambiguo:** varios procesos activos exigen `--url`; `clear`
|
|
154
|
+
también lo exige con varios estados inactivos.
|
|
155
|
+
- **Token en el archivo de estado** solo con `--store-token` (opt-in);
|
|
156
|
+
prefiere variables de entorno o `gh auth token`. `adopt` sí puede usarlo.
|
|
157
|
+
- **Límite de comentarios:** un Issue con >10 000 comentarios produce
|
|
158
|
+
`COMMENTS_TOO_MANY` (error explícito, no veredicto truncado).
|
|
159
|
+
|
|
160
|
+
## Verification
|
|
161
|
+
|
|
162
|
+
- `doctor` → `"ok": true` (gh opcional no rompe el global).
|
|
163
|
+
- Tras `init`: `status` muestra `active_process_id` = número del Issue.
|
|
164
|
+
- Tras `finalize`: `status` → `process_state: "FINALIZED"` o `"CANCELADO"`
|
|
165
|
+
según `outcome`, `active_process_id: null`; `resume` responde `NONE` con el
|
|
166
|
+
mismo `outcome`.
|
|
167
|
+
- Reanudación determinista: tras `step-error`, `resume` →
|
|
168
|
+
`verdict.action == "RERUN_STEP"` con el mismo paso; `step-done` sin
|
|
169
|
+
`step-start` previo falla con `NO_STEP_IN_FLIGHT`.
|
|
170
|
+
- 141 pruebas descubiertas: `python3 tests/test_tracking.py` (140 offline;
|
|
171
|
+
la integración real opt-in se omite por defecto).
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Referencia de API (CLI) — github-proyect-tracking
|
|
2
|
+
|
|
3
|
+
`scripts/tracking.py` — stdlib only, Python 3.8+. Cada comando imprime
|
|
4
|
+
EXACTAMENTE un objeto JSON a stdout y usa un exit code documentado. Los
|
|
5
|
+
ejemplos suponen `PY="python3 <ruta-del-skill>/scripts/tracking.py"`.
|
|
6
|
+
|
|
7
|
+
## Contrato de respuesta
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{ "ok": true, "action": "INIT", "execution_id": "exec-...", "...": "..." }
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Error:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{ "ok": false, "error": { "code": "RESUME_REQUIRED", "message": "...",
|
|
17
|
+
"state": {...}, "next": ["resume"] } }
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| Exit | code típico | Significado / acción del agente |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| 0 | — | Éxito |
|
|
23
|
+
| 1 | `NETWORK_ERROR`, `RATE_LIMITED`, `STATE_CORRUPT`, `COMMENTS_TOO_MANY`, `STATE_LOCKED`, `STATE_LOCK_LOST`… | Reintentar / diagnosticar / `resume` |
|
|
24
|
+
| 2 | `VALIDATION_ERROR`, `NO_STEP_IN_FLIGHT`, `STEP_OUT_OF_ORDER`, `STEP_NOT_IN_PLAN`, `STEP_ALREADY_DONE`, `STEP_IN_FLIGHT`, `CONTEXT_AMBIGUOUS` | Corregir argumentos o transición |
|
|
25
|
+
| 3 | `INVALID_TOKEN`, `INSUFFICIENT_SCOPE`, `NO_REPO_ACCESS`, `REPO_NOT_FOUND`, `ISSUES_WRITE_FAILED` | Token sin `issues:write` |
|
|
26
|
+
| 4 | `NO_TOKEN` | Pedir el token al usuario |
|
|
27
|
+
| 5 | `NO_STATE`, `NO_ACTIVE_PROCESS` | `init` primero |
|
|
28
|
+
| 6 | `ISSUE_NOT_FOUND`, `ISSUE_AMBIGUOUS` | Verificar número / `adopt` (ID EXACTO) |
|
|
29
|
+
| 7 | `RESUME_REQUIRED` (proceso inconcluso, estado `ERROR`, `adopt` sobre activo) | `resume` (o `adopt --force`) |
|
|
30
|
+
| 8 | `FINALIZE_BLOCKED`, `ABORTED_EXTERNALLY`, `ISSUE_CLOSED_EXTERNALLY`, `ALREADY_FINALIZED` | Conflicto con estado remoto |
|
|
31
|
+
|
|
32
|
+
`finalize --force` publica `[STATUS: CANCELADO] - El proceso fue cerrado
|
|
33
|
+
anticipadamente (--force).` con los pasos pendientes — distinguible del
|
|
34
|
+
éxito (`FINALIZADO`). `NONE` y `FINISH_CLEANUP` incluyen `outcome:
|
|
35
|
+
FINALIZADO|CANCELADO`; reintentar, reanudar o adoptar nunca reclasifica una
|
|
36
|
+
cancelación como éxito.
|
|
37
|
+
|
|
38
|
+
Los comentarios nuevos conservan exactamente esos formatos visibles y añaden
|
|
39
|
+
una línea HTML `<!-- github-proyect-tracking: {...} -->` con `execution_id`,
|
|
40
|
+
`sequence` y `transition_id` UUID. El parser ignora esos metadatos al leer el
|
|
41
|
+
texto y los usa para reconciliar únicamente el intento que originó el POST.
|
|
42
|
+
|
|
43
|
+
## Comandos
|
|
44
|
+
|
|
45
|
+
### `init --url URL [--plan JSON | --plan-file F] [--token T] [--prompt-token] [--store-token]`
|
|
46
|
+
|
|
47
|
+
- Valida la URL (regex, redactada en errores), resuelve el token, valida
|
|
48
|
+
`issues:write` ANTES de crear nada.
|
|
49
|
+
- Crea el Issue `[Process Tracking] Execution ID: {exec_id}` con el cuerpo
|
|
50
|
+
estándar (metadatos + plan numerado SANITIZADO) y guarda
|
|
51
|
+
`active_process_id`.
|
|
52
|
+
- `--plan` recomendado: lista JSON de cadenas (nombres únicos tras
|
|
53
|
+
normalizar: strip+sanitización; duplicados y colisiones se rechazan) →
|
|
54
|
+
`resume` determinista.
|
|
55
|
+
- `--store-token`: persistencia del token OPT-IN (por defecto NO se
|
|
56
|
+
guarda; `--no-store-token` quedó obsoleto).
|
|
57
|
+
|
|
58
|
+
Salida: `execution_id`, `issue_number`, `issue_url`, `state_file`, `plan`,
|
|
59
|
+
`first_step`, `token_source`.
|
|
60
|
+
|
|
61
|
+
### `resume [--url URL] [--token T]`
|
|
62
|
+
|
|
63
|
+
- Trae Issue + TODOS los comentarios (paginación por `Link`), parsea
|
|
64
|
+
eventos, emite `verdict` (ver tabla en `references/architecture.md`).
|
|
65
|
+
- `--url` opcional si hay exactamente UN proceso activo; con varios
|
|
66
|
+
activos → `CONTEXT_AMBIGUOUS` (el error lista los repos activos).
|
|
67
|
+
- Registra `last_resume`: después de ERROR autoriza únicamente el reintento
|
|
68
|
+
del paso fallido. Un plan legado duplicado produce `PLAN_AMBIGUOUS` con
|
|
69
|
+
instrucciones para asignar nombres únicos en el cuerpo del Issue.
|
|
70
|
+
|
|
71
|
+
Salida: `issue_state`, `comment_count`, `verdict{action, outcome?,
|
|
72
|
+
resume_from_step, remaining_steps?, failed_step?, error_details?, reason}`.
|
|
73
|
+
|
|
74
|
+
### `step-start --name N` / `step-done --name N`
|
|
75
|
+
|
|
76
|
+
Publican exactamente:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
[STATUS: INICIANDO] - Paso: {N}
|
|
80
|
+
[STATUS: COMPLETADO] - Paso: {N}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `step-error --name N (--details D | --details-file F)`
|
|
84
|
+
|
|
85
|
+
Publica `[STATUS: ERROR] - Paso: {N} - Detalles: {D}` y fija
|
|
86
|
+
`process_state: "ERROR"` — **el proceso se detiene aquí** (por diseño;
|
|
87
|
+
reanudar exige `resume`). `--details-file` ideal para stacktraces largos
|
|
88
|
+
(trunca a 60000 chars).
|
|
89
|
+
|
|
90
|
+
### `finalize [--url URL] [--token T] [--force]`
|
|
91
|
+
|
|
92
|
+
1. Verifica el veredicto remoto: rechaza procesos con pasos pendientes
|
|
93
|
+
(`FINALIZE_BLOCKED`, exit 8) y abortes externos. Sin plan permite éxito
|
|
94
|
+
después de un COMPLETADO; sin trabajo exige `--force`.
|
|
95
|
+
2. Sin `--force`: comenta `[STATUS: FINALIZADO] - El proceso concluyó
|
|
96
|
+
exitosamente.` (idempotente). Con `--force`: `[STATUS: CANCELADO] -
|
|
97
|
+
El proceso fue cerrado anticipadamente (--force).` con los pasos
|
|
98
|
+
pendientes (cierre anticipado distinguible, nunca éxito falso).
|
|
99
|
+
3. Cierra el Issue (PATCH state=closed) y **verifica re-leyendo**.
|
|
100
|
+
4. Limpia `active_process_id` (estado `FINALIZED`/`CANCELADO`) + historial
|
|
101
|
+
(con `outcome`). Un CANCELADO existente conserva ese resultado aunque el
|
|
102
|
+
reintento no lleve `--force`.
|
|
103
|
+
|
|
104
|
+
### `status [--url URL] [--remote]`
|
|
105
|
+
|
|
106
|
+
Estado local enmascarado (nunca imprime el token) y, con `--remote`,
|
|
107
|
+
veredicto fresco del Issue.
|
|
108
|
+
|
|
109
|
+
### `adopt --url URL --execution-id ID [--token T] [--force]`
|
|
110
|
+
|
|
111
|
+
Recuperación: busca el Issue por título canónico EXACTO (el ID completo;
|
|
112
|
+
parciales rechazados, ambigüedad → `ISSUE_AMBIGUOUS`), reconstruye el
|
|
113
|
+
archivo de estado local. Carga el estado candidato antes de resolver el
|
|
114
|
+
token, por lo que puede usar el guardado con `--store-token`. No sustituye
|
|
115
|
+
un proceso activo sin `--force` (que conserva token e historial). Si adopta
|
|
116
|
+
un ERROR, guarda `process_state=ERROR` y exige `resume`; conserva CANCELADO
|
|
117
|
+
y ABORTED según el Issue. Útil tras perder la máquina local o tras un
|
|
118
|
+
`clear --hard` accidental.
|
|
119
|
+
|
|
120
|
+
### `clear [--url URL] [--hard]`
|
|
121
|
+
|
|
122
|
+
Soft: `active_process_id := null`, `process_state := "CLEARED"` (conserva
|
|
123
|
+
token/historial). Hard: elimina el archivo. Si el proceso era inconcluso,
|
|
124
|
+
avisa: el Issue queda abierto en GitHub (recuperable con `adopt`). Sin URL,
|
|
125
|
+
varios estados locales —activos o inactivos— producen `CONTEXT_AMBIGUOUS`;
|
|
126
|
+
nunca se elige un archivo destructivamente por fecha.
|
|
127
|
+
|
|
128
|
+
### `doctor [--url URL] [--token T]`
|
|
129
|
+
|
|
130
|
+
Checks: python ≥ 3.8, directorio de estado escribible (crea el directorio
|
|
131
|
+
y un archivo de prueba — efecto de escritura declarado), token resuelto
|
|
132
|
+
(incluye el guardado con `--store-token` del repo de `--url`), GitHub API
|
|
133
|
+
+ rate limit, `gh` CLI (opcional), archivos de estado, URL válida y
|
|
134
|
+
`issues:write` (con `--url`). Siempre exit 0 — es un reporte.
|
|
135
|
+
|
|
136
|
+
## Variables de entorno
|
|
137
|
+
|
|
138
|
+
| Variable | Uso |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `GITHUB_TRACKING_TOKEN` | Token (prioridad máxima de las fuentes automáticas) |
|
|
141
|
+
| `GITHUB_TOKEN` / `GH_TOKEN` | Fallbacks estándar |
|
|
142
|
+
| `GITHUB_TRACKING_STATE` | Directorio alternativo de archivos de estado |
|
|
143
|
+
|
|
144
|
+
## Detalles de la API REST usada
|
|
145
|
+
|
|
146
|
+
| Operación | Endpoint |
|
|
147
|
+
|---|---|
|
|
148
|
+
| Validación de repo/permisos | `GET /repos/{owner}/{repo}` |
|
|
149
|
+
| Crear Issue | `POST /repos/{owner}/{repo}/issues` |
|
|
150
|
+
| Leer Issue / cerrar Issue | `GET` / `PATCH /repos/{owner}/{repo}/issues/{n}` |
|
|
151
|
+
| Comentarios (paginado 100/pág) | `GET /repos/{owner}/{repo}/issues/{n}/comments` |
|
|
152
|
+
| Publicar comentario | `POST /repos/{owner}/{repo}/issues/{n}/comments` |
|
|
153
|
+
| Buscar Issue (adopt) | `GET /search/issues?q=...` |
|
|
154
|
+
| Rate limit (doctor) | `GET /rate_limit` |
|
|
155
|
+
|
|
156
|
+
Cabeceras: `Authorization: Bearer`, `Accept: application/vnd.github+json`,
|
|
157
|
+
`X-GitHub-Api-Version: 2022-11-28`, `User-Agent: github-proyect-tracking/{v}`.
|
|
158
|
+
Reintentos: GET/PATCH/5xx con backoff. Ante respuesta perdida o
|
|
159
|
+
500/502/503/504 en POST, se busca el `transition_id` UUID fuera de la línea
|
|
160
|
+
base y solo entonces se reconcilia; si no aparece, hay un único reintento con
|
|
161
|
+
el mismo UUID y una segunda reconciliación. La creación del Issue usa búsqueda
|
|
162
|
+
paginada por título exacto. Rate limit 403/429 con `X-RateLimit-Remaining: 0`
|
|
163
|
+
espera controlada (máx 90 s).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Arquitectura — github-proyect-tracking
|
|
2
|
+
|
|
3
|
+
## Visión general
|
|
4
|
+
|
|
5
|
+
El skill instrumenta procesos (no los ejecuta): convierte cada transición en
|
|
6
|
+
un evento persistido en un Issue de GitHub y mantiene una memoria local para
|
|
7
|
+
saber si hay un proceso inconcluso y en qué Issue vive.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌──────────────┐ instrucción ┌─────────────────────────────────────┐
|
|
11
|
+
│ Agente (tú) │ ───────────────▶ │ scripts/tracking.py (CLI, stdlib) │
|
|
12
|
+
│ │ ◀─────────────── │ · 1 JSON por comando + exit code │
|
|
13
|
+
└──────────────┘ JSON + exit └───────────────┬─────────────────────┘
|
|
14
|
+
│ trabajo real del proceso │
|
|
15
|
+
▼ ▼
|
|
16
|
+
┌──────────────────┐ comentarios [STATUS:…] ┌──────────────────────────┐
|
|
17
|
+
│ Memoria interna │ ◀──────────────────────── │ GitHub REST API v3 │
|
|
18
|
+
│ ~/.github- │ Issue #N (fuente de │ /repos/{o}/{r}/issues/… │
|
|
19
|
+
│ proyect-tracking/│ verdad al reanudar) │ Bearer token │
|
|
20
|
+
│ {owner}__{repo}. │ └──────────────────────────┘
|
|
21
|
+
│ json │
|
|
22
|
+
└──────────────────┘
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Doble libro:
|
|
26
|
+
|
|
27
|
+
1. **Remoto (autoritativo):** un Issue con título
|
|
28
|
+
`[Process Tracking] Execution ID: {id}`; cada transición es un comentario
|
|
29
|
+
con contrato visible exacto y el último evento decide la reanudación.
|
|
30
|
+
2. **Local (rápido):** `~/.github-proyect-tracking/{owner}__{repo}.json` con
|
|
31
|
+
esquema v2, `active_process_id`, `github_token` opcional, plan y metadatos.
|
|
32
|
+
La carga migra v1 a v2 en memoria y el siguiente guardado la persiste.
|
|
33
|
+
Escritura atómica (tmp único + fsync + rename), permisos 0600.
|
|
34
|
+
|
|
35
|
+
## Contrato de eventos y reconciliación
|
|
36
|
+
|
|
37
|
+
Formatos visibles:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
[STATUS: INICIANDO] - Paso: {nombre}
|
|
41
|
+
[STATUS: ERROR] - Paso: {nombre} - Detalles: {detalles}
|
|
42
|
+
[STATUS: COMPLETADO] - Paso: {nombre}
|
|
43
|
+
[STATUS: FINALIZADO] - El proceso concluyó exitosamente.
|
|
44
|
+
[STATUS: CANCELADO] - El proceso fue cerrado anticipadamente (--force).
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Los eventos nuevos añaden una línea HTML invisible:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
<!-- github-proyect-tracking: {"execution_id":"...","sequence":1,"transition_id":"..."} -->
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`transition_id` es un UUID por intento y se conserva en el reintento del mismo
|
|
54
|
+
comando. Ante respuesta de red perdida o HTTP 500/502/503/504, el cliente lee
|
|
55
|
+
los comentarios y solo reconcilia si encuentra ese UUID fuera de la línea base.
|
|
56
|
+
Un texto idéntico antiguo o nuevo de otro emisor no basta. Si no lo encuentra,
|
|
57
|
+
reintenta una vez y vuelve a reconciliar. `init` recupera una creación ambigua
|
|
58
|
+
buscando el título exacto con paginación; si no lo encuentra, el error incluye
|
|
59
|
+
Execution ID, título y comando `adopt` sugerido.
|
|
60
|
+
|
|
61
|
+
El parser elimina los metadatos antes de aplicar `COMMENT_RE`, por lo que los
|
|
62
|
+
formatos visibles y los comentarios antiguos siguen siendo compatibles.
|
|
63
|
+
`Detalles` admite stacktraces multilínea. `events_from_comments()` ordena por
|
|
64
|
+
`(created_at, comment_id)`, sin confiar en el orden de llegada.
|
|
65
|
+
|
|
66
|
+
## Máquina de estados del proceso
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
init (URL válida + token issues:write)
|
|
70
|
+
(sin proceso) ───────────────────────────────────────▶ RUNNING
|
|
71
|
+
│
|
|
72
|
+
┌─────────── step-start / step-done ───────────────┤
|
|
73
|
+
▼ │
|
|
74
|
+
RUNNING ── step-error ──▶ ERROR ──(resume: RERUN_STEP)─┘
|
|
75
|
+
│
|
|
76
|
+
├──── finalize: FINALIZADO + Issue cerrado ────────▶ FINALIZED
|
|
77
|
+
└──── finalize --force: CANCELADO + Issue cerrado ─▶ CANCELADO
|
|
78
|
+
(active_process_id := null)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Estado en memoria | Significado | `active_process_id` |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `RUNNING` | Issue abierto, último evento INICIANDO/COMPLETADO | número |
|
|
84
|
+
| `ERROR` | Último evento ERROR; proceso detenido | número |
|
|
85
|
+
| `FINALIZED` | Issue cerrado con FINALIZADO | `null` |
|
|
86
|
+
| `CANCELADO` | Issue cerrado anticipadamente con CANCELADO | `null` |
|
|
87
|
+
| `ABORTED` | Issue cerrado sin FINALIZADO/CANCELADO | `null` |
|
|
88
|
+
| `CLEARED` | Memoria descartada localmente | `null` |
|
|
89
|
+
|
|
90
|
+
`is_unfinished()` exige ID activo y estado RUNNING o ERROR. `init` sobre un
|
|
91
|
+
proceso inconcluso falla con exit 7 (`RESUME_REQUIRED`). El esquema valida
|
|
92
|
+
tipos y estas invariantes cruzadas; un estado terminal no puede conservar ID
|
|
93
|
+
activo.
|
|
94
|
+
|
|
95
|
+
Después de ERROR, `resume` registra `last_resume` con Issue y comentario de
|
|
96
|
+
error. `step-start` solo acepta esa autorización para reintentar el mismo paso;
|
|
97
|
+
`step-done` y `step-error` también se detienen mientras el último evento remoto
|
|
98
|
+
sea ERROR. `adopt` reconstruye ERROR, nunca lo sustituye por RUNNING.
|
|
99
|
+
|
|
100
|
+
## Ciclo de vida de una ejecución
|
|
101
|
+
|
|
102
|
+
1. **init** — valida URL y permisos, genera `exec-{timestamp}-{12 hex}`, crea
|
|
103
|
+
el Issue y guarda memoria. Un plan duplicado nuevo se rechaza.
|
|
104
|
+
2. **resume** — lee Issue y todos los comentarios, deriva el veredicto y
|
|
105
|
+
sincroniza el estado. Los planes legados duplicados producen
|
|
106
|
+
`PLAN_AMBIGUOUS` con instrucciones de reparación; no se deduplican.
|
|
107
|
+
3. **step-start / step-error / step-done** — una transición por comentario,
|
|
108
|
+
validada contra la misma instantánea remota.
|
|
109
|
+
4. **finalize** — publica el evento terminal, cierra y verifica el Issue,
|
|
110
|
+
limpia memoria y conserva las últimas 20 entradas de historial.
|
|
111
|
+
|
|
112
|
+
Con plan, el éxito exige todos los pasos completados. Sin plan, el último
|
|
113
|
+
evento COMPLETADO permite FINALIZADO; sin trabajo, `finalize` se bloquea y
|
|
114
|
+
`--force` registra CANCELADO. El `outcome` siempre procede del evento remoto:
|
|
115
|
+
un reintento sin `--force` no convierte CANCELADO en FINALIZADO.
|
|
116
|
+
|
|
117
|
+
## Veredictos de `resume`
|
|
118
|
+
|
|
119
|
+
| Último evento / situación | `verdict.action` | `outcome` | Acción |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
| FINALIZADO + Issue abierto | `FINISH_CLEANUP` | `FINALIZADO` | Ejecutar `finalize` |
|
|
122
|
+
| CANCELADO + Issue abierto | `FINISH_CLEANUP` | `CANCELADO` | Ejecutar `finalize` |
|
|
123
|
+
| FINALIZADO + Issue cerrado | `NONE` | `FINALIZADO` | Nada; memoria auto-limpia |
|
|
124
|
+
| CANCELADO + Issue cerrado | `NONE` | `CANCELADO` | Nada; conservar cancelación |
|
|
125
|
+
| ERROR | `RERUN_STEP` | — | Re-ejecutar ese paso tras esta autorización |
|
|
126
|
+
| INICIANDO | `RERUN_STEP` | — | Re-ejecutar el paso en vuelo |
|
|
127
|
+
| COMPLETADO + plan pendiente | `NEXT_STEP` | — | Ejecutar `resume_from_step` |
|
|
128
|
+
| COMPLETADO + plan agotado | `ALL_STEPS_DONE` | — | Ejecutar `finalize` |
|
|
129
|
+
| Sin comentarios | `START_FIRST` | — | Empezar por el paso 1 |
|
|
130
|
+
| Issue cerrado sin evento terminal | `ABORTED_EXTERNALLY` | — | Preguntar al usuario |
|
|
131
|
+
| Sin plan explícito | `NEXT_STEP_AGENT_DEFINED` | — | Elegir paso o finalizar tras COMPLETADO |
|
|
132
|
+
|
|
133
|
+
## Módulos
|
|
134
|
+
|
|
135
|
+
| Archivo | Responsabilidad |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `scripts/tracking.py` | Parser CLI, punto de entrada y exportaciones compatibles |
|
|
138
|
+
| `tracking_core/common.py` | Versión, constantes, errores y validación/redacción |
|
|
139
|
+
| `tracking_core/events.py` | Contrato visible, metadatos, parser y planner |
|
|
140
|
+
| `tracking_core/state.py` | Esquema v2, migración, persistencia, selección y locks |
|
|
141
|
+
| `tracking_core/github.py` | Token, preflight, transporte, paginación y reconciliación |
|
|
142
|
+
| `tracking_core/commands.py` | Comandos y guardas sobre instantáneas remotas |
|
|
143
|
+
|
|
144
|
+
## Locks y robustez
|
|
145
|
+
|
|
146
|
+
`StateLock` usa un directorio por repositorio, propietario con token y un hilo
|
|
147
|
+
daemon que refresca heartbeat durante toda la operación, incluidas las llamadas
|
|
148
|
+
HTTP. `ensure_owner()` se exige antes de efectos remotos y antes de guardar o
|
|
149
|
+
borrar. Perder el lock produce `STATE_LOCK_LOST` y exige `resume`.
|
|
150
|
+
|
|
151
|
+
- Escritura atómica, estado 0600, tokens enmascarados y persistencia opt-in.
|
|
152
|
+
- Reintentos de GET/PATCH y rate limit con espera acotada.
|
|
153
|
+
- Paginación completa por `Link`; más de 10 000 comentarios →
|
|
154
|
+
`COMMENTS_TOO_MANY`.
|
|
155
|
+
- `adopt` usa el token almacenado antes de buscar el Issue.
|
|
156
|
+
- `clear` sin URL exige contexto único también cuando solo hay estados
|
|
157
|
+
inactivos; varios candidatos → `CONTEXT_AMBIGUOUS`.
|
|
158
|
+
- Preflight: un rol demostrado de solo lectura falla; `triage`, `push`,
|
|
159
|
+
`maintain` o `admin` habilitan la gestión de Issues.
|
|
160
|
+
- Redacción de todo userinfo, query y fragment en URLs reflejadas.
|
|
161
|
+
|
|
162
|
+
## Límites y garantías
|
|
163
|
+
|
|
164
|
+
- Un proceso activo por repositorio (la memoria es por `{owner}/{repo}`).
|
|
165
|
+
- Los eventos son append-only; una persona con acceso puede imitar el contrato
|
|
166
|
+
visible. Usa repositorios controlados y revisa la cronología ante anomalías.
|
|
167
|
+
- No hay transacciones distribuidas: el lock protege el estado local y el
|
|
168
|
+
heartbeat evita su expiración durante una operación viva, pero dos agentes
|
|
169
|
+
independientes pueden intervenir manualmente en el mismo Issue.
|
|
170
|
+
- La integración real es opt-in y requiere autorización y un repositorio
|
|
171
|
+
desechable; no se ejecuta en la suite offline por defecto.
|