Sesión 2 del track Dev: práctica guiada, instalamos la base común, dominamos Spec-Driven Development sobre el ticket real ONEDEV-218 y dejamos la revisión de PRs automatizada.
Hoy hacemos el ciclo completo propose → archive sobre un ticket real. Con tests, con review y con PR generado por IA.
--preset onehub)/enrich-us + /opsx:propose)design.md + tasks.md de /opsx:propose)/opsx:apply)/opsx:verify + /adversarial-review)/commit + /opsx:archive)Cada misión es una fase del ciclo. XP por completar, badges por destacar, leaderboard en cada pausa.
15 minutos para salvar el entorno de quien llegue a la sesión sin él.
# 1. Clonar el SDD kit (repo privado — los asistentes tienen acceso) git clone https://github.com/One-Hub-Energy/aimate-sdd-kit.git # 2. Clonar el repo del proyecto y cambiar a la rama de la feature git clone https://github.com/One-Hub-Energy/onedev-grid-service.git cd onedev-grid-service git checkout ONEDEV-63-automated-capacity-data # 3. Instalar el scaffold del SDD kit (también activa /opsx:verify automáticamente) node ../aimate-sdd-kit/packages/sdd-kit/bin/init.js . --preset onehub # 4. Inicializar OpenSpec (genera los comandos /opsx:*, incluido /opsx:verify) openspec init --tools claude .
docs/docs/data-model.mdbackend-developer configuradoexamples/frontend-standards.md no se instalabackend-developerconfig.yml ya apunta a Python 3.12 + structlogLo que aprendimos en la Sesión 1 y por qué el ciclo SDD es el eje de la sesión de hoy.
docs/ — backend-standards, api-spec, data-model, dev guideai-specs/agents/ — backend-developer, product-analystai-specs/skills/ — enrich-us, adversarial-review, commit, code-auditing--preset onehub → stack Python/FastAPI + dominio OHE ya configuradoEl kit no ejecuta código. Es el contexto que Claude necesita para razonar sobre tu proyecto específico.
# Instalar en tu proyecto (desde el repo clonado del kit) node ../aimate-sdd-kit/packages/sdd-kit/bin/init.js . --preset onehub # Repositorio privado — el equipo tiene acceso github.com/One-Hub-Energy/aimate-sdd-kit # Requiere también OpenSpec (el motor SDD) npm install -g @fission-ai/openspec@latest openspec init
/opsx:propose es el comando nuevo de OpenSpec que combina /new + /ff. Genera todos los artifacts en un paso: proposal.md, spec.md, design.md y tasks.md.
# Flujo actual (recomendado) /enrich-us → /opsx:propose → /opsx:apply → /opsx:verify → /adversarial-review → /opsx:archive → /commit
La especificación ES el contrato: el código viene después. Si cambias de idea, actualizas la spec primero.
# Hoy hacemos el ciclo completo sobre ONEDEV-218 (dedup) /enrich-us → /opsx:propose → /opsx:apply → /opsx:verify → /adversarial-review → /opsx:archive → /commit
Convertir ONEDEV-218 en una spec ejecutable que el equipo y la IA puedan seguir.
# 1. Abre examples/onedev-63/enriched-us.md # Úsalo como referencia o pégalo en Claude /enrich-us # Claude hace preguntas sobre: # · Reglas de negocio del dedup # · Edge cases (fuerza, race condition) # · Criterios de aceptación precisos
/opsx:proposeTip: el archivo examples/onedev-63/enriched-us.md ya tiene el ejemplo completo de ONEDEV-218. Úsalo si el tiempo aprieta.
# Con la US enriquecida activa en el contexto /opsx:propose # Claude genera TODOS los artifacts en un paso: # · proposal.md — objetivo, alcance, riesgos # · spec.md — ACs formales + escenarios # · design.md — decisiones técnicas # · tasks.md — lista de tareas ordenadas
/new — inicia el change en OpenSpec/ff — genera los artifacts (fast-forward)/new + /ff = /opsx:proposeLa spec no es documentación. Es el contrato que la IA va a ejecutar en /opsx:apply y que /opsx:verify va a comprobar. Si la spec es ambigua, el código también lo será.
# Estructura esperada en openspec/changes/dedup-218/
proposal.md ← PRD con objetivo, alcance, riesgos
spec.md ← ACs formales + escenarios
design.md ← (siguiente bloque)
tasks.md ← (siguiente bloque)
apply-progress.md ← (bloque 4)
verify-report.md ← (bloque 5)
proposal.md y spec.md existen en openspec/changes/proposal.md generado y revisadospec.md con los 4 ACs de ONEDEV-218feat: add spec for ONEDEV-218 dedupSi no llegas al commit, guarda el trabajo igualmente — en la pausa lo sincronizamos.
Del spec al plan de ejecución: arquitectura técnica y lista de tareas ordenada.
# /opsx:propose ya generó design.md y tasks.md — revisar en openspec/changes/dedup-218/ # design.md: verificar decisiones clave # · Diagrama de flujo: compute_sha256 → is_duplicate → IntegrityError # · Decisión: UNIQUE index como safety net (no solo app-level check) # · Estructura: services/dedup.py + models/raw_file.py # tasks.md: verificar orden y estimaciones # · T1: compute_sha256 + unit test (0.5 SP) # · T2: is_duplicate + integration test (1 SP) # · T3: integración en flujo + flag force (1 SP) # · T4: race condition IntegrityError (0.5 SP)
design.md con la decisión de UNIQUE index documentadatasks.md con las 4 tareas ordenadas y estimadasRED → GREEN → REFACTOR. El test primero, siempre, sin excepción.
# tests/unit/test_dedup.py # Escribir ANTES de abrir services/dedup.py from src.grid_service.services.dedup import compute_sha256 def test_compute_sha256_returns_64_char_hex() -> None: result = compute_sha256(b"hello world") assert len(result) == 64 assert all(c in "0123456789abcdef" for c in result) def test_sha256_is_deterministic() -> None: content = b"grid capacity" assert compute_sha256(content) == compute_sha256(content) # Ejecutar → FALLA (ImportError o AssertionError) pytest tests/unit/test_dedup.py -v # Expected: FAILED ← esto es correcto
Si el test no falla antes de escribir el código, el test no vale nada.
# src/grid_service/services/dedup.py import hashlib from sqlalchemy.orm import Session from ..models.raw_file import RawFile def compute_sha256(content: bytes) -> str: """Return hex SHA-256 digest of content.""" return hashlib.sha256(content).hexdigest() # Ejecutar → VERDE pytest tests/unit/test_dedup.py -v # Expected: PASSED ✅
No agregar nada más. Mínima implementación. El refactor viene después.
# /opsx:apply continúa con las tareas T2–T4 /opsx:apply # Claude implementa: # T2: is_duplicate(db, sha256_hash) + integration test # T3: integración en flujo de ingesta + flag force # T4: try/except IntegrityError + log WARNING # Mantiene todos los tests en verde pytest --cov=src tests/ # Expected: PASSED · coverage report # Verificar estilo black . && ruff check .
src/grid_service/services/dedup.py src/grid_service/models/raw_file.py src/grid_service/api/ingest.py ← modif tests/unit/test_dedup.py tests/integration/test_ingest_api.py tests/conftest.py ← fixtures DB
pytest --cov=src tests/ ====== test session starts ====== tests/unit/test_dedup.py .... PASSED tests/integration/test_ingest.py .. PASSED 6 passed in 1.23s Coverage: src/grid_service/services/dedup.py: 94%
pytest --cov=src tests/ pasa sin erroresblack . y ruff check . sin issuescompute_sha256 e is_duplicate en services/dedup.pyIntegrityErrorforce implementado en POST /ingest/triggerPrimero la IA verifica contra la spec. Luego dos jueces ciegos se intentan refutar mutuamente.
/opsx:verify # Claude compara la implementación contra spec.md + tasks.md # Produce verify-report.md con: # CRITICAL — falla de un AC (bloqueante) # WARNING — no sigue el diseño (revisar) # SUGGESTION — mejora opcional # Si hay un CRITICAL, /opsx:apply de nuevo antes de continuar
/adversarial-review # Dos jueces ciegos, en paralelo, con una misión: # REFUTAR el código. Buscar bugs, edge cases, problemas. # Juez A — correctness + security # Juez B — resilience + performance # El voto: si ≥2 de 3 jueces confirman → el bug es real # Si refutan → el código sobrevive
content es un archivo vacío?Ambos jueces lo encontraron. Es un bug real. /opsx:apply de nuevo para corregirlo antes de continuar.
Solo un juez lo encontró. Revisar en equipo: ¿es un edge case real o hipotético? Decidir si bloquea o va al backlog.
Los jueces no pudieron encontrar el bug. El código sobrevive al boss fight. Continuar.
El adversarial review no es para criticar. Es para que el equipo aprenda a leer código con ojos críticos.
verify-report.md generado sin CRITICALCommit convencional, Pull Request con review automática, cierre del ciclo SDD.
/commit # Claude propone el commit message # feat: add SHA-256 file-level deduplication for raw ingestion files # test: add unit + integration tests for dedup service # fix: handle IntegrityError race condition in concurrent ingest # Luego genera el PR description ## Summary - Implements file-level dedup via SHA-256 hash in raw_files - Handles concurrent ingest race condition via IntegrityError catch - force flag allows re-ingestion when needed ## Test plan - [ ] pytest tests/unit/test_dedup.py - [ ] pytest tests/integration/test_ingest_api.py
on: [push, pull_request] jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pre-commit/action@v3 # black + ruff + file hygiene
services: postgres: image: postgres:15 steps: - name: Test run: pytest --cov=src tests/ # Requiere BD activa para integration
El CI de OHE ya tiene estos dos workflows. El PR se va a revisar automáticamente cuando hagas push.
/opsx:archive # Claude genera archive-report.md con: # · Resumen del cambio implementado # · ACs verificados y su estado final # · Lessons learned del adversarial review # · Referencia al PR mergeado # Mueve los artifacts a openspec/archive/dedup-218/
Cada dev sale con el SDD kit instalado, un ciclo SDD completo sobre ONEDEV-218 (TDD estricto, verify + AI code review y PR generado) y el flujo /opsx dominado. Lo que quede, en deberes para la Sesión 3.