refactor: reorganizar openspec y planificacion con spec recaptcha
- renombrar openspec/ a _openspec/ (carpeta auxiliar) - mover specs de features a changes/ - crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura - migrar _planificacion/ con design-system y roi-theme-template - agregar especificacion recaptcha anti-spam (proposal, tasks, spec) - corregir rutas y referencias en todas las specs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
190
_openspec/changes/cache-first-architecture/spec.md
Normal file
190
_openspec/changes/cache-first-architecture/spec.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# Especificacion: Hook de Pre-Evaluacion de Pagina
|
||||
|
||||
## Purpose
|
||||
|
||||
Define un hook que permite a plugins externos evaluar condiciones ANTES de que WordPress sirva una pagina singular. Este hook es util para plugins de control de acceso, rate limiters, membership, etc.
|
||||
|
||||
**Alcance del tema:** ROI-Theme SOLO provee el hook. La implementacion de logica de acceso es responsabilidad de cada plugin.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Hook roi_theme_before_page_serve
|
||||
|
||||
The system MUST provide a hook that fires before WordPress serves a singular page, allowing external plugins to evaluate conditions and potentially redirect.
|
||||
|
||||
#### Scenario: Plugin externo evalua acceso antes de servir pagina
|
||||
- **GIVEN** un plugin de control de acceso enganchado a `roi_theme_before_page_serve`
|
||||
- **WHEN** un visitante anonimo solicita una pagina singular (post, page, CPT)
|
||||
- **THEN** el tema DEBE disparar `do_action('roi_theme_before_page_serve', $post_id)`
|
||||
- **AND** el hook DEBE ejecutarse en `template_redirect` con priority 0
|
||||
- **AND** si el plugin llama `wp_safe_redirect()` + `exit`, la pagina NO se sirve
|
||||
|
||||
#### Scenario: Ningun plugin enganchado
|
||||
- **GIVEN** ningun plugin esta escuchando `roi_theme_before_page_serve`
|
||||
- **WHEN** un visitante solicita una pagina
|
||||
- **THEN** la pagina se sirve normalmente
|
||||
- **AND** no hay impacto en rendimiento
|
||||
|
||||
#### Scenario: Solo paginas singulares
|
||||
- **GIVEN** el hook `roi_theme_before_page_serve`
|
||||
- **WHEN** la solicitud es para archivo, home, search, feed, o admin
|
||||
- **THEN** el hook NO DEBE dispararse
|
||||
|
||||
#### Scenario: Usuarios logueados excluidos
|
||||
- **GIVEN** un usuario autenticado (logged in)
|
||||
- **WHEN** solicita cualquier pagina
|
||||
- **THEN** el hook NO DEBE dispararse
|
||||
- **BECAUSE** los plugins de cache no cachean paginas para usuarios logueados
|
||||
|
||||
#### Scenario: REST API excluida
|
||||
- **GIVEN** una peticion REST API (REST_REQUEST === true)
|
||||
- **WHEN** se procesa la peticion
|
||||
- **THEN** el hook NO DEBE dispararse
|
||||
- **BECAUSE** las peticiones REST tienen su propio ciclo de vida y no sirven paginas HTML
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Contexto para Plugins
|
||||
|
||||
The hook MUST provide sufficient context for plugins to make decisions.
|
||||
|
||||
#### Scenario: Plugin accede a informacion del post
|
||||
- **GIVEN** un plugin enganchado a `roi_theme_before_page_serve`
|
||||
- **WHEN** el hook se dispara
|
||||
- **THEN** el plugin recibe `$post_id` como parametro
|
||||
- **AND** `get_queried_object()` retorna el WP_Post completo
|
||||
- **AND** funciones como `is_singular()`, `is_single()`, `is_page()` funcionan
|
||||
|
||||
#### Scenario: Plugin accede a informacion del visitante
|
||||
- **GIVEN** un plugin enganchado al hook
|
||||
- **WHEN** el hook se dispara
|
||||
- **THEN** `$_SERVER['REMOTE_ADDR']` esta disponible
|
||||
- **AND** headers HTTP estan disponibles via `$_SERVER`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: No Interferir con Cache
|
||||
|
||||
The theme MUST NOT define cache-blocking constants.
|
||||
|
||||
#### Scenario: Tema no bloquea cache
|
||||
- **GIVEN** el tema roi-theme instalado
|
||||
- **WHEN** plugins de cache (W3TC, WP Super Cache, etc.) estan activos
|
||||
- **THEN** el tema NO DEBE definir `DONOTCACHEPAGE`
|
||||
- **AND** el tema NO DEBE enviar headers `Cache-Control: no-cache`
|
||||
|
||||
#### Scenario: Plugin decide bloquear cache
|
||||
- **GIVEN** un plugin necesita bloquear cache para una pagina
|
||||
- **WHEN** el plugin esta enganchado al hook
|
||||
- **THEN** es responsabilidad del PLUGIN definir `DONOTCACHEPAGE`
|
||||
- **AND** el tema NO participa en esa decision
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### Limitation 1: Page Cache Bypass
|
||||
|
||||
**Severity**: CRITICAL para plugins que requieren evaluacion en cada request
|
||||
|
||||
Cuando Page Cache esta habilitado (W3TC, WP Super Cache, etc.), el hook `roi_theme_before_page_serve` NO SE EJECUTA para paginas cacheadas.
|
||||
|
||||
**Razon tecnica:**
|
||||
```
|
||||
Request → advanced-cache.php → [Cache HIT] → HTML servido
|
||||
↓
|
||||
WordPress NUNCA carga
|
||||
↓
|
||||
Hook NUNCA se dispara
|
||||
```
|
||||
|
||||
**Implicacion para plugins:**
|
||||
- Rate limiters: Los limites no se evaluan en cache hits
|
||||
- Membership plugins: El acceso no se verifica en cache hits
|
||||
- Geolocation: Las restricciones no aplican en cache hits
|
||||
|
||||
**Solucion:** Los plugins que requieren evaluacion en cada request deben implementar su propia estrategia (JavaScript-First, cookies, edge workers, etc.). Esto esta FUERA del alcance de esta spec.
|
||||
|
||||
### Limitation 2: Solo Paginas Singulares
|
||||
|
||||
El hook solo dispara para `is_singular() === true`. Archives, taxonomies, search, y home NO disparan el hook.
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### Ubicacion en Clean Architecture
|
||||
|
||||
El hook DEBE registrarse en `Shared/Infrastructure/Hooks/`.
|
||||
|
||||
### Archivo: CacheFirstHooksRegistrar.php
|
||||
|
||||
```php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Infrastructure\Hooks;
|
||||
|
||||
final class CacheFirstHooksRegistrar
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
add_action('template_redirect', [$this, 'fireBeforePageServe'], 0);
|
||||
}
|
||||
|
||||
public function fireBeforePageServe(): void
|
||||
{
|
||||
if (is_user_logged_in()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_singular()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defined('REST_REQUEST') && REST_REQUEST) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = get_queried_object_id();
|
||||
|
||||
if ($post_id > 0) {
|
||||
do_action('roi_theme_before_page_serve', $post_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Registro en functions.php
|
||||
|
||||
```php
|
||||
$cacheFirstHooks = new \ROITheme\Shared\Infrastructure\Hooks\CacheFirstHooksRegistrar();
|
||||
$cacheFirstHooks->register();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Hook `roi_theme_before_page_serve` se dispara en `template_redirect` priority 0
|
||||
2. Solo dispara para `is_singular() === true`
|
||||
3. NO dispara para usuarios logueados
|
||||
4. Pasa `$post_id` como parametro
|
||||
5. No define DONOTCACHEPAGE ni headers anti-cache
|
||||
6. Plugins pueden enganchar y hacer redirect/exit
|
||||
7. Sin impacto en rendimiento si ningun plugin engancha
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Date | Changes |
|
||||
|---------|------|---------|
|
||||
| 1.0 | 2025-12-07 | Initial spec |
|
||||
| 2.0 | 2025-12-07 | Simplified: Only defines hook, removed plugin implementation details |
|
||||
Reference in New Issue
Block a user